]> git.lyx.org Git - lyx.git/blobdiff - src/TextMetrics.cpp
Amend 6c3447c8: FindAdv: sometimes a space is added on some math symbols
[lyx.git] / src / TextMetrics.cpp
index ba05519c677081cd0e818f7a1ac7915e15b847e6..766f0aae24427129a9185a3e0d5a76935a80de05 100644 (file)
@@ -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();
@@ -125,6 +131,17 @@ pair<pit_type, ParagraphMetrics const *> TextMetrics::first() const
 }
 
 
+pair<pit_type, ParagraphMetrics const *> TextMetrics::firstVisible() const
+{
+       // This only works in the main text, I think (bottom > 0)
+       LASSERT(text_->isMainText(), return first());
+       auto it = find_if(par_metrics_.begin(), par_metrics_.end(),
+                         [] (ParMetricsCache::value_type const & p) {
+                             return p.second.hasPosition() && p.second.bottom() > 0;
+                         });
+       return make_pair(it->first, &it->second);
+}
+
 pair<pit_type, ParagraphMetrics const *> TextMetrics::last() const
 {
        LBUFERR(!par_metrics_.empty());
@@ -191,8 +208,7 @@ void TextMetrics::newParMetricsDown()
 
        // do it and update its position.
        redoParagraph(pit);
-       par_metrics_[pit].setPosition(last.second.position()
-               + last.second.descent() + par_metrics_[pit].ascent());
+       par_metrics_[pit].setPosition(last.second.bottom() + par_metrics_[pit].ascent());
        updatePosCache(pit);
 }
 
@@ -206,12 +222,65 @@ void TextMetrics::newParMetricsUp()
        pit_type const pit = first.first - 1;
        // do it and update its position.
        redoParagraph(pit);
-       par_metrics_[pit].setPosition(first.second.position()
-               - first.second.ascent() - par_metrics_[pit].descent());
+       par_metrics_[pit].setPosition(first.second.top() - par_metrics_[pit].descent());
        updatePosCache(pit);
 }
 
 
+void TextMetrics::updateMetrics(pit_type const anchor_pit, int const anchor_ypos,
+                                int const bv_height)
+{
+       LASSERT(text_->isMainText(), return);
+
+       // Forget existing positions
+       for (auto & pm_pair : par_metrics_)
+               pm_pair.second.resetPosition();
+
+       if (!contains(anchor_pit))
+               // Rebreak anchor paragraph.
+               redoParagraph(anchor_pit);
+       ParagraphMetrics & anchor_pm = parMetrics(anchor_pit);
+       anchor_pm.setPosition(anchor_ypos);
+
+       // Redo paragraphs above anchor if necessary.
+       int y1 = anchor_ypos - anchor_pm.ascent();
+       // We are now just above the anchor paragraph.
+       pit_type pit1 = anchor_pit - 1;
+       for (; pit1 >= 0 && y1 > 0; --pit1) {
+               if (!contains(pit1))
+                       redoParagraph(pit1);
+               ParagraphMetrics & pm = parMetrics(pit1);
+               y1 -= pm.descent();
+               // Save the paragraph position in the cache.
+               pm.setPosition(y1);
+               y1 -= pm.ascent();
+       }
+
+       // Redo paragraphs below the anchor if necessary.
+       int y2 = anchor_ypos + anchor_pm.descent();
+       // We are now just below the anchor paragraph.
+       pit_type pit2 = anchor_pit + 1;
+       pit_type const npit = pit_type(text_->paragraphs().size());
+       for (; pit2 < npit && y2 < bv_height; ++pit2) {
+               if (!contains(pit2))
+                       redoParagraph(pit2);
+               ParagraphMetrics & pm = parMetrics(pit2);
+               y2 += pm.ascent();
+               // Save the paragraph position in the cache.
+               pm.setPosition(y2);
+               y2 += pm.descent();
+       }
+
+       LYXERR(Debug::PAINTING, "TextMetrics::updateMetrics "
+               << " anchor pit = " << anchor_pit
+               << " anchor ypos = " << anchor_ypos
+               << " y1 = " << y1
+               << " y2 = " << y2
+               << " pit1 = " << pit1
+               << " pit2 = " << pit2);
+}
+
+
 bool TextMetrics::metrics(MetricsInfo const & mi, Dimension & dim, int min_width)
 {
        LBUFERR(mi.base.textwidth > 0);
@@ -522,17 +591,20 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows)
                MetricsInfo mi(bv_, font.fontInfo(), w, mc, e.pos == 0, tight_);
                mi.base.outer_font = displayFont(pit, e.pos).fontInfo();
                e.inset->metrics(mi, dim);
-               /* FIXME: This is a kind of hack. This allows InsetMathHull to
-                * state that it needs some elbow room beyond its width, in
-                * order to fit the numbering and/or the left margin (with
-                * left alignment), which are outside of the inset itself.
+               /* FIXME: This is a hack. This allows InsetMathHull to state
+                * that it needs some elbow room beyond its width, in order to
+                * fit the numbering and/or the left margin (with left
+                * alignment), which are outside of the inset itself.
                 *
                 * To this end, InsetMathHull::metrics() sets a value in
-                * MetricsInfo::extrawidth and this value is recorded later in
-                * the corresponding row element's `extra' field. See ticket
-                * #12320 for details.
+                * MetricsInfo::extrawidth and this value is added later to
+                * the width of the row that contains the inset (when this row
+                * is tight or shorter than the max allowed width).
+                *
+                * See ticket #12320 for details.
                */
                extrawidths[e.inset] = mi.extrawidth;
+
                if (!insetCache.has(e.inset) || insetCache.dim(e.inset) != dim) {
                        insetCache.add(e.inset, dim);
                        changed = true;
@@ -540,14 +612,16 @@ bool TextMetrics::redoParagraph(pit_type const pit, bool const align_rows)
        }
 
        // Transform the paragraph into a single row containing all the elements.
-       Row bigrow = tokenizeParagraph(pit);
-       // Add the needed extra width to the row elements of the insets
-       for (auto & e : bigrow)
-               if (e.type == Row::INSET)
-                       e.extra = extrawidths[e.inset];
+       Row const bigrow = tokenizeParagraph(pit);
        // Split the row in several rows fitting in available width
        pm.rows() = breakParagraph(bigrow);
 
+       // Add the needed extra width to the rows that contain the insets that request it
+       for (Row & row : pm.rows())
+               for (Row::Element & e : row)
+                       if (e.type == Row::INSET && (row.width() < max_width_ || tight_))
+                               row.dim().wid += extrawidths[e.inset];
+
        /* If there is more than one row, expand the text to the full
         * allowable width. This setting here is needed for the
         * setRowAlignment() below. We do nothing when tight insets are
@@ -1176,6 +1250,9 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const
                // Last row in paragraph is flushed
                rows.back().flushed(true);
                cleanupRow(rows.back(), true);
+               // Is there an end-of-paragraph change?
+               if (bigrow.needsChangeBar())
+                       rows.back().needsChangeBar(true);
        }
 
        return rows;
@@ -1458,9 +1535,7 @@ pit_type TextMetrics::getPitNearY(int y)
        ParMetricsCache::const_iterator last = et;
        --last;
 
-       ParagraphMetrics const & pm = it->second;
-
-       if (y < it->second.position() - pm.ascent()) {
+       if (y < it->second.top()) {
                // We are looking for a position that is before the first paragraph in
                // the cache (which is in priciple off-screen, that is before the
                // visible part.
@@ -1473,9 +1548,7 @@ pit_type TextMetrics::getPitNearY(int y)
                return pit;
        }
 
-       ParagraphMetrics const & pm_last = par_metrics_[last->first];
-
-       if (y >= last->second.position() + pm_last.descent()) {
+       if (y >= par_metrics_[last->first].bottom()) {
                // We are looking for a position that is after the last paragraph in
                // the cache (which is in priciple off-screen), that is before the
                // visible part.
@@ -1489,12 +1562,10 @@ pit_type TextMetrics::getPitNearY(int y)
        }
 
        for (; it != et; ++it) {
-               LYXERR(Debug::PAINTING, "examining: pit: " << it->first
-                       << " y: " << it->second.position());
-
-               ParagraphMetrics const & pm2 = par_metrics_[it->first];
+               // LYXERR(Debug::PAINTING, "examining: pit: " << it->first
+               //      << " y: " << it->second.position());
 
-               if (it->first >= pit && it->second.position() - pm2.ascent() <= y) {
+               if (it->first >= pit && it->second.top() <= y) {
                        pit = it->first;
                        yy = it->second.position();
                }
@@ -1511,7 +1582,7 @@ Row const & TextMetrics::getPitAndRowNearY(int & y, pit_type & pit,
 {
        ParagraphMetrics const & pm = par_metrics_[pit];
 
-       int yy = pm.position() - pm.ascent();
+       int yy = pm.top();
        LBUFERR(!pm.rows().empty());
        RowList::const_iterator rit = pm.rows().begin();
        RowList::const_iterator rlast = pm.rows().end();
@@ -1992,9 +2063,16 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
        if (pm.rows().empty())
                return;
        size_t const nrows = pm.rows().size();
+       int const wh = bv_->workHeight();
        // Remember left and right margin for drawing math numbers
-       Changer changeleft = changeVar(pi.leftx, x + leftMargin(pit));
-       Changer changeright = changeVar(pi.rightx, x + width() - rightMargin(pit));
+       Changer changeleft, changeright;
+       if (text_->isRTL(pit)) {
+               changeleft = changeVar(pi.leftx, x + rightMargin(pit));
+               changeright = changeVar(pi.rightx, x + width() - leftMargin(pit));
+       } else {
+               changeleft = changeVar(pi.leftx, x + leftMargin(pit));
+               changeright = changeVar(pi.rightx, x + width() - rightMargin(pit));
+       }
 
        // Use fast lane in nodraw stage.
        if (pi.pain.isNull()) {
@@ -2006,15 +2084,17 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                        if (i)
                                y += row.ascent();
 
-                       RowPainter rp(pi, *text_, row, row_x, y);
-
-                       rp.paintOnlyInsets();
+                       // It is not needed to draw on screen if we are not inside
+                       bool const inside = (y + row.descent() >= 0 && y - row.ascent() < wh);
+                       if (inside) {
+                               RowPainter rp(pi, *text_, row, row_x, y);
+                               rp.paintOnlyInsets();
+                       }
                        y += row.descent();
                }
                return;
        }
 
-       int const ww = bv_->workHeight();
        Cursor const & cur = bv_->cursor();
        DocIterator sel_beg = cur.selectionBegin();
        DocIterator sel_end = cur.selectionEnd();
@@ -2041,9 +2121,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                }
        }
 
-       if (text_->isRTL(pit))
-               swap(pi.leftx, pi.rightx);
-
        BookmarksSection::BookmarkPosList bpl =
                theSession().bookmarks().bookmarksInPar(bv_->buffer().fileName(), pm.id());
 
@@ -2057,7 +2134,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
 
                // It is not needed to draw on screen if we are not inside.
                bool const inside = (y + row.descent() >= 0
-                       && y - row.ascent() < ww);
+                       && y - row.ascent() < wh);
                if (!inside) {
                        // Inset positions have already been set in nodraw stage.
                        y += row.descent();