diff --git a/src/Row.cpp b/src/Row.cpp index b7e4c07c95..6b0faa3292 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -123,41 +123,81 @@ pos_type Row::Element::x2pos(int &x) const x = 0; i = isRTL(); } - break; - case INVALID: - LYXERR0("x2pos: INVALID row element !"); } //lyxerr << "=> p=" << pos + i << " x=" << x << endl; return pos + i; } -Row::Element Row::Element::splitAt(int w, bool force) +bool Row::Element::splitAt(int const width, int next_width, bool force, + Row::Elements & tail) { - if (type != STRING || !(row_flags & CanBreakInside)) - return Element(); + // Not a string or already OK. + if (type != STRING || (dim.wid > 0 && dim.wid < width)) + return false; FontMetrics const & fm = theFontMetrics(font); - dim.wid = w; - int const i = fm.breakAt(str, dim.wid, isRTL(), force); - if (i != -1) { - //Create a second row element to return - Element ret(STRING, pos + i, font, change); - ret.str = str.substr(i); - ret.endpos = ret.pos + ret.str.length(); - // Copy the after flags of the original element to the second one. - ret.row_flags = row_flags & (CanBreakInside | AfterFlags); - // Now update ourselves - str.erase(i); - endpos = pos + i; - // Row should be broken after the original element - row_flags = (row_flags & ~AfterFlags) | BreakAfter; - //LYXERR0("breakAt(" << w << ") Row element Broken at " << w << "(w(str)=" << fm.width(str) << "): e=" << *this); - return ret; + // A a string that is not breakable + if (!(row_flags & CanBreakInside)) { + // has width been computed yet? + if (dim.wid == 0) + dim.wid = fm.width(str); + return false; } - return Element(); + bool const wrap_any = !font.language()->wordWrap(); + FontMetrics::Breaks breaks = fm.breakString(str, width, next_width, + isRTL(), wrap_any | force); + + // if breaking did not really work, give up + if (!force && breaks.front().wid > width) { + if (dim.wid == 0) + dim.wid = fm.width(str); + return false; + } + + Element first_e(STRING, pos, font, change); + // should next element eventually replace *this? + bool first = true; + docstring::size_type i = 0; + for (FontMetrics::Break const & brk : breaks) { + Element e(STRING, pos + i, font, change); + e.str = str.substr(i, brk.len); + e.endpos = e.pos + brk.len; + e.dim.wid = brk.wid; + e.row_flags = CanBreakInside | BreakAfter; + if (first) { + // this element eventually goes to *this + e.row_flags |= row_flags & ~AfterFlags; + first_e = e; + first = false; + } else + tail.push_back(e); + i += brk.len; + } + + if (!tail.empty()) { + // Avoid having a last empty element. This happens when + // breaking at the trailing space of string + if (tail.back().str.empty()) + tail.pop_back(); + else { + // Copy the after flags of the original element to the last one. + tail.back().row_flags &= ~BreakAfter; + tail.back().row_flags |= row_flags & AfterFlags; + } + // first_e row should be broken after the original element + first_e.row_flags |= BreakAfter; + } else { + // Restore the after flags of the original element. + first_e.row_flags &= ~BreakAfter; + first_e.row_flags |= row_flags & AfterFlags; + } + + // update ourselves + swap(first_e, *this); + return true; } @@ -265,10 +305,6 @@ ostream & operator<<(ostream & os, Row::Element const & e) break; case Row::SPACE: os << "SPACE: "; - break; - case Row::INVALID: - os << "INVALID: "; - break; } os << "width=" << e.full_width() << ", row_flags=" << e.row_flags; return os; @@ -393,11 +429,6 @@ void Row::finalizeLast() elt.final = true; if (elt.change.changed()) changebar_ = true; - - if (elt.type == STRING && elt.dim.wid == 0) { - elt.dim.wid = theFontMetrics(elt.font).width(elt.str); - dim_.wid += elt.dim.wid; - } } @@ -474,19 +505,14 @@ void Row::pop_back() namespace { -// Remove stuff after \c it from \c elts, and return it. -// if \c init is provided, it will prepended to the rest -Row::Elements splitFrom(Row::Elements & elts, Row::Elements::iterator const & it, - Row::Element const & init = Row::Element()) +// Move stuff after \c it from \c from and the end of \c to. +void moveElements(Row::Elements & from, Row::Elements::iterator const & it, + Row::Elements & to) { - Row::Elements ret; - if (init.isValid()) - ret.push_back(init); - ret.insert(ret.end(), it, elts.end()); - elts.erase(it, elts.end()); - if (!elts.empty()) - elts.back().row_flags = (elts.back().row_flags & ~AfterFlags) | BreakAfter; - return ret; + to.insert(to.end(), it, from.end()); + from.erase(it, from.end()); + if (!from.empty()) + from.back().row_flags = (from.back().row_flags & ~AfterFlags) | BreakAfter; } } @@ -522,6 +548,7 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) Elements::iterator cit_brk = cit; int wid_brk = wid + cit_brk->dim.wid; ++cit_brk; + Elements tail; while (cit_brk != beg) { --cit_brk; // make a copy of the element to work on it. @@ -533,28 +560,18 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) if (wid_brk <= w && brk.row_flags & CanBreakAfter) { end_ = brk.endpos; dim_.wid = wid_brk; - return splitFrom(elements_, cit_brk + 1); + moveElements(elements_, cit_brk + 1, tail); + return tail; } // assume now that the current element is not there wid_brk -= brk.dim.wid; - /* - * Some Asian languages split lines anywhere (no notion of - * word). It seems that QTextLayout is not aware of this fact. - * See for reference: - * https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages - * - * FIXME: Something shall be done about characters which are - * not allowed at the beginning or end of line. - */ - bool const word_wrap = brk.font.language()->wordWrap(); /* We have found a suitable separable element. This is the common case. - * Try to break it cleanly (at word boundary) at a length that is both + * Try to break it cleanly at a length that is both * - less than the available space on the row * - shorter than the natural width of the element, in order to enforce * break-up. */ - Element remainder = brk.splitAt(min(w - wid_brk, brk.dim.wid - 2), !word_wrap); - if (brk.row_flags & BreakAfter) { + if (brk.splitAt(min(w - wid_brk, brk.dim.wid - 2), next_width, false, tail)) { /* if this element originally did not cause a row overflow * in itself, and the remainder of the row would still be * too large after breaking, then we will have issues in @@ -568,12 +585,10 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) *cit_brk = brk; dim_.wid = wid_brk + brk.dim.wid; // If there are other elements, they should be removed. - // remainder can be empty when splitting at trailing space - if (remainder.str.empty()) - return splitFrom(elements_, next(cit_brk, 1)); - else - return splitFrom(elements_, next(cit_brk, 1), remainder); + moveElements(elements_, cit_brk + 1, tail); + return tail; } + LATTEST(tail.empty()); } if (cit != beg && cit->row_flags & NoBreakBefore) { @@ -588,20 +603,23 @@ Row::Elements Row::shortenIfNeeded(int const w, int const next_width) // been added. We can cut right here. end_ = cit->pos; dim_.wid = wid; - return splitFrom(elements_, cit); + moveElements(elements_, cit, tail); + return tail; } /* If we are here, it means that we have not found a separator to - * shorten the row. Let's try to break it again, but not at word - * boundary this time. + * shorten the row. Let's try to break it again, but force + * splitting this time. */ - Element remainder = cit->splitAt(w - wid, true); - if (cit->row_flags & BreakAfter) { + if (cit->splitAt(w - wid, next_width, true, tail)) { + LYXERR0(*cit); end_ = cit->endpos; dim_.wid = wid + cit->dim.wid; // If there are other elements, they should be removed. - return splitFrom(elements_, next(cit, 1), remainder); + moveElements(elements_, cit + 1, tail); + return tail; } + return Elements(); } diff --git a/src/Row.h b/src/Row.h index 1272a0f6ea..bf49eb1b33 100644 --- a/src/Row.h +++ b/src/Row.h @@ -50,9 +50,7 @@ public: // An inset INSET, // Some spacing described by its width, not a string - SPACE, - // Something that should not happen (for error handling) - INVALID + SPACE }; /** @@ -60,8 +58,6 @@ public: * by other methods that need to parse the Row contents. */ struct Element { - // - Element() = default; // Element(Type const t, pos_type p, Font const & f, Change const & ch) : type(t), pos(p), endpos(p + 1), font(f), change(ch) {} @@ -94,13 +90,16 @@ public: pos_type x2pos(int &x) const; /** Break the element in two if possible, so that its width is less * than \param w. - * \return an element containing the remainder of the text, or - * an invalid element if nothing happened. - * \param w: the desired maximum width - * \param force: if true, the string is cut at any place, otherwise it - * respects the row breaking rules of characters. + * \return a vector of elements containing the remainder of + * the text (empty if nothing happened). + * \param width maximum width of the row. + * \param next_width available width on next row. + * \param force: if true, cut string at any place, even for + * languages that wrap at word delimiters; if false, do not + * break at all if first element would larger than \c width. */ - Element splitAt(int w, bool force); + // FIXME: ideally last parameter should be Elements&, but it is not possible. + bool splitAt(int width, int next_width, bool force, std::vector & tail); // remove trailing spaces (useful for end of row) void rtrim(); @@ -108,8 +107,6 @@ public: bool isRTL() const { return font.isVisibleRightToLeft(); } // This is true for virtual elements. bool isVirtual() const { return type == VIRTUAL; } - // Invalid element, for error handling - bool isValid() const { return type !=INVALID; } // Returns the position on left side of the element. pos_type left_pos() const { return isRTL() ? endpos : pos; }; @@ -117,11 +114,11 @@ public: pos_type right_pos() const { return isRTL() ? pos : endpos; }; // The kind of row element - Type type = INVALID; + Type type; // position of the element in the paragraph - pos_type pos = 0; + pos_type pos; // first position after the element in the paragraph - pos_type endpos = 0; + pos_type endpos; // The dimension of the chunk (does not contains the // separator correction) Dimension dim; @@ -289,8 +286,8 @@ public: * separator and update endpos if necessary. If all that * remains is a large word, cut it to \param width. * \param width maximum width of the row. - * \param available width on next row. - * \return true if the row has been shortened. + * \param next_width available width on next row. + * \return list of elements remaining after breaking. */ Elements shortenIfNeeded(int const width, int const next_width); diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 656f89a4e0..400b7b66e8 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -565,10 +565,6 @@ void RowPainter::paintText() case Row::SPACE: paintTextDecoration(e); - break; - - case Row::INVALID: - LYXERR0("Trying to paint INVALID row element."); } // The markings of foreign languages diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index d681a63356..76e2fb1170 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1059,7 +1059,7 @@ Row newRow(TextMetrics const & tm, pit_type pit, pos_type pos, bool is_rtl) } -void cleanupRow(Row & row, pos_type real_endpos, bool is_rtl) +void cleanupRow(Row & row, bool at_end, bool is_rtl) { if (row.empty()) { row.endpos(0); @@ -1067,11 +1067,12 @@ void cleanupRow(Row & row, pos_type real_endpos, bool is_rtl) } row.endpos(row.back().endpos); + row.flushed(at_end); // remove trailing spaces on row break - if (row.endpos() < real_endpos) + if (!at_end) row.back().rtrim(); // boundary exists when there was no space at the end of row - row.right_boundary(row.endpos() < real_endpos && row.back().endpos == row.endpos()); + row.right_boundary(!at_end && row.back().endpos == row.endpos()); // make sure that the RTL elements are in reverse ordering row.reverseRTL(is_rtl); } @@ -1098,6 +1099,8 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const RowList rows; bool const is_rtl = text_->isRTL(bigrow.pit()); bool const end_label = text_->getEndLabel(bigrow.pit()) != END_LABEL_NO_LABEL; + int const next_width = max_width_ - leftMargin(bigrow.pit(), bigrow.endpos()) + - rightMargin(bigrow.pit()); int width = 0; flexible_const_iterator fcit = flexible_begin(bigrow); @@ -1115,7 +1118,7 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const : fcit->row_flags; if (rows.empty() || needsRowBreak(f1, f2)) { if (!rows.empty()) - cleanupRow(rows.back(), bigrow.endpos(), is_rtl); + cleanupRow(rows.back(), false, is_rtl); pos_type pos = rows.empty() ? 0 : rows.back().endpos(); rows.push_back(newRow(*this, bigrow.pit(), pos, is_rtl)); // the width available for the row. @@ -1130,45 +1133,26 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) const // Next element to consider is either the top of the temporary // pile, or the place when we were in main row Row::Element elt = *fcit; - Row::Element next_elt = elt.splitAt(width - rows.back().width(), - !elt.font.language()->wordWrap()); - if (elt.dim.wid > width - rows.back().width()) { - Row & rb = rows.back(); - rb.push_back(*fcit); - // if the row is too large, try to cut at last separator. In case - // of success, reset indication that the row was broken abruptly. - int const next_width = max_width_ - leftMargin(rb.pit(), rb.endpos()) - - rightMargin(rb.pit()); - - Row::Elements next_elts = rb.shortenIfNeeded(width, next_width); - - // Go to next element - ++fcit; - - // Handle later the elements returned by shortenIfNeeded. - if (!next_elts.empty()) { - rb.flushed(false); - fcit.put(next_elts); - } - } else { - // a new element in the row - rows.back().push_back(elt); - rows.back().finalizeLast(); - - // Go to next element - ++fcit; - - // Add a new next element on the pile - if (next_elt.isValid()) { - // do as if we inserted this element in the original row - if (!next_elt.str.empty()) - fcit.put(next_elt); - } + Row::Elements tail; + elt.splitAt(width - rows.back().width(), next_width, false, tail); + Row & rb = rows.back(); + rb.push_back(elt); + rb.finalizeLast(); + if (rb.width() > width) { + LATTEST(tail.empty()); + // if the row is too large, try to cut at last separator. + tail = rb.shortenIfNeeded(width, next_width); } + + // Go to next element + ++fcit; + + // Handle later the elements returned by splitAt or shortenIfNeeded. + fcit.put(tail); } if (!rows.empty()) { - cleanupRow(rows.back(), bigrow.endpos(), is_rtl); + cleanupRow(rows.back(), true, is_rtl); // Last row in paragraph is flushed rows.back().flushed(true); } diff --git a/src/frontends/FontMetrics.h b/src/frontends/FontMetrics.h index b562ebfac1..b78bc2dbcd 100644 --- a/src/frontends/FontMetrics.h +++ b/src/frontends/FontMetrics.h @@ -16,6 +16,8 @@ #include "support/strfwd.h" +#include + /** * A class holding helper functions for determining * the screen dimensions of fonts. @@ -121,15 +123,27 @@ public: * \param ws is the amount of extra inter-word space applied text justification. */ virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) const = 0; + + // The places where to break a string and the width of the resulting lines. + struct Break { + Break(int l, int w) : len(l), wid(w) {} + int len = 0; + int wid = 0; + }; + typedef std::vector Breaks; /** - * Break string s at width at most x. - * \return break position (-1 if not successful) - * \param position x is updated to real width - * \param rtl is true for right-to-left layout + * Break a string in multiple fragments according to width limits. + * \return a sequence of Break elements. + * \param s is the string to break. + * \param first_wid is the available width for first line. + * \param wid is the available width for the next lines. + * \param rtl is true for right-to-left layout. * \param force is false for breaking at word separator, true for * arbitrary position. */ - virtual int breakAt(docstring const & s, int & x, bool rtl, bool force) const = 0; + virtual Breaks + breakString(docstring const & s, int first_wid, int wid, bool rtl, bool force) const = 0; + /// return char dimension for the font. virtual Dimension const dimension(char_type c) const = 0; /** diff --git a/src/frontends/qt/GuiFontMetrics.cpp b/src/frontends/qt/GuiFontMetrics.cpp index 47537ae0a3..3feb33e6a8 100644 --- a/src/frontends/qt/GuiFontMetrics.cpp +++ b/src/frontends/qt/GuiFontMetrics.cpp @@ -19,6 +19,7 @@ #include "support/convert.h" #include "support/lassert.h" +#include "support/lstrings.h" #include "support/lyxlib.h" #include "support/debug.h" @@ -86,14 +87,14 @@ namespace frontend { /* - * Limit (strwidth|breakat)_cache_ size to 512kB of string data. + * Limit (strwidth|breakstr)_cache_ size to 512kB of string data. * Limit qtextlayout_cache_ size to 500 elements (we do not know the * size of the QTextLayout objects anyway). * Note that all these numbers are arbitrary. * Also, setting size to 0 is tantamount to disabling the cache. */ int cache_metrics_width_size = 1 << 19; -int cache_metrics_breakat_size = 1 << 19; +int cache_metrics_breakstr_size = 1 << 19; // Qt 5.x already has its own caching of QTextLayout objects // but it does not seem to work well on MacOS X. #if (QT_VERSION < 0x050000) || defined(Q_OS_MAC) @@ -128,7 +129,7 @@ inline QChar const ucs4_to_qchar(char_type const ucs4) GuiFontMetrics::GuiFontMetrics(QFont const & font) : font_(font), metrics_(font, 0), strwidth_cache_(cache_metrics_width_size), - breakat_cache_(cache_metrics_breakat_size), + breakstr_cache_(cache_metrics_breakstr_size), qtextlayout_cache_(cache_metrics_qtextlayout_size) { // Determine italic slope @@ -485,11 +486,13 @@ int GuiFontMetrics::countExpanders(docstring const & str) const } -pair -GuiFontMetrics::breakAt_helper(docstring const & s, int const x, - bool const rtl, bool const force) const +namespace { + +const int brkStrOffset = 1 + BIDI_OFFSET; + + +QString createBreakableString(docstring const & s, bool rtl, QTextLayout & tl) { - QTextLayout tl; /* Qt will not break at a leading or trailing space, and we need * that sometimes, see http://www.lyx.org/trac/ticket/9921. * @@ -518,34 +521,23 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x, // Left-to-right override: forces to draw text left-to-right qs = QChar(0x202D) + qs; #endif - int const offset = 1 + BIDI_OFFSET; + return qs; +} - tl.setText(qs); - tl.setFont(font_); - QTextOption to; - to.setWrapMode(force ? QTextOption::WrapAtWordBoundaryOrAnywhere - : QTextOption::WordWrap); - // Let QTextLine::naturalTextWidth() account for trailing spaces - // (horizontalAdvance() still does not). - to.setFlags(QTextOption::IncludeTrailingSpaces); - tl.setTextOption(to); - tl.beginLayout(); - QTextLine line = tl.createLine(); - line.setLineWidth(x); - tl.createLine(); - tl.endLayout(); - int line_wid = iround(line.horizontalAdvance()); - if ((force && line.textLength() == offset) || line_wid > x) - return {-1, line_wid}; + +docstring::size_type brkstr2str_pos(QString brkstr, docstring const & str, int pos) +{ /* Since QString is UTF-16 and docstring is UCS-4, the offsets may * not be the same when there are high-plan unicode characters * (bug #10443). */ - // The variable `offset' is here to account for the extra leading characters. + // The variable `brkStrOffset' is here to account for the extra leading characters. // The ending character zerow_nbsp has to be ignored if the line is complete. - int const qlen = line.textLength() - offset - (line.textLength() == qs.length()); + int const qlen = pos - brkStrOffset - (pos == brkstr.length()); #if QT_VERSION < 0x040801 || QT_VERSION >= 0x050100 - int len = qstring_to_ucs4(qs.mid(offset, qlen)).length(); + auto const len = qstring_to_ucs4(brkstr.mid(brkStrOffset, qlen)).length(); + // Avoid warning + (void)str; #else /* Due to QTBUG-25536 in 4.8.1 <= Qt < 5.1.0, the string returned * by QString::toUcs4 (used by qstring_to_ucs4) may have wrong @@ -555,52 +547,108 @@ GuiFontMetrics::breakAt_helper(docstring const & s, int const x, * worthwhile to implement a dichotomy search if this shows up * under a profiler. */ - int len = min(qlen, static_cast(s.length())); - while (len >= 0 && toqstr(s.substr(0, len)).length() != qlen) + int len = min(qlen, static_cast(str.length())); + while (len >= 0 && toqstr(str.substr(0, len)).length() != qlen) --len; LASSERT(len > 0 || qlen == 0, /**/); #endif - // Do not cut is the string is already short enough. We rely on - // naturalTextWidth() to catch the case where we cut at the trailing - // space. - if (len == static_cast(s.length()) - && line.naturalTextWidth() <= x) { - len = -1; -#if QT_VERSION < 0x050000 + return len; +} + +} + +FontMetrics::Breaks +GuiFontMetrics::breakString_helper(docstring const & s, int first_wid, int wid, + bool rtl, bool force) const +{ + QTextLayout tl; + QString qs = createBreakableString(s, rtl, tl); + tl.setText(qs); + tl.setFont(font_); + QTextOption to; + /* + * Some Asian languages split lines anywhere (no notion of + * word). It seems that QTextLayout is not aware of this fact. + * See for reference: + * https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages + * + * FIXME: Something shall be done about characters which are + * not allowed at the beginning or end of line. + */ + to.setWrapMode(force ? QTextOption::WrapAtWordBoundaryOrAnywhere + : QTextOption::WordWrap); + // Let QTextLine::naturalTextWidth() account for trailing spaces + // (horizontalAdvance() still does not). + to.setFlags(QTextOption::IncludeTrailingSpaces); + tl.setTextOption(to); + + bool first = true; + tl.beginLayout(); + while(true) { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(first ? first_wid : wid); + tl.createLine(); + first = false; + } + tl.endLayout(); + + Breaks breaks; + int pos = 0; + for (int i = 0 ; i < tl.lineCount() ; ++i) { + QTextLine const & line = tl.lineAt(i); + int const epos = brkstr2str_pos(qs, s, line.textStart() + line.textLength()); +#if QT_VERSION >= 0x050000 + int const wid = i + 1 < tl.lineCount() ? iround(line.horizontalAdvance()) + : iround(line.naturalTextWidth()); +#else // With some monospace fonts, the value of horizontalAdvance() // can be wrong with Qt4. One hypothesis is that the invisible // characters that we use are given a non-null width. - line_wid = width(s); + // FIXME: this is slower than it could be but we'll get rid of Qt4 anyway + int const wid = i + 1 < tl.lineCount() ? width(rtrim(s.substr(pos, epos - pos))) + : width(s.substr(pos, epos - pos)); #endif + breaks.emplace_back(epos - pos, wid); + pos = epos; +#if 0 + // FIXME: should it be kept in some form? + if ((force && line.textLength() == brkStrOffset) || line_wid > x) + return {-1, line_wid}; +#endif + } - return {len, line_wid}; + + return breaks; } -uint qHash(BreakAtKey const & key) +uint qHash(BreakStringKey const & key) { - int params = key.force + 2 * key.rtl + 4 * key.x; + // assume widths are less than 10000. This fits in 32 bits. + uint params = key.force + 2 * key.rtl + 4 * key.first_wid + 10000 * key.wid; return std::qHash(key.s) ^ ::qHash(params); } -int GuiFontMetrics::breakAt(docstring const & s, int & x, bool const rtl, bool const force) const +FontMetrics::Breaks GuiFontMetrics::breakString(docstring const & s, int first_wid, int wid, + bool rtl, bool force) const { - PROFILE_THIS_BLOCK(breakAt); + PROFILE_THIS_BLOCK(breakString); if (s.empty()) - return false; + return Breaks(); - BreakAtKey key{s, x, rtl, force}; - pair pp; - if (auto * pp_ptr = breakat_cache_.object_ptr(key)) - pp = *pp_ptr; + BreakStringKey key{s, first_wid, wid, rtl, force}; + Breaks brks; + if (auto * brks_ptr = breakstr_cache_.object_ptr(key)) + brks = *brks_ptr; else { - PROFILE_CACHE_MISS(breakAt); - pp = breakAt_helper(s, x, rtl, force); - breakat_cache_.insert(key, pp, sizeof(key) + s.size() * sizeof(char_type)); + PROFILE_CACHE_MISS(breakString); + brks = breakString_helper(s, first_wid, wid, rtl, force); + breakstr_cache_.insert(key, brks, sizeof(key) + s.size() * sizeof(char_type)); } - x = pp.second; - return pp.first; + return brks; } diff --git a/src/frontends/qt/GuiFontMetrics.h b/src/frontends/qt/GuiFontMetrics.h index ef8588a5f8..9501eb866d 100644 --- a/src/frontends/qt/GuiFontMetrics.h +++ b/src/frontends/qt/GuiFontMetrics.h @@ -27,14 +27,16 @@ namespace lyx { namespace frontend { -struct BreakAtKey +struct BreakStringKey { - bool operator==(BreakAtKey const & key) const { - return key.s == s && key.x == x && key.rtl == rtl && key.force == force; + bool operator==(BreakStringKey const & key) const { + return key.s == s && key.first_wid == first_wid && key.wid == wid + && key.rtl == rtl && key.force == force; } docstring s; - int x; + int first_wid; + int wid; bool rtl; bool force; }; @@ -77,7 +79,7 @@ public: int signedWidth(docstring const & s) const override; int pos2x(docstring const & s, int pos, bool rtl, double ws) const override; int x2pos(docstring const & s, int & x, bool rtl, double ws) const override; - int breakAt(docstring const & s, int & x, bool rtl, bool force) const override; + Breaks breakString(docstring const & s, int first_wid, int wid, bool rtl, bool force) const override; Dimension const dimension(char_type c) const override; void rectText(docstring const & str, @@ -101,8 +103,8 @@ public: private: - std::pair breakAt_helper(docstring const & s, int const x, - bool const rtl, bool const force) const; + Breaks breakString_helper(docstring const & s, int first_wid, int wid, + bool rtl, bool force) const; /// The font QFont font_; @@ -117,8 +119,8 @@ private: mutable QHash width_cache_; /// Cache of string widths mutable Cache strwidth_cache_; - /// Cache for breakAt - mutable Cache> breakat_cache_; + /// Cache for breakString + mutable Cache breakstr_cache_; /// Cache for QTextLayout mutable Cache> qtextlayout_cache_;