commit b68f391232887a7879d8e736f14dd7692f56c365
Author: Jean-Marc Lasgouttes <[email protected]>
Date:   Sun Jul 19 01:22:10 2015 +0200

    Re-implement text justification
    
    * GuiFontMetrics::pos2x, x2pos: add support for inter-word spacing.
    * GuiPainter::text: idem
    
    * Row::Element::countSeparators:
      Row::countSeparators: new methods that count spaces in strings.
      Row::setSeparatorExtraWidth: new method (code lifted from 
TextMetrics.cpp).
    
    * TextMetrics::computeRowMetrics: rely on the above methods.
    
    * RowPainter::paintMispelledMarked: pass only a Row::Element object 
reference
      RowPainter::paintStringAndSel: idem; do not rely on values returned by
          Painter::text (trailing spaces do not honor wordspacing value).

diff --git a/src/Row.cpp b/src/Row.cpp
index 5118f17..ec01d5c 100644
--- a/src/Row.cpp
+++ b/src/Row.cpp
@@ -36,6 +36,15 @@ namespace lyx {
 using support::rtrim;
 using frontend::FontMetrics;
 
+
+int Row::Element::countSeparators() const
+{
+       if (type != STRING)
+               return 0;
+       return count(str.begin(), str.end(), ' ');
+}
+
+
 double Row::Element::pos2x(pos_type const i) const
 {
        // This can happen with inline completion when clicking on the
@@ -54,7 +63,7 @@ double Row::Element::pos2x(pos_type const i) const
                w = rtl ? full_width() : 0;
        else {
                FontMetrics const & fm = theFontMetrics(font);
-               w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft());
+               w = fm.pos2x(str, i - pos, font.isVisibleRightToLeft(), extra);
        }
 
        return w;
@@ -70,7 +79,7 @@ pos_type Row::Element::x2pos(int &x) const
        switch (type) {
        case STRING: {
                FontMetrics const & fm = theFontMetrics(font);
-               i = fm.x2pos(str, x, rtl);
+               i = fm.x2pos(str, x, rtl, extra);
                break;
        }
        case VIRTUAL:
@@ -257,6 +266,26 @@ ostream & operator<<(ostream & os, Row const & row)
 }
 
 
+int Row::countSeparators() const
+{
+       int n = 0;
+       const_iterator const end = elements_.end();
+       for (const_iterator cit = elements_.begin() ; cit != end ; ++cit)
+               n += cit->countSeparators();
+       return n;
+}
+
+
+void Row::setSeparatorExtraWidth(double w)
+{
+       separator = w;
+       iterator const end = elements_.end();
+       for (iterator it = elements_.begin() ; it != end ; ++it)
+               if (it->type == Row::STRING)
+                       it->extra = w;
+}
+
+
 bool Row::sameString(Font const & f, Change const & ch) const
 {
        if (elements_.empty())
diff --git a/src/Row.h b/src/Row.h
index 490aa6b..77dcbb5 100644
--- a/src/Row.h
+++ b/src/Row.h
@@ -76,7 +76,10 @@ public:
                          extra(0), font(f), change(ch), final(false) {}
 
                // Return total width of element, including separator overhead
-               double full_width() const { return dim.wid + extra; };
+               double full_width() const { return dim.wid + extra * 
countSeparators(); };
+               // Return the number of separator in the element (only STRING 
type)
+               int countSeparators() const;
+
                /** Return position in pixels (from the left) of position
                 * \param i in the row element.
                 */
@@ -174,6 +177,11 @@ public:
        ///
        int descent() const { return dim_.des; }
 
+       // Return the number of separators in the row
+       int countSeparators() const;
+       // Set the extra spacing for every separator in STRING elements
+       void setSeparatorExtraWidth(double w);
+
        ///
        void add(pos_type pos, Inset const * ins, Dimension const & dim,
                 Font const & f, Change const & ch);
diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp
index 1c74107..b8f9189 100644
--- a/src/RowPainter.cpp
+++ b/src/RowPainter.cpp
@@ -186,14 +186,12 @@ void RowPainter::paintForeignMark(double orig_x, Language 
const * lang, int desc
 
 
 void RowPainter::paintMisspelledMark(double const orig_x,
-                                     docstring const & str, Font const & font,
-                                     pos_type const start_pos,
-                                     bool const changed) const
+                                     Row::Element const & e) const
 {
        // if changed the misspelled marker gets placed slightly lower than 
normal
        // to avoid drawing at the same vertical offset
        int const y = yo_ + solid_line_offset_ + solid_line_thickness_
-               + (changed ? solid_line_thickness_ + 1 : 0)
+               + (e.change.changed() ? solid_line_thickness_ + 1 : 0)
                + dotted_line_offset_;
 
        //FIXME: this could be computed only once, it is probably not costly.
@@ -210,8 +208,8 @@ void RowPainter::paintMisspelledMark(double const orig_x,
                        --cpos;
        }
 
-       pos_type pos = start_pos;
-       while (pos < start_pos + pos_type(str.length())) {
+       pos_type pos = e.pos;
+       while (pos < e.pos + pos_type(e.str.length())) {
                if (!par_.isMisspelled(pos)) {
                        ++pos;
                        continue;
@@ -226,12 +224,12 @@ void RowPainter::paintMisspelledMark(double const orig_x,
                        continue;
                }
 
-               FontMetrics const & fm = theFontMetrics(font);
-               int x1 = fm.pos2x(str, range.first - start_pos,
-                                 font.isVisibleRightToLeft());
-               int x2 = fm.pos2x(str, min(range.last - start_pos + 1,
-                                          pos_type(str.length())),
-                                 font.isVisibleRightToLeft());
+               FontMetrics const & fm = theFontMetrics(e.font);
+               int x1 = fm.pos2x(e.str, range.first - e.pos,
+                                 e.font.isVisibleRightToLeft(), e.extra);
+               int x2 = fm.pos2x(e.str, min(range.last - e.pos + 1,
+                                                                        
pos_type(e.str.length())),
+                                                                        
e.font.isVisibleRightToLeft(), e.extra);
                if (x1 > x2)
                        swap(x1, x2);
 
@@ -243,32 +241,31 @@ void RowPainter::paintMisspelledMark(double const orig_x,
 }
 
 
-void RowPainter::paintStringAndSel(docstring const & str, Font const & font,
-                                 Change const & change,
-                                 pos_type start_pos, pos_type end_pos)
+void RowPainter::paintStringAndSel(Row::Element const & e)
 {
        // at least part of text selected?
-       bool const some_sel = (end_pos >= row_.sel_beg && start_pos < 
row_.sel_end)
+       bool const some_sel = (e.endpos >= row_.sel_beg && e.pos < row_.sel_end)
                || pi_.selected;
        // all the text selected?
-       bool const all_sel = (start_pos >= row_.sel_beg && end_pos < 
row_.sel_end)
+       bool const all_sel = (e.pos >= row_.sel_beg && e.endpos < row_.sel_end)
                || pi_.selected;
 
        if (all_sel) {
-               Font copy = font;
+               Font copy = e.font;
                copy.fontInfo().setPaintColor(Color_selectiontext);
-               x_ += pi_.pain.text(int(x_), yo_, str, copy);
-       } else if (change.changed()) {
-               Font copy = font;
-               copy.fontInfo().setPaintColor(change.color());
-               x_ += pi_.pain.text(int(x_), yo_, str, copy);
+               pi_.pain.text(int(x_), yo_, e.str, copy, e.extra);
+       } else if (e.change.changed()) {
+               Font copy = e.font;
+               copy.fontInfo().setPaintColor(e.change.color());
+               pi_.pain.text(int(x_), yo_, e.str, copy, e.extra);
        } else if (!some_sel) {
-               x_ += pi_.pain.text(int(x_), yo_, str, font);
+               pi_.pain.text(int(x_), yo_, e.str, e.font, e.extra);
        } else {
-               x_ += pi_.pain.text(int(x_), yo_, str, font, 
Color_selectiontext,
-                                   max(row_.sel_beg, start_pos) - start_pos,
-                                   min(row_.sel_end, end_pos) - start_pos);
+               pi_.pain.text(int(x_), yo_, e.str, e.font, Color_selectiontext,
+                                   max(row_.sel_beg, e.pos) - e.pos,
+                        min(row_.sel_end, e.endpos) - e.pos, e.extra);
        }
+       x_ += e.full_width();
 }
 
 
@@ -639,12 +636,12 @@ void RowPainter::paintText()
                switch (e.type) {
                case Row::STRING:
                case Row::VIRTUAL:
-                       paintStringAndSel(e.str, e.font, e.change, e.pos, 
e.endpos);
+                       paintStringAndSel(e);
 
                        // Paint the spelling mark if needed.
                        if (lyxrc.spellcheck_continuously && pi_.do_spellcheck
                                && par_.isMisspelled(e.pos)) {
-                               paintMisspelledMark(orig_x, e.str, e.font, 
e.pos, e.change.changed());
+                               paintMisspelledMark(orig_x, e);
                        }
                        break;
                case Row::INSET: {
diff --git a/src/RowPainter.h b/src/RowPainter.h
index f559eeb..aa48f46 100644
--- a/src/RowPainter.h
+++ b/src/RowPainter.h
@@ -15,6 +15,7 @@
 #define ROWPAINTER_H
 
 #include "Changes.h"
+#include "Row.h"
 
 #include "support/types.h"
 
@@ -29,7 +30,6 @@ class PainterInfo;
 class Paragraph;
 class ParagraphList;
 class ParagraphMetrics;
-class Row;
 class Text;
 class TextMetrics;
 
@@ -60,12 +60,8 @@ public:
 private:
        void paintSeparator(double width, Font const & font);
        void paintForeignMark(double orig_x, Language const * lang, int desc = 
0) const;
-       void paintStringAndSel(docstring const & str, Font const & font,
-                         Change const & change,
-                         pos_type start_pos, pos_type end_pos);
-       void paintMisspelledMark(double orig_x,
-                                docstring const & str, Font const & font,
-                                pos_type pos, bool changed) const;
+       void paintStringAndSel(Row::Element const & e);
+       void paintMisspelledMark(double orig_x, Row::Element const & e) const;
        void paintChange(double orig_x , Font const & font, Change const & 
change) const;
        int paintAppendixStart(int y) const;
        void paintInset(Inset const * inset, Font const & font,
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index c3017d8..b394169 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -58,28 +58,6 @@ using frontend::FontMetrics;
 
 namespace {
 
-int numberOfSeparators(Row const & row)
-{
-       int n = 0;
-       Row::const_iterator cit = row.begin();
-       Row::const_iterator const end = row.end();
-       for ( ; cit != end ; ++cit)
-               if (cit->type == Row::SEPARATOR)
-                       ++n;
-       return n;
-}
-
-
-void setSeparatorWidth(Row & row, double w)
-{
-       row.separator = w;
-       Row::iterator it = row.begin();
-       Row::iterator const end = row.end();
-       for ( ; it != end ; ++it)
-               if (it->type == Row::SEPARATOR)
-                       it->extra = w;
-}
-
 
 int numberOfLabelHfills(Paragraph const & par, Row const & row)
 {
@@ -603,13 +581,13 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                // set x how you need it
                switch (getAlign(par, row.pos())) {
                case LYX_ALIGN_BLOCK: {
-                       int const ns = numberOfSeparators(row);
+                       int const ns = row.countSeparators();
                        /** If we have separators, and this row has
                         * not be broken abruptly by a display inset
                         * or newline, then stretch it */
                        if (ns && !row.right_boundary()
                            && row.endpos() != par.size()) {
-                               setSeparatorWidth(row, double(w) / ns);
+                               row.setSeparatorExtraWidth(double(w) / ns);
                                row.dimension().wid = width;
                        } else if (text_->isRTL(par)) {
                                row.dimension().wid = width;
diff --git a/src/frontends/FontMetrics.h b/src/frontends/FontMetrics.h
index 84000d1..1c1a658 100644
--- a/src/frontends/FontMetrics.h
+++ b/src/frontends/FontMetrics.h
@@ -84,15 +84,19 @@ public:
         * return the x offset of a position in the string. The
         * direction of the string is forced, and the returned value
         * is from the left edge of the word, not from the start of the string.
+        * \param rtl is true for right-to-left layout
+        * \param ws is the amount of extra inter-word space applied text 
justication.
         */
-       virtual int pos2x(docstring const & s, int pos, bool rtl) const = 0;
+       virtual int pos2x(docstring const & s, int pos, bool rtl, double ws) 
const = 0;
        /**
         * return the position in the string for a given x offset. The
         * direction of the string is forced, and the returned value
         * is from the left edge of the word, not from the start of the string.
         * the offset x is updated to match the closest position in the string.
+        * \param rtl is true for right-to-left layout
+        * \param ws is the amount of extra inter-word space applied text 
justication.
         */
-       virtual int x2pos(docstring const & s, int & x, bool rtl) const = 0;
+       virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) 
const = 0;
        /**
         * Break string at width at most x.
         * \return true if successful
diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h
index 0a499f5..d077a1f 100644
--- a/src/frontends/Painter.h
+++ b/src/frontends/Painter.h
@@ -120,13 +120,15 @@ public:
         * text direction is given by \c rtl.
         * \return the width of the drawn text.
         */
-       virtual int text(int x, int y, docstring const & str, FontInfo const & 
f, bool rtl = false) = 0;
+       virtual int text(int x, int y, docstring const & str, FontInfo const & 
f,
+                     bool rtl = false, double wordspacing = 0.0) = 0;
 
        /** draw a string at position x, y (y is the baseline). The
         * text direction is enforced by the \c Font.
         * \return the width of the drawn text.
         */
-       virtual int text(int x, int y, docstring const & str, Font const & f) = 
0;
+       virtual int text(int x, int y, docstring const & str, Font const & f,
+                     double wordspacing = 0.0) = 0;
 
        /** draw a string at position x, y (y is the baseline), but
         * make sure that the part between \c from and \c to is in
@@ -134,7 +136,8 @@ public:
         * \return the width of the drawn text.
         */
        virtual int text(int x, int y, docstring const & str, Font const & f,
-                        Color other, size_type from, size_type to) = 0;
+                     Color other, size_type from, size_type to,
+                     double const wordspacing) = 0;
 
        void setDrawingEnabled(bool drawing_enabled)
        { drawing_enabled_ = drawing_enabled; }
diff --git a/src/frontends/qt4/GuiFontMetrics.cpp 
b/src/frontends/qt4/GuiFontMetrics.cpp
index 804813a..e41ef3c 100644
--- a/src/frontends/qt4/GuiFontMetrics.cpp
+++ b/src/frontends/qt4/GuiFontMetrics.cpp
@@ -150,10 +150,11 @@ int GuiFontMetrics::signedWidth(docstring const & s) const
 }
 
 namespace {
-void setTextLayout(QTextLayout & tl, docstring const & s, QFont const & font,
-                            bool const rtl)
+void setTextLayout(QTextLayout & tl, docstring const & s, QFont font,
+                   bool const rtl, double const wordspacing)
 {
        tl.setText(toqstr(s));
+       font.setWordSpacing(wordspacing);
        tl.setFont(font);
        // Note that both setFlags and the enums are undocumented
        tl.setFlags(rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
@@ -164,18 +165,22 @@ void setTextLayout(QTextLayout & tl, docstring const & s, 
QFont const & font,
 }
 
 
-int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl) 
const
+int GuiFontMetrics::pos2x(docstring const & s, int const pos, bool const rtl,
+                          double const wordspacing) const
 {
        QTextLayout tl;
-       setTextLayout(tl, s, font_, rtl);
+       QFont copy = font_;
+       copy.setWordSpacing(wordspacing);
+       setTextLayout(tl, s, font_, rtl, wordspacing);
        return static_cast<int>(tl.lineForTextPosition(pos).cursorToX(pos));
 }
 
 
-int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl) const
+int GuiFontMetrics::x2pos(docstring const & s, int & x, bool const rtl,
+                          double const wordspacing) const
 {
        QTextLayout tl;
-       setTextLayout(tl, s, font_, rtl);
+       setTextLayout(tl, s, font_, rtl, wordspacing);
        int pos = tl.lineForTextPosition(0).xToCursor(x);
        // correct x value to the actual cursor position.
        x = static_cast<int>(tl.lineForTextPosition(0).cursorToX(pos));
diff --git a/src/frontends/qt4/GuiFontMetrics.h 
b/src/frontends/qt4/GuiFontMetrics.h
index bc6b24a..a382253 100644
--- a/src/frontends/qt4/GuiFontMetrics.h
+++ b/src/frontends/qt4/GuiFontMetrics.h
@@ -43,8 +43,8 @@ public:
        virtual int rbearing(char_type c) const;
        virtual int width(docstring const & s) const;
        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 int pos2x(docstring const & s, int pos, bool rtl, double ws) 
const;
+       virtual int x2pos(docstring const & s, int & x, bool rtl, double ws) 
const;
        virtual bool breakAt(docstring & s, int & x, bool rtl, bool force) 
const;
        virtual Dimension const dimension(char_type c) const;
 
diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp
index b0a78c9..8e0229e 100644
--- a/src/frontends/qt4/GuiPainter.cpp
+++ b/src/frontends/qt4/GuiPainter.cpp
@@ -290,7 +290,8 @@ int GuiPainter::text(int x, int y, char_type c, FontInfo 
const & f)
 
 
 int GuiPainter::text(int x, int y, docstring const & s,
-                    FontInfo const & f, bool const rtl)
+                     FontInfo const & f, bool const rtl,
+                     double const wordspacing)
 {
        //LYXERR0("text: x=" << x << ", s=" << s);
        if (s.empty())
@@ -316,7 +317,8 @@ int GuiPainter::text(int x, int y, docstring const & s,
                str = ' ' + str;
 #endif
 
-       QFont const & ff = getFont(f);
+       QFont ff = getFont(f);
+       ff.setWordSpacing(wordspacing);
        GuiFontMetrics const & fm = getFontMetrics(f);
 
        // Here we use the font width cache instead of
@@ -418,14 +420,16 @@ int GuiPainter::text(int x, int y, docstring const & s,
 }
 
 
-int GuiPainter::text(int x, int y, docstring const & str, Font const & f)
+int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
+                     double const wordspacing)
 {
-       return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft());
+       return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft(), 
wordspacing);
 }
 
 
 int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
-                    Color other, size_type from, size_type to)
+                     Color other, size_type const from, size_type const to,
+                     double const wordspacing)
 {
        GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
        FontInfo fi = f.fontInfo();
@@ -434,8 +438,8 @@ int GuiPainter::text(int x, int y, docstring const & str, 
Font const & f,
        // dimensions
        int const ascent = fm.maxAscent();
        int const height = fm.maxAscent() + fm.maxDescent();
-       int xmin = fm.pos2x(str, from, rtl);
-       int xmax = fm.pos2x(str, to, rtl);
+       int xmin = fm.pos2x(str, from, rtl, wordspacing);
+       int xmax = fm.pos2x(str, to, rtl, wordspacing);
        if (xmin > xmax)
                swap(xmin, xmax);
 
@@ -444,7 +448,7 @@ int GuiPainter::text(int x, int y, docstring const & str, 
Font const & f,
        fi.setPaintColor(other);
        QRegion const clip(x + xmin, y - ascent, xmax - xmin, height);
        setClipRegion(clip);
-       int const textwidth = text(x, y, str, fi, rtl);
+       int const textwidth = text(x, y, str, fi, rtl, wordspacing);
 
        // Then the part in normal color
        // Note that in Qt5, it is not possible to use Qt::UniteClip,
@@ -452,7 +456,7 @@ int GuiPainter::text(int x, int y, docstring const & str, 
Font const & f,
        fi.setPaintColor(orig);
        QRegion region(viewport());
        setClipRegion(region - clip);
-       text(x, y, str, fi, rtl);
+       text(x, y, str, fi, rtl, wordspacing);
        setClipping(false);
 
        return textwidth;
diff --git a/src/frontends/qt4/GuiPainter.h b/src/frontends/qt4/GuiPainter.h
index 5afe5a5..5538d14 100644
--- a/src/frontends/qt4/GuiPainter.h
+++ b/src/frontends/qt4/GuiPainter.h
@@ -91,13 +91,15 @@ public:
         * text direction is given by \c rtl.
         * \return the width of the drawn text.
         */
-       virtual int text(int x, int y, docstring const & str, FontInfo const & 
f, bool rtl = false);
+       virtual int text(int x, int y, docstring const & str, FontInfo const & 
f,
+                     bool rtl = false, double wordspacing = 0.0);
 
        /** draw a string at position x, y (y is the baseline). The
         * text direction is enforced by the \c Font.
         * \return the width of the drawn text.
         */
-       virtual int text(int x, int y, docstring const & str, Font const & f);
+       virtual int text(int x, int y, docstring const & str, Font const & f,
+                     double wordspacing = 0.0);
 
        /** draw a string at position x, y (y is the baseline), but
         * make sure that the part between \c from and \c to is in
@@ -105,7 +107,8 @@ public:
         * \return the width of the drawn text.
         */
        virtual int text(int x, int y, docstring const & str, Font const & f,
-                        Color other, size_type from, size_type to);
+                     Color other, size_type from, size_type to,
+                     double const wordspacing);
 
        /// draw a char at position x, y (y is the baseline)
        virtual int text(int x, int y, char_type c, FontInfo const & f);

Reply via email to