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 firstpm = tm.first(); pair 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 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 first() const; /// std::pair last() const;