On Tue, Jul 21, 2015 at 6:01 PM, Jean-Marc Lasgouttes
<lasgout...@lyx.org> wrote:
> commit f65f3adbf73283a288f5d186b51b6b98c9ecb51d
> Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
> Date:   Sun Jul 19 01:22:10 2015 +0200
>
>     Do not break row elements at spaces
>
>     The goal of this commit is to make painting faster by reducing the
>     number of strings to paint. To this end, it is necessary to include
>     spaces in row elements.
>
>     Also importantly, this commit should fix existing problems with line
>     breaking in chinese text.
>
>     * TextMetrics::breakRow: do not do anything special for word separators.
>
>     * Row::add: when adding a character to a row element, keep the string
>       width updated. If need be, it is possible to tweak this by updating
>       every 10 characters, for example.
>
>     * GuiFontMetrics::breakAt (new): use QTextLayout to break text either
>       at word boundary or at an arbitrary width.
>
>     * Row::Element::breakAt: use the above method.
>
>     * Row::shortenIfNeeded: simplify now that because there is no need for
>       handling separator elements. This will be taken care of by the
>       improved breakAt.
>
>     Two things remain to be done:
>
>     * remove all traces of separator row element
>
>     * re-implement text justification.

This commit seems to have caused a regression in continuous spellcheck
for me. Continuous spellcheck still works in some cases but not in
all. Attached is a case where it does not work. Two words are
misspelled: "tzhat" and "pointwise-consistent". Note that Tools >
Spellchecker still works as expected.

Scott

On Tue, Jul 21, 2015 at 6:01 PM, Jean-Marc Lasgouttes
<lasgout...@lyx.org> wrote:
> commit f65f3adbf73283a288f5d186b51b6b98c9ecb51d
> Author: Jean-Marc Lasgouttes <lasgout...@lyx.org>
> Date:   Sun Jul 19 01:22:10 2015 +0200
>
>     Do not break row elements at spaces
>
>     The goal of this commit is to make painting faster by reducing the
>     number of strings to paint. To this end, it is necessary to include
>     spaces in row elements.
>
>     Also importantly, this commit should fix existing problems with line
>     breaking in chinese text.
>
>     * TextMetrics::breakRow: do not do anything special for word separators.
>
>     * Row::add: when adding a character to a row element, keep the string
>       width updated. If need be, it is possible to tweak this by updating
>       every 10 characters, for example.
>
>     * GuiFontMetrics::breakAt (new): use QTextLayout to break text either
>       at word boundary or at an arbitrary width.
>
>     * Row::Element::breakAt: use the above method.
>
>     * Row::shortenIfNeeded: simplify now that because there is no need for
>       handling separator elements. This will be taken care of by the
>       improved breakAt.
>
>     Two things remain to be done:
>
>     * remove all traces of separator row element
>
>     * re-implement text justification.
>
> diff --git a/src/Row.cpp b/src/Row.cpp
> index 57f02e2..5118f17 100644
> --- a/src/Row.cpp
> +++ b/src/Row.cpp
> @@ -24,6 +24,7 @@
>
>  #include "support/debug.h"
>  #include "support/lassert.h"
> +#include "support/lstrings.h"
>  #include "support/lyxalgo.h"
>
>  #include <ostream>
> @@ -32,6 +33,7 @@ using namespace std;
>
>  namespace lyx {
>
> +using support::rtrim;
>  using frontend::FontMetrics;
>
>  double Row::Element::pos2x(pos_type const i) const
> @@ -96,24 +98,21 @@ pos_type Row::Element::x2pos(int &x) const
>  }
>
>
> -bool Row::Element::breakAt(int w)
> +bool Row::Element::breakAt(int w, bool force)
>  {
>         if (type != STRING || dim.wid <= w)
>                 return false;
>
>         bool const rtl = font.isVisibleRightToLeft();
> -       if (rtl)
> -               w = dim.wid - w;
> -       pos_type new_pos = x2pos(w);
> -       if (new_pos == pos)
> -               return false;
> -       str = str.substr(0, new_pos - pos);
> -       if (rtl)
> -               dim.wid -= w;
> -       else
> -               dim.wid = w;
> -       endpos = new_pos;
> -       return true;
> +       FontMetrics const & fm = theFontMetrics(font);
> +       int x = w;
> +       if(fm.breakAt(str, x, rtl, force)) {
> +               dim.wid = x;
> +               endpos = pos + str.length();
> +               //lyxerr << "breakAt(" << w << ")  Row element Broken at " << 
> x << "(w(str)=" << fm.width(str) << "): e=" << *this << endl;
> +               return true;
> +       }
> +       return false;
>  }
>
>
> @@ -278,6 +277,7 @@ void Row::finalizeLast()
>         elt.final = true;
>
>         if (elt.type == STRING) {
> +               dim_.wid -= elt.dim.wid;
>                 elt.dim.wid = theFontMetrics(elt.font).width(elt.str);
>                 dim_.wid += elt.dim.wid;
>         }
> @@ -304,8 +304,11 @@ void Row::add(pos_type const pos, char_type const c,
>                 Element e(STRING, pos, f, ch);
>                 elements_.push_back(e);
>         }
> +       dim_.wid -= back().dim.wid;
>         back().str += c;
>         back().endpos = pos + 1;
> +       back().dim.wid = theFontMetrics(back().font).width(back().str);
> +       dim_.wid += back().dim.wid;
>  }
>
>
> @@ -357,33 +360,17 @@ void Row::shortenIfNeeded(pos_type const keep, int 
> const w)
>  {
>         if (empty() || width() <= w)
>                 return;
> -
>         Elements::iterator const beg = elements_.begin();
>         Elements::iterator const end = elements_.end();
> -       Elements::iterator last_sep = elements_.end();
> -       int last_width = 0;
>         int wid = left_margin;
>
>         Elements::iterator cit = beg;
>         for ( ; cit != end ; ++cit) {
> -               if (cit->type == SEPARATOR && cit->pos >= keep) {
> -                       last_sep = cit;
> -                       last_width = wid;
> -               }
> -               if (wid + cit->dim.wid > w)
> +               if (cit->endpos >= keep && wid + cit->dim.wid > w)
>                         break;
>                 wid += cit->dim.wid;
>         }
>
> -       if (last_sep != end) {
> -               // We have found a suitable separator. This is the
> -               // common case.
> -               end_ = last_sep->endpos;
> -               dim_.wid = last_width;
> -               elements_.erase(last_sep, end);
> -               return;
> -       }
> -
>         if (cit == end) {
>                 // This should not happen since the row is too long.
>                 LYXERR0("Something is wrong cannot shorten row: " << *this);
> @@ -397,23 +384,41 @@ void Row::shortenIfNeeded(pos_type const keep, int 
> const w)
>                 wid -= cit->dim.wid;
>         }
>
> +       // Try to break this row cleanly (at word boundary)
> +       if (cit->breakAt(w - wid, false)) {
> +               end_ = cit->endpos;
> +               // after breakAt, there may be spaces at the end of the
> +               // string, but they are not counted in the string length
> +               // (qtextlayout feature, actually). We remove them, but do not
> +               // change the endo of the row, since the spaces at row break
> +               // are invisible.
> +               cit->str = rtrim(cit->str);
> +               cit->endpos = cit->pos + cit->str.length();
> +               dim_.wid = wid + cit->dim.wid;
> +               // If there are other elements, they should be removed.
> +               elements_.erase(next(cit, 1), end);
> +               return;
> +       }
> +
>         if (cit != beg) {
> -               // There is no separator, but several elements (probably
> -               // insets) have been added. We can cut at this place.
> +               // There is no separator, but several elements have been
> +               // added. We can cut right here.
>                 end_ = cit->pos;
>                 dim_.wid = wid;
>                 elements_.erase(cit, end);
>                 return;
>         }
>
> -       /* If we are here, it means that we have not found a separator
> -        * to shorten the row. There is one case where we can do
> -        * something: when we have one big string, maybe with some
> -        * other things after it.
> +       /* 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.
>          */
> -       if (cit->breakAt(w - left_margin)) {
> +       if (cit->breakAt(w - wid, true)) {
>                 end_ = cit->endpos;
> -               dim_.wid = left_margin + cit->dim.wid;
> +               // See comment above.
> +               cit->str = rtrim(cit->str);
> +               cit->endpos = cit->pos + cit->str.length();
> +               dim_.wid = wid + cit->dim.wid;
>                 // If there are other elements, they should be removed.
>                 elements_.erase(next(cit, 1), end);
>         }
> diff --git a/src/Row.h b/src/Row.h
> index 9039ff8..490aa6b 100644
> --- a/src/Row.h
> +++ b/src/Row.h
> @@ -86,10 +86,12 @@ public:
>                  *  adjusted to the actual pixel position.
>                 */
>                 pos_type x2pos(int &x) const;
> -               /** Break the element if possible, so that its width is
> -                * less then \param w. Returns true on success.
> +               /** Break the element if possible, so that its width is less
> +                * than \param w. Returns true on success. When \param force
> +                * is true, the string is cut at any place, other wise it
> +                * respects the row breaking rules of characters.
>                  */
> -               bool breakAt(int w);
> +               bool breakAt(int w, bool force);
>
>                 // Returns the position on left side of the element.
>                 pos_type left_pos() const;
> diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
> index 2f314b5..c3017d8 100644
> --- a/src/TextMetrics.cpp
> +++ b/src/TextMetrics.cpp
> @@ -819,7 +819,7 @@ void TextMetrics::breakRow(Row & row, int const 
> right_margin, pit_type const pit
>         // or the end of the par, then build a representation of the row.
>         pos_type i = pos;
>         FontIterator fi = FontIterator(*this, par, pit, pos);
> -       while (i < end && row.width() < width) {
> +       while (i < end && row.width() <= width) {
>                 char_type c = par.getChar(i);
>                 // The most special cases are handled first.
>                 if (par.isInset(i)) {
> @@ -837,13 +837,6 @@ void TextMetrics::breakRow(Row & row, int const 
> right_margin, pit_type const pit
>                         int const add = max(fm.width(par.layout().labelsep),
>                                             labelEnd(pit) - row.width());
>                         row.addSpace(i, add, *fi, par.lookupChange(i));
> -               } else if (par.isLineSeparator(i)) {
> -                       // In theory, no inset has this property. If
> -                       // this is done, a new addSeparator which
> -                       // takes an inset as parameter should be
> -                       // added.
> -                       LATTEST(!par.isInset(i));
> -                       row.addSeparator(i, c, *fi, par.lookupChange(i));
>                 } else if (c == '\t')
>                         row.addSpace(i, 
> theFontMetrics(*fi).width(from_ascii("    ")),
>                                      *fi, par.lookupChange(i));
> @@ -905,6 +898,7 @@ void TextMetrics::breakRow(Row & row, int const 
> right_margin, pit_type const pit
>
>         // make sure that the RTL elements are in reverse ordering
>         row.reverseRTL(is_rtl);
> +       //LYXERR0("breakrow: row is " << row);
>  }
>
>
> diff --git a/src/frontends/FontMetrics.h b/src/frontends/FontMetrics.h
> index 53f57a2..84000d1 100644
> --- a/src/frontends/FontMetrics.h
> +++ b/src/frontends/FontMetrics.h
> @@ -93,6 +93,14 @@ public:
>          * the offset x is updated to match the closest position in the 
> string.
>          */
>         virtual int x2pos(docstring const & s, int & x, bool rtl) const = 0;
> +       /**
> +        * Break string at width at most x.
> +        * \return true if successful
> +        * \param rtl is true for right-to-left layout
> +        * \param force is false for breaking at word separator, true for
> +        *   arbitrary position.
> +        */
> +       virtual bool breakAt(docstring & s, int & x, 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/qt4/GuiFontMetrics.cpp 
> b/src/frontends/qt4/GuiFontMetrics.cpp
> index b488b68..804813a 100644
> --- a/src/frontends/qt4/GuiFontMetrics.cpp
> +++ b/src/frontends/qt4/GuiFontMetrics.cpp
> @@ -183,6 +183,31 @@ int GuiFontMetrics::x2pos(docstring const & s, int & x, 
> bool const rtl) const
>  }
>
>
> +bool GuiFontMetrics::breakAt(docstring & s, int & x, bool const rtl, bool 
> const force) const
> +{
> +       if (s.empty())
> +               return false;
> +       QTextLayout tl;
> +       tl.setText(toqstr(s));
> +       tl.setFont(font_);
> +       // Note that both setFlags and the enums are undocumented
> +       tl.setFlags(rtl ? Qt::TextForceRightToLeft : 
> Qt::TextForceLeftToRight);
> +       QTextOption to;
> +       to.setWrapMode(force ? QTextOption::WrapAnywhere : 
> QTextOption::WordWrap);
> +       tl.setTextOption(to);
> +       tl.beginLayout();
> +       QTextLine line = tl.createLine();
> +       line.setLineWidth(x);
> +       tl.createLine();
> +       tl.endLayout();
> +       if (int(line.naturalTextWidth()) > x)
> +               return false;
> +       x = int(line.naturalTextWidth());
> +       s = s.substr(0, line.textLength());
> +       return true;
> +}
> +
> +
>  void GuiFontMetrics::rectText(docstring const & str,
>         int & w, int & ascent, int & descent) const
>  {
> diff --git a/src/frontends/qt4/GuiFontMetrics.h 
> b/src/frontends/qt4/GuiFontMetrics.h
> index 7555929..bc6b24a 100644
> --- a/src/frontends/qt4/GuiFontMetrics.h
> +++ b/src/frontends/qt4/GuiFontMetrics.h
> @@ -45,6 +45,7 @@ public:
>         virtual int signedWidth(docstring const & s) const;
>         virtual int pos2x(docstring const & s, int pos, bool rtl) const;
>         virtual int x2pos(docstring const & s, int & x, bool rtl) const;
> +       virtual bool breakAt(docstring & s, int & x, bool rtl, bool force) 
> const;
>         virtual Dimension const dimension(char_type c) const;
>
>         virtual void rectText(docstring const & str,

Attachment: regression-f65f3adb.21.lyx
Description: application/lyx

Reply via email to