The display of partially-selected word is now done in a new Painter::text method
which displays the string twice with different clip settings. This allows to
catter for the case where Color_selectiontext is not black.
Morover, the code that uses unicode override characters to force the
direction of a string is moved to lstrings.h.
Fixes: #9116
justified. This speeds-up painting by reducing the number of strings
to draw.
+* Do not cut strings at selection boundary in RowPainter. This avoids
+ ligature/kerning breaking in latin text, and bad rendering problems
+ in Arabic.
+
Next steps:
happens. This depends on the update machinery.
This would allow to get rid of the Bidi.cpp code.
-
-
#define PAINTER_H
#include "support/strfwd.h"
+#include "support/types.h"
namespace lyx {
+class Font;
class FontInfo;
namespace graphics { class Image; }
virtual void image(int x, int y, int w, int h,
graphics::Image const & image) = 0;
- /// draw a string at position x, y (y is the baseline)
- /**
- * \return the width of the drawn text.
- */
+ /** draw a string at position x, y (y is the baseline). The
+ * text direction is deduced from \c str.
+ * \return the width of the drawn text.
+ */
virtual int text(int x, int y, docstring const & str, FontInfo const & f) = 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;
+
+ /** 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
+ * \c other color. 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,
+ Color other, size_type from, size_type to) = 0;
+
void setDrawingEnabled(bool drawing_enabled)
{ drawing_enabled_ = drawing_enabled; }
#include "insets/Inset.h"
#include "support/lassert.h"
+#include "support/lstrings.h"
#include <QTextLayout>
using namespace std;
+using namespace lyx::support;
namespace lyx {
namespace frontend {
* why this works well for symbol fonts used in mathed too, even though
* these are not real ucs4 characters. These are codepoints in the
* computer modern fonts used, nothing unicode related.
- * See comment in QLPainter::text() for more explanation.
+ * See comment in GuiPainter::text() for more explanation.
**/
inline QChar const ucs4_to_qchar(char_type const ucs4)
{
void setTextLayout(QTextLayout & tl, docstring const & s, QFont const & font,
bool const rtl)
{
- QString qs;
- /* In LyX, the character direction is forced by the language.
- * Therefore, we have to signal that fact to Qt.
- * Source: http://www.iamcal.com/understanding-bidirectional-text/
- */
- // Left-to-right override: forces to draw text left-to-right
- char_type const LRO = 0x202D;
- // Right-to-left override: forces to draw text right-to-left
- char_type const RLO = 0x202E;
- // Pop directional formatting: return to previous state
- char_type const PDF = 0x202C;
- if (rtl)
- qs = toqstr(RLO + s + PDF);
- else
- qs = toqstr(LRO + s + PDF);
-
+ QString const qs = toqstr(directedString(s, rtl));
tl.setText(qs);
tl.setFont(font);
tl.beginLayout();
{
QTextLayout tl;
setTextLayout(tl, s, font_, rtl);
+ // we take into account the unicode formatting characters
return tl.lineForTextPosition(pos + 1).cursorToX(pos + 1);
}
#include "GuiImage.h"
#include "qt_helpers.h"
-#include "FontInfo.h"
+#include "Font.h"
#include "Language.h"
#include "LyXRC.h"
#include "insets/Inset.h"
#include "support/lassert.h"
+#include "support/lstrings.h"
#include "support/debug.h"
#include <QPixmapCache>
#endif
using namespace std;
+using namespace lyx::support;
namespace lyx {
namespace frontend {
QFont const & ff = getFont(f);
GuiFontMetrics const & fm = getFontMetrics(f);
- int textwidth;
-
// Here we use the font width cache instead of
// textwidth = fontMetrics().width(str);
// because the above is awfully expensive on MacOSX
- textwidth = fm.width(s);
- textDecoration(f, x, y, textwidth);
+ int const textwidth = fm.width(s);
if (!isDrawingEnabled())
return textwidth;
+ textDecoration(f, x, y, textwidth);
+
// Qt4 does not display a glyph whose codepoint is the
// same as that of a soft-hyphen (0x00ad), unless it
// occurs at a line-break. As a kludge, we force Qt to
setQPainterPen(computeColor(f.realColor()));
if (font() != ff)
setFont(ff);
- // We need to draw the text as LTR as we use our own bidi code.
- QPainter::setLayoutDirection(Qt::LeftToRight);
drawText(x, y, str);
//LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
// << " at " << x << "," << y);
}
+int GuiPainter::text(int x, int y, docstring const & str, Font const & f)
+{
+ docstring const dstr = directedString(str, f.isVisibleRightToLeft());
+ return text(x, y, dstr, f.fontInfo());
+}
+
+
+int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
+ Color other, size_type from, size_type to)
+{
+ GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
+ FontInfo fi = f.fontInfo();
+ bool const rtl = f.isVisibleRightToLeft();
+ docstring const dstr = directedString(str, rtl);
+
+ // 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);
+ if (xmin > xmax)
+ swap(xmin, xmax);
+
+ // First the part in other color
+ Color const orig = fi.realColor();
+ fi.setPaintColor(other);
+ setClipRect(QRect(x + xmin, y - ascent, xmax - xmin, height));
+ int const textwidth = text(x, y, dstr, fi);
+
+ // Then the part in normal color
+ // Note that in Qt5, it is not possible to use Qt::UniteClip
+ fi.setPaintColor(orig);
+ setClipRect(QRect(x, y - ascent, xmin, height));
+ text(x, y, dstr, fi);
+ setClipRect(QRect(x + xmax, y - ascent, textwidth - xmax, height));
+ text(x, y, dstr, fi);
+ setClipping(false);
+
+ return textwidth;
+}
+
+
void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
{
if (f.underbar() == FONT_ON)
virtual void image(int x, int y, int w, int h,
lyx::graphics::Image const & image);
- /// draw a string at position x, y (y is the baseline)
- virtual int text(int x, int y,
- docstring const & str, FontInfo const & f);
+ /** draw a string at position x, y (y is the baseline). The
+ * text direction is deduced from \c str.
+ * \return the width of the drawn text.
+ */
+ virtual int text(int x, int y, docstring const & str, FontInfo const & f);
+
+ /** 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);
+
+ /** 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
+ * \c other color. 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,
+ Color other, size_type from, size_type to);
/// draw a char at position x, y (y is the baseline)
virtual int text(int x, int y, char_type c, FontInfo const & f);
{
// This method takes up 70% of time when typing
pos_type pos = bidi_.vis2log(vpos);
+ pos_type start_pos = pos;
// first character
char_type c = par_.getChar(pos);
docstring str;
// Track-change status.
Change const & change_running = par_.lookupChange(pos);
- // selected text?
- bool const selection = (pos >= row_.sel_beg && pos < row_.sel_end)
- || pi_.selected;
-
// spelling correct?
bool const spell_state =
lyxrc.spellcheck_continuously && par_.isMisspelled(pos);
// collect as much similar chars as we can
for (++vpos ; vpos < end ; ++vpos) {
pos = bidi_.vis2log(vpos);
- if (pos < font_span.first || pos > font_span.last)
- break;
- bool const new_selection = pos >= row_.sel_beg && pos < row_.sel_end;
- if (new_selection != selection)
- // Selection ends or starts here.
+ if (!font_span.inside(pos))
break;
bool const new_spell_state =
if (c == '\t')
break;
+ // When row_.separator == 0, it is possible to print a
+ // string longer than a word in one fell swoop.
+ // Therefore there is no need to break at spaces.
if (!isPrintableNonspace(c)
&& (c != ' ' || row_.separator > 0))
break;
str.push_back(c);
}
+ // Make pos point to the last character in the string.
+ // Using "pos = bidi_.vis2log(vpos)" does not work for some reason.
+ if (vpos < end)
+ pos = bidi_.vis2log(vpos - 1);
+
+ // Now make pos point to the position _after_ the string.
+ // Using vis2log for that is not a good idea in general, we
+ // want logical ordering.
+ if (font.isVisibleRightToLeft())
+ --pos;
+ else
+ ++pos;
+
if (str[0] == '\t')
str.replace(0,1,from_ascii(" "));
* See also http://thread.gmane.org/gmane.editors.lyx.devel/79740
* for an earlier thread on the subject
*/
- // Left-to-right override: forces to draw text left-to-right
- char_type const LRO = 0x202D;
- // Right-to-left override: forces to draw text right-to-left
- char_type const RLO = 0x202E;
- // Pop directional formatting: return to previous state
- char_type const PDF = 0x202C;
if (font.isVisibleRightToLeft()) {
reverse(str.begin(), str.end());
- str = RLO + str + PDF;
- } else
- str = LRO + str + PDF;
+ // If the string is reversed, the positions need to be adjusted
+ ++pos;
+ ++start_pos;
+ swap(start_pos, pos);
+ }
+
+ // at least part of text selected?
+ bool const some_sel = (pos >= row_.sel_beg && start_pos < row_.sel_end)
+ || pi_.selected;
+ // all the text selected?
+ bool const all_sel = (start_pos >= row_.sel_beg && pos < row_.sel_end)
+ || pi_.selected;
- if (!selection && !change_running.changed()) {
- x_ += pi_.pain.text(int(x_), yo_, str, font.fontInfo());
- return;
+ if (all_sel) {
+ Font copy = font;
+ copy.fontInfo().setPaintColor(Color_selectiontext);
+ x_ += pi_.pain.text(int(x_), yo_, str, copy);
+ } else if (change_running.changed()) {
+ Font copy = font;
+ copy.fontInfo().setPaintColor(change_running.color());
+ x_ += pi_.pain.text(int(x_), yo_, str, copy);
+ } else if (!some_sel) {
+ x_ += pi_.pain.text(int(x_), yo_, str, font);
+ } else {
+ x_ += pi_.pain.text(int(x_), yo_, str, font, Color_selectiontext,
+ max(row_.sel_beg, start_pos) - start_pos,
+ min(row_.sel_end, pos) - start_pos);
}
-
- FontInfo copy = font.fontInfo();
- if (change_running.changed())
- copy.setPaintColor(change_running.color());
- else if (selection)
- copy.setPaintColor(Color_selectiontext);
-
- x_ += pi_.pain.text(int(x_), yo_, str, copy);
}
return subst(str, from_ascii("%%"), from_ascii("%"));
}
+
+docstring directedString(docstring const & s, bool const rtl)
+{
+ /* In LyX, the character direction is forced by the language.
+ * Therefore, we have to signal that fact to Qt.
+ * Source: http://www.iamcal.com/understanding-bidirectional-text/
+ */
+ // Left-to-right override: forces to draw text left-to-right
+ char_type const LRO = 0x202D;
+ // Right-to-left override: forces to draw text right-to-left
+ char_type const RLO = 0x202E;
+ // Pop directional formatting: return to previous state
+ char_type const PDF = 0x202C;
+ return (rtl ? RLO : LRO) + s + PDF;
+}
+
+
+
} // namespace support
} // namespace lyx
template<> docstring bformat(docstring const & fmt, docstring arg1, docstring arg2, docstring arg3);
template<> docstring bformat(docstring const & fmt, docstring arg1, docstring arg2, docstring arg3, docstring arg4);
+/// Return a string with Unicode overrides to enforce the writing direction
+docstring directedString(docstring const & s, bool rtl);
+
} // namespace support
} // namespace lyx