mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 21:21:32 +00:00
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).
This commit is contained in:
parent
f65f3adbf7
commit
b68f391232
33
src/Row.cpp
33
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())
|
||||
|
10
src/Row.h
10
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);
|
||||
|
@ -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: {
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user