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);