[LyX features/biginset] Implement quick scroll
Jean-Marc Lasgouttes
lasgouttes at lyx.org
Tue Jul 25 14:07:16 UTC 2023
The branch, biginset, has been updated.
- Log -----------------------------------------------------------------
commit 9df4b7e297ff851d839f47d3d369e731366e7d4d
Author: Jean-Marc Lasgouttes <lasgouttes at 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.
diff --git a/src/BufferView.cpp b/src/BufferView.cpp
index 2ed4794..cb96843 100644
--- a/src/BufferView.cpp
+++ b/src/BufferView.cpp
@@ -533,10 +533,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).
@@ -545,7 +548,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) {
@@ -554,13 +557,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;
@@ -742,10 +745,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;
}
@@ -3074,12 +3080,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)
{
PROFILE_THIS_BLOCK(updateMetrics);
if (height_ == 0 || width_ == 0)
@@ -3088,14 +3096,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);
@@ -3107,11 +3117,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 (force || !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;
@@ -3125,16 +3136,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 (force || !tm.contains(pit1))
+ tm.redoParagraph(pit1);
ParagraphMetrics & pm = tm.parMetrics(pit1);
y1 -= pm.descent();
// Save the paragraph position in the cache.
@@ -3146,8 +3157,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 (force || !tm.contains(pit2))
+ tm.redoParagraph(pit2);
ParagraphMetrics & pm = tm.parMetrics(pit2);
y2 += pm.ascent();
// Save the paragraph position in the cache.
@@ -3155,6 +3167,28 @@ void BufferView::updateMetrics(Update::flags & update_flags)
y2 += pm.descent();
}
+ // if updating, remove paragraphs that are outside of screen
+ if (!force) {
+ 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_
@@ -3163,9 +3197,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();
@@ -3632,7 +3663,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) {
@@ -3640,13 +3671,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 b64b431..785901b 100644
--- a/src/BufferView.h
+++ b/src/BufferView.h
@@ -303,7 +303,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
@@ -400,8 +401,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 cc5b8e6..f080aa6 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 9e79e3f..b362cb9 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;
-----------------------------------------------------------------------
Summary of changes:
src/BufferView.cpp | 103 +++++++++++++++++++++++++++++++++-----------------
src/BufferView.h | 14 +++++-
src/TextMetrics.cpp | 6 +++
src/TextMetrics.h | 2 +
4 files changed, 87 insertions(+), 38 deletions(-)
hooks/post-receive
--
Repository for new features
More information about the lyx-cvs
mailing list