commit 08010c6a5e425b3f2d0d625536e3a571c90a0482
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon Jul 24 23:23:40 2023 +0200

    Implement quick scroll
    
    Replace flag parameter for updateMetrics() by a `force' boolean. When
    it is false, the method keeps the metrics of paragraphs that are still
    visible in WorkArea instead of computing everything afresh. All it has
    to do is update their positions.
    
    Add code to updateMetrics() to update the value of the anchor pit/ypos
    (similar to the one in draw()).
    
    Update processUpdateFlags() to use this when update flag is ForceDraw.
    
    Modify scrollDocView() to just change the anchor paragraph position
    when the scrolling operation would re-use some of the existing
    paragraphs.
    
    The time needed to update the metrics when scrolling with mouse in the
    branch-test.lyx document is now divided by 20!
    
    Part of bug #12297.
---
 src/BufferView.cpp  | 102 ++++++++++++++++++++++++++++++++++------------------
 src/BufferView.h    |  14 ++++++--
 src/TextMetrics.cpp |   6 ++++
 src/TextMetrics.h   |   2 ++
 4 files changed, 86 insertions(+), 38 deletions(-)

diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index dd312739b5..ca3939aabf 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -545,10 +545,13 @@ void BufferView::processUpdateFlags(Update::flags flags)
 
        // First check whether the metrics and inset positions should be updated
        if (flags & Update::Force) {
-               // This will update the CoordCache items and replace Force
-               // with ForceDraw in flags.
-               updateMetrics(flags);
-       }
+               // This will compute all metrics and positions.
+               updateMetrics(true);
+               // metrics is done, full drawing is necessary now
+               flags = (flags & ~Update::Force) | Update::ForceDraw;
+       } else if (flags & Update::ForceDraw)
+               // This will compute only the needed metrics and update 
positions.
+               updateMetrics(false);
 
        // Detect whether we can only repaint a single paragraph (if we
        // are not already redrawing all).
@@ -557,7 +560,7 @@ void BufferView::processUpdateFlags(Update::flags flags)
        if (!(flags & Update::ForceDraw)
                        && (flags & Update::SinglePar)
                        && !singleParUpdate())
-               updateMetrics(flags);
+               updateMetrics(true);
 
        // Then make sure that the screen contains the cursor if needed
        if (flags & Update::FitCursor) {
@@ -566,13 +569,13 @@ void BufferView::processUpdateFlags(Update::flags flags)
                        // (which is just the cursor when there is no selection)
                        scrollToCursor(d->cursor_.selectionBegin(), 
SCROLL_VISIBLE);
                        // Metrics have to be recomputed (maybe again)
-                       updateMetrics();
+                       updateMetrics(true);
                        // Is the cursor visible? (only useful if cursor is at 
end of selection)
                        if (needsFitCursor()) {
                                // then try to make cursor visible instead
                                scrollToCursor(d->cursor_, SCROLL_VISIBLE);
                                // Metrics have to be recomputed (maybe again)
-                               updateMetrics(flags);
+                               updateMetrics(true);
                        }
                }
                flags = flags & ~Update::FitCursor;
@@ -754,10 +757,13 @@ void BufferView::scrollDocView(int const pixels, bool 
update)
        if (pixels == 0)
                return;
 
-       // If the offset is less than 2 screen height, prefer to scroll instead.
-       if (abs(pixels) <= 2 * height_) {
+       // If part of the existing paragraphs will remain visible, prefer to
+       // scroll
+       TextMetrics const & tm = textMetrics(&buffer_.text());
+       if (tm.first().second->top() - pixels <= height_
+            &&  tm.last().second->bottom() - pixels >= 0) {
                d->anchor_ypos_ -= pixels;
-               processUpdateFlags(Update::Force);
+               processUpdateFlags(Update::ForceDraw);
                return;
        }
 
@@ -3110,12 +3116,14 @@ bool BufferView::singleParUpdate()
 
 void BufferView::updateMetrics()
 {
-       updateMetrics(d->update_flags_);
+       updateMetrics(true);
+       // metrics is done, full drawing is necessary now
+       d->update_flags_ = (d->update_flags_ & ~Update::Force) | 
Update::ForceDraw;
        d->update_strategy_ = FullScreenUpdate;
 }
 
 
-void BufferView::updateMetrics(Update::flags & update_flags)
+void BufferView::updateMetrics(bool force)
 {
        if (height_ == 0 || width_ == 0)
                return;
@@ -3123,14 +3131,16 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
        Text & buftext = buffer_.text();
        pit_type const npit = int(buftext.paragraphs().size());
 
-       // Clear out the position cache in case of full screen redraw,
-       d->coord_cache_.clear();
-       d->math_rows_.clear();
+       if (force) {
+               // Clear out the position cache in case of full screen redraw,
+               d->coord_cache_.clear();
+               d->math_rows_.clear();
 
-       // Clear out paragraph metrics to avoid having invalid metrics
-       // in the cache from paragraphs not relayouted below
-       // The complete text metrics will be redone.
-       d->text_metrics_.clear();
+               // Clear out paragraph metrics to avoid having invalid metrics
+               // in the cache from paragraphs not relayouted below. The
+               // complete text metrics will be redone.
+               d->text_metrics_.clear();
+       }
 
        TextMetrics & tm = textMetrics(&buftext);
 
@@ -3142,11 +3152,12 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
                // The anchor pit must have been deleted...
                d->anchor_pit_ = npit - 1;
 
-       // Rebreak anchor paragraph.
-       tm.redoParagraph(d->anchor_pit_);
+       if (!tm.contains(d->anchor_pit_))
+               // Rebreak anchor paragraph.
+               tm.redoParagraph(d->anchor_pit_);
        ParagraphMetrics & anchor_pm = tm.parMetrics(d->anchor_pit_);
 
-       // position anchor
+       // make sure than first paragraph of document is not too low
        if (d->anchor_pit_ == 0) {
                int scrollRange = d->scrollbarParameters_.max - 
d->scrollbarParameters_.min;
 
@@ -3160,16 +3171,16 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
        }
        anchor_pm.setPosition(d->anchor_ypos_);
 
-       LYXERR(Debug::PAINTING, "metrics: "
-               << " anchor pit = " << d->anchor_pit_
-               << " anchor ypos = " << d->anchor_ypos_);
+       LYXERR(Debug::PAINTING, "metrics: " << " anchor pit = " << 
d->anchor_pit_
+                                           << " anchor ypos = " << 
d->anchor_ypos_);
 
        // Redo paragraphs above anchor if necessary.
        int y1 = d->anchor_ypos_ - anchor_pm.ascent();
        // We are now just above the anchor paragraph.
        pit_type pit1 = d->anchor_pit_ - 1;
-       for (; pit1 >= 0 && y1 >= 0; --pit1) {
-               tm.redoParagraph(pit1);
+       for (; pit1 >= 0 && y1 > 0; --pit1) {
+               if (!tm.contains(pit1))
+                       tm.redoParagraph(pit1);
                ParagraphMetrics & pm = tm.parMetrics(pit1);
                y1 -= pm.descent();
                // Save the paragraph position in the cache.
@@ -3181,8 +3192,9 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
        int y2 = d->anchor_ypos_ + anchor_pm.descent();
        // We are now just below the anchor paragraph.
        pit_type pit2 = d->anchor_pit_ + 1;
-       for (; pit2 < npit && y2 <= height_; ++pit2) {
-               tm.redoParagraph(pit2);
+       for (; pit2 < npit && y2 < height_; ++pit2) {
+               if (!tm.contains(pit2))
+                       tm.redoParagraph(pit2);
                ParagraphMetrics & pm = tm.parMetrics(pit2);
                y2 += pm.ascent();
                // Save the paragraph position in the cache.
@@ -3190,6 +3202,27 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
                y2 += pm.descent();
        }
 
+       //FIXME: do we want that?
+       // if updating, remove paragraphs that are outside of screen
+       while(tm.first().second->bottom() <= 0) {
+               //LYXERR0("Forget pit: " << tm.first().first);
+               tm.forget(tm.first().first);
+       }
+       while(tm.last().second->top() > height_) {
+               //LYXERR0("Forget pit: " << tm.first().first);
+               tm.forget(tm.last().first);
+       }
+
+       // Normalize anchor for next time
+       if (d->anchor_pit_ != tm.first().first
+           || d->anchor_ypos_ != tm.first().second->position()) {
+               LYXERR(Debug::PAINTING, __func__ << ": Found new anchor pit = " 
<< tm.first().first
+                               << "  anchor ypos = " << 
tm.first().second->position()
+                               << " (was " << d->anchor_pit_ << ", " << 
d->anchor_ypos_ << ")");
+               d->anchor_pit_ = tm.first().first;
+               d->anchor_ypos_ = tm.first().second->position();
+       }
+
        LYXERR(Debug::PAINTING, "Metrics: "
                << " anchor pit = " << d->anchor_pit_
                << " anchor ypos = " << d->anchor_ypos_
@@ -3198,9 +3231,6 @@ void BufferView::updateMetrics(Update::flags & 
update_flags)
                << " pit1 = " << pit1
                << " pit2 = " << pit2);
 
-       // metrics is done, full drawing is necessary now
-       update_flags = (update_flags & ~Update::Force) | Update::ForceDraw;
-
        // Now update the positions of insets in the cache.
        updatePosCache();
 
@@ -3667,7 +3697,7 @@ void BufferView::draw(frontend::Painter & pain, bool 
paint_caret)
        // FIXME: does it always? see ticket #11947.
        updateScrollbarParameters();
 
-       // Normalize anchor for next time
+       // Normalize anchor for next time (in case updateMetrics did not do it 
yet)
        pair<pit_type, ParagraphMetrics const *> firstpm = tm.first();
        pair<pit_type, ParagraphMetrics const *> lastpm = tm.last();
        for (pit_type pit = firstpm.first; pit <= lastpm.first; ++pit) {
@@ -3675,13 +3705,15 @@ void BufferView::draw(frontend::Painter & pain, bool 
paint_caret)
                if (pm.bottom() > 0) {
                        if (d->anchor_pit_ != pit
                            || d->anchor_ypos_ != pm.position())
-                               LYXERR(Debug::PAINTING, "Found new anchor pit = 
" << d->anchor_pit_
-                                      << "  anchor ypos = " << 
d->anchor_ypos_);
+                               LYXERR(Debug::PAINTING, __func__ << ": Found 
new anchor pit = " << pit
+                                               << "  anchor ypos = " << 
pm.position()
+                                               << " (was " << d->anchor_pit_ 
<< ", " << d->anchor_ypos_ << ")");
                        d->anchor_pit_ = pit;
                        d->anchor_ypos_ = pm.position();
                        break;
                }
        }
+
        if (!pain.isNull()) {
                // reset the update flags, everything has been done
                d->update_flags_ = Update::None;
diff --git a/src/BufferView.h b/src/BufferView.h
index b46ade3df5..cc92e215fb 100644
--- a/src/BufferView.h
+++ b/src/BufferView.h
@@ -309,7 +309,8 @@ public:
        /// selects the item at cursor if its paragraph is empty.
        bool selectIfEmpty(DocIterator & cur);
 
-       /// update the internal \c ViewMetricsInfo.
+       /// Ditch all metrics information and rebuild it. Set the update
+       /// flags and the draw strategy flags accordingly.
        void updateMetrics();
 
        // this is the "nodraw" drawing stage: only set the positions of the
@@ -408,8 +409,15 @@ private:
        /// Update current paragraph metrics.
        /// \return true if no further update is needed.
        bool singleParUpdate();
-       /// do the work for the public updateMetrics()
-       void updateMetrics(Update::flags & update_flags);
+       /** Helper for the public updateMetrics() and for processUpdateFlags()
+        * * When \c force is true, get rid of all paragraph metrics and
+         rebuild them anew.
+        * * When it is false, keep the paragraphs that are still visible in
+        *   WorkArea and rebuild the missing ones.
+        *
+        * This does also set the anchor paragraph and its position correctly
+       */
+       void updateMetrics(bool force);
 
        // Set the row on which the cursor lives.
        void setCurrentRowSlice(CursorSlice const & rowSlice);
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index 837ad5766f..abeb5cfbb8 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -118,6 +118,12 @@ bool TextMetrics::contains(pit_type pit) const
 }
 
 
+void TextMetrics::forget(pit_type pit)
+{
+       par_metrics_.erase(pit);
+}
+
+
 pair<pit_type, ParagraphMetrics const *> TextMetrics::first() const
 {
        ParMetricsCache::const_iterator it = par_metrics_.begin();
diff --git a/src/TextMetrics.h b/src/TextMetrics.h
index 74eef5e0f3..f53701b12e 100644
--- a/src/TextMetrics.h
+++ b/src/TextMetrics.h
@@ -46,6 +46,8 @@ public:
        ///
        bool contains(pit_type pit) const;
        ///
+       void forget(pit_type pit);
+       ///
        std::pair<pit_type, ParagraphMetrics const *> first() const;
        ///
        std::pair<pit_type, ParagraphMetrics const *> last() const;
-- 
lyx-cvs mailing list
lyx-cvs@lists.lyx.org
http://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to