commit 71d9f6e90d52d8f85c9e8b530027a9fd59c697e8
Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
Date:   Mon Sep 25 12:35:40 2023 +0200

    Avoid row breaking at inconvenient places.
    
    When it turns out that breaking a STRING row element was not
    sufficient in Row::shortenIfNeeded, we still remember the shortest
    width that one can obtain. Later, when we try to split a previous
    element of the row, we have a better idea of how much of the row
    remains after it.
    
    To this end, change the signature of Element::splitAt to use an enum:
    FIT (was: force=false), FORCE (was: force= true) and BEST_EFFORT
    (split at max_width, but do not return an error if the string is too
    large).
    
    Fixes bug #12660.
---
 src/Row.cpp         |   25 ++++++++++++++++++-------
 src/Row.h           |   14 ++++++++++----
 src/TextMetrics.cpp |    2 +-
 3 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/src/Row.cpp b/src/Row.cpp
index a7f00d5..e911fef 100644
--- a/src/Row.cpp
+++ b/src/Row.cpp
@@ -123,7 +123,7 @@ pos_type Row::Element::x2pos(int &x) const
 }
 
 
-bool Row::Element::splitAt(int const width, int next_width, bool force,
+bool Row::Element::splitAt(int const width, int next_width, SplitType 
split_type,
                            Row::Elements & tail)
 {
        // Not a string or already OK.
@@ -142,13 +142,13 @@ bool Row::Element::splitAt(int const width, int 
next_width, bool force,
 
        bool const wrap_any = !font.language()->wordWrap();
        FontMetrics::Breaks breaks = fm.breakString(str, width, next_width,
-                                                isRTL(), wrap_any | force);
+                                                isRTL(), wrap_any || 
split_type == FORCE);
 
        /** if breaking did not really work, give up
-        * case 1: we do not force break and the first element is longer than 
the limit;
+        * case 1: split type is FIT and the first element is longer than the 
limit;
         * case 2: the first break occurs at the front of the string
         */
-       if ((!force && breaks.front().nspc_wid > width)
+       if ((split_type == FIT && breaks.front().nspc_wid > width)
            || (breaks.size() > 1 && breaks.front().len == 0)) {
                if (dim.wid == 0)
                        dim.wid = fm.width(str);
@@ -548,6 +548,8 @@ Row::Elements Row::shortenIfNeeded(int const max_width, int 
const next_width)
        Elements::iterator const beg = elements_.begin();
        Elements::iterator const end = elements_.end();
        int wid = left_margin;
+       // the smallest row width we know we can achieve by breaking a string.
+       int min_row_wid = dim_.wid;
 
        // Search for the first element that goes beyond right margin
        Elements::iterator cit = beg;
@@ -600,14 +602,23 @@ Row::Elements Row::shortenIfNeeded(int const max_width, 
int const next_width)
                 * - shorter than the natural width of the element, in order to 
enforce
                 *   break-up.
                 */
-               if (brk.splitAt(min(max_width - wid_brk, brk.dim.wid - 2), 
next_width, false, tail)) {
+               int const split_width =  min(max_width - wid_brk, brk.dim.wid - 
2);
+               if (brk.splitAt(split_width, next_width, BEST_EFFORT, tail)) {
+                       // if we did not manage to fit a part of the element 
into
+                       // the split_width limit, at least remember that we can
+                       // shorten the row if needed.
+                       if (brk.dim.wid > split_width) {
+                               min_row_wid = wid_brk + brk.dim.wid;
+                               tail.clear();
+                               continue;
+                       }
                        /* 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
                         * next row. Thus breaking does not help.
                         */
                        if (wid_brk + cit_brk->dim.wid < max_width
-                           && dim_.wid - (wid_brk + brk.dim.wid) >= 
next_width) {
+                           && min_row_wid - (wid_brk + brk.dim.wid) >= 
next_width) {
                                tail.clear();
                                break;
                        }
@@ -641,7 +652,7 @@ Row::Elements Row::shortenIfNeeded(int const max_width, int 
const next_width)
         * shorten the row. Let's try to break it again, but force
         * splitting this time.
         */
-       if (cit->splitAt(max_width - wid, next_width, true, tail)) {
+       if (cit->splitAt(max_width - wid, next_width, FORCE, tail)) {
                end_ = cit->endpos;
                dim_.wid = wid + cit->dim.wid;
                // If there are other elements, they should be removed.
diff --git a/src/Row.h b/src/Row.h
index 795e3e4..babe115 100644
--- a/src/Row.h
+++ b/src/Row.h
@@ -55,6 +55,14 @@ public:
                // by the initial width
                MARGINSPACE
        };
+       enum SplitType {
+               // split string to fit requested width, fail if string remains 
too long
+               FIT,
+               // if the requested width is too small, accept the first 
possible break
+               BEST_EFFORT,
+               // cut string at any place, even for languages that wrap at 
word delimiters
+               FORCE
+       };
 
 /**
  * One element of a Row. It has a set of attributes that can be used
@@ -94,14 +102,12 @@ public:
                 * not needed or not possible.
                 * \param width: maximum width of the row.
                 * \param next_width: available width on next rows.
-                * \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.
+                * \param split_type: indicate how the string should be split.
                 * \param tail: a vector of elements where the remainder of
                 *   the text will be appended (empty if nothing happened).
                 */
                // FIXME: ideally last parameter should be Elements&, but it is 
not possible.
-               bool splitAt(int width, int next_width, bool force, 
std::vector<Element> & tail);
+               bool splitAt(int width, int next_width, SplitType split_type, 
std::vector<Element> & tail);
                // remove trailing spaces (useful for end of row)
                void rtrim();
 
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index 51f73fd..eb99de0 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -1150,7 +1150,7 @@ RowList TextMetrics::breakParagraph(Row const & bigrow) 
const
                // pile, or the place when we were in main row
                Row::Element elt = *fcit;
                Row::Elements tail;
-               elt.splitAt(width - rows.back().width(), next_width, false, 
tail);
+               elt.splitAt(width - rows.back().width(), next_width, Row::FIT, 
tail);
                Row & rb = rows.back();
                if (elt.type == Row::MARGINSPACE)
                        elt.dim.wid = max(elt.dim.wid, leftMargin(bigrow.pit()) 
- rb.width());
-- 
lyx-cvs mailing list
lyx-cvs@lists.lyx.org
http://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to