2006-03-05 17:24:44 +00:00
|
|
|
/**
|
2007-08-31 05:53:55 +00:00
|
|
|
* \file GuiPainter.cpp
|
2006-03-05 17:24:44 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
|
|
|
*
|
|
|
|
* \author John Levon
|
|
|
|
* \author Abdelrazak Younes
|
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2007-08-31 05:53:55 +00:00
|
|
|
#include "GuiPainter.h"
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2008-05-23 08:43:07 +00:00
|
|
|
#include "ColorCache.h"
|
2006-09-26 09:57:47 +00:00
|
|
|
#include "GuiApplication.h"
|
2008-03-20 00:00:53 +00:00
|
|
|
#include "GuiFontLoader.h"
|
2006-10-27 21:27:03 +00:00
|
|
|
#include "GuiFontMetrics.h"
|
2007-08-31 05:53:55 +00:00
|
|
|
#include "GuiImage.h"
|
2006-08-30 14:59:07 +00:00
|
|
|
#include "qt_helpers.h"
|
2006-06-26 16:55:35 +00:00
|
|
|
|
2014-05-23 16:59:53 +00:00
|
|
|
#include "Font.h"
|
2007-11-02 14:43:09 +00:00
|
|
|
#include "LyXRC.h"
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2008-02-18 07:14:42 +00:00
|
|
|
#include "support/debug.h"
|
2017-04-10 08:27:08 +00:00
|
|
|
#include "support/lassert.h"
|
|
|
|
#include "support/lyxlib.h"
|
2008-02-18 07:14:42 +00:00
|
|
|
|
2016-06-09 15:09:14 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2007-08-13 17:35:09 +00:00
|
|
|
#include <QTextLayout>
|
|
|
|
|
2007-12-12 10:16:00 +00:00
|
|
|
using namespace std;
|
2014-05-23 16:59:53 +00:00
|
|
|
using namespace lyx::support;
|
2007-11-07 20:44:36 +00:00
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
namespace lyx {
|
|
|
|
namespace frontend {
|
2015-07-14 15:42:45 +00:00
|
|
|
|
2016-03-19 14:48:44 +00:00
|
|
|
const int Painter::thin_line = 1;
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2014-10-12 17:23:13 +00:00
|
|
|
GuiPainter::GuiPainter(QPaintDevice * device, double pixel_ratio)
|
2018-10-07 12:48:05 +00:00
|
|
|
: QPainter(device), Painter(pixel_ratio)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2017-09-18 09:21:18 +00:00
|
|
|
// set cache correctly
|
|
|
|
current_color_ = pen().color();
|
|
|
|
current_ls_ = pen().style() == Qt::DotLine ? line_onoffdash : line_solid;
|
|
|
|
current_lw_ = pen().width();
|
2006-06-23 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
GuiPainter::~GuiPainter()
|
2006-06-23 14:12:17 +00:00
|
|
|
{
|
2006-10-23 08:46:09 +00:00
|
|
|
QPainter::end();
|
2007-08-31 22:16:11 +00:00
|
|
|
//lyxerr << "GuiPainter::end()" << endl;
|
2006-06-23 14:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-01 10:52:51 +00:00
|
|
|
void GuiPainter::setQPainterPen(QColor const & col,
|
2015-07-14 15:42:45 +00:00
|
|
|
Painter::line_style ls, int lw)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2006-06-14 14:23:25 +00:00
|
|
|
if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
|
|
|
|
return;
|
2006-06-11 21:22:36 +00:00
|
|
|
|
2006-06-14 14:23:25 +00:00
|
|
|
current_color_ = col;
|
2006-06-11 21:22:36 +00:00
|
|
|
current_ls_ = ls;
|
|
|
|
current_lw_ = lw;
|
|
|
|
|
2006-10-23 08:46:09 +00:00
|
|
|
QPen pen = QPainter::pen();
|
2007-11-01 10:52:51 +00:00
|
|
|
pen.setColor(col);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
|
|
|
switch (ls) {
|
2016-05-23 10:38:48 +00:00
|
|
|
case line_solid:
|
|
|
|
case line_solid_aliased:
|
|
|
|
pen.setStyle(Qt::SolidLine); break;
|
|
|
|
case line_onoffdash:
|
|
|
|
pen.setStyle(Qt::DotLine); break;
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2015-07-14 15:42:45 +00:00
|
|
|
pen.setWidth(lw);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2006-10-23 08:46:09 +00:00
|
|
|
setPen(pen);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
2006-06-25 11:18:56 +00:00
|
|
|
|
2009-02-09 20:10:11 +00:00
|
|
|
QColor GuiPainter::computeColor(Color col)
|
2007-11-01 10:52:51 +00:00
|
|
|
{
|
|
|
|
return filterColor(guiApp->colorCache().get(col));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QColor GuiPainter::filterColor(QColor const & col)
|
|
|
|
{
|
|
|
|
if (monochrome_min_.empty())
|
|
|
|
return col;
|
|
|
|
|
|
|
|
// map into [min,max] interval
|
|
|
|
QColor const & min = monochrome_min_.top();
|
|
|
|
QColor const & max = monochrome_max_.top();
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2007-11-01 10:52:51 +00:00
|
|
|
qreal v = col.valueF();
|
2007-11-08 08:00:31 +00:00
|
|
|
v *= v; // make it a bit steeper (i.e. darker)
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2007-11-01 10:52:51 +00:00
|
|
|
qreal minr, ming, minb;
|
|
|
|
qreal maxr, maxg, maxb;
|
|
|
|
min.getRgbF(&minr, &ming, &minb);
|
|
|
|
max.getRgbF(&maxr, &maxg, &maxb);
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2007-11-07 20:44:36 +00:00
|
|
|
QColor c;
|
2007-11-08 08:00:31 +00:00
|
|
|
c.setRgbF(
|
|
|
|
v * (minr - maxr) + maxr,
|
|
|
|
v * (ming - maxg) + maxg,
|
|
|
|
v * (minb - maxb) + maxb);
|
2007-11-07 20:44:36 +00:00
|
|
|
return c;
|
2007-11-01 10:52:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-09 20:10:11 +00:00
|
|
|
void GuiPainter::enterMonochromeMode(Color const & min, Color const & max)
|
2007-11-01 10:52:51 +00:00
|
|
|
{
|
|
|
|
QColor qmin = filterColor(guiApp->colorCache().get(min));
|
|
|
|
QColor qmax = filterColor(guiApp->colorCache().get(max));
|
|
|
|
monochrome_min_.push(qmin);
|
|
|
|
monochrome_max_.push(qmax);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GuiPainter::leaveMonochromeMode()
|
|
|
|
{
|
2013-04-25 21:27:10 +00:00
|
|
|
LASSERT(!monochrome_min_.empty(), return);
|
2007-11-01 10:52:51 +00:00
|
|
|
monochrome_min_.pop();
|
|
|
|
monochrome_max_.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-09 20:10:11 +00:00
|
|
|
void GuiPainter::point(int x, int y, Color col)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-11-01 10:52:51 +00:00
|
|
|
setQPainterPen(computeColor(col));
|
2006-10-23 08:46:09 +00:00
|
|
|
drawPoint(x, y);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
void GuiPainter::line(int x1, int y1, int x2, int y2,
|
2009-02-09 20:10:11 +00:00
|
|
|
Color col,
|
2006-03-05 17:24:44 +00:00
|
|
|
line_style ls,
|
2015-07-14 15:42:45 +00:00
|
|
|
int lw)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-11-01 10:52:51 +00:00
|
|
|
setQPainterPen(computeColor(col), ls, lw);
|
2007-05-04 09:24:44 +00:00
|
|
|
bool const do_antialiasing = renderHints() & TextAntialiasing
|
2016-05-23 10:38:48 +00:00
|
|
|
&& x1 != x2 && y1 != y2 && ls != line_solid_aliased;
|
2007-05-04 08:52:40 +00:00
|
|
|
setRenderHint(Antialiasing, do_antialiasing);
|
2006-10-23 08:46:09 +00:00
|
|
|
drawLine(x1, y1, x2, y2);
|
2007-03-29 23:08:29 +00:00
|
|
|
setRenderHint(Antialiasing, false);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
void GuiPainter::lines(int const * xp, int const * yp, int np,
|
2009-02-09 20:10:11 +00:00
|
|
|
Color col,
|
2014-10-25 21:38:52 +00:00
|
|
|
fill_style fs,
|
2006-03-05 17:24:44 +00:00
|
|
|
line_style ls,
|
2015-07-14 15:42:45 +00:00
|
|
|
int lw)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-05-20 08:50:54 +00:00
|
|
|
// double the size if needed
|
2016-08-04 12:30:47 +00:00
|
|
|
// FIXME THREAD
|
2007-05-20 08:50:54 +00:00
|
|
|
static QVector<QPoint> points(32);
|
|
|
|
if (np > points.size())
|
|
|
|
points.resize(2 * np);
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2016-07-30 23:42:51 +00:00
|
|
|
// Note: the proper way to not get blurry vertical and horizontal lines is
|
|
|
|
// to add 0.5 to all coordinates.
|
2016-08-01 19:50:46 +00:00
|
|
|
bool antialias = false;
|
2007-05-19 10:52:47 +00:00
|
|
|
for (int i = 0; i < np; ++i) {
|
|
|
|
points[i].setX(xp[i]);
|
|
|
|
points[i].setY(yp[i]);
|
2007-05-28 22:27:45 +00:00
|
|
|
if (i != 0)
|
2007-05-19 10:52:47 +00:00
|
|
|
antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
|
2007-05-04 08:52:40 +00:00
|
|
|
}
|
2014-10-25 21:38:52 +00:00
|
|
|
QColor const color = computeColor(col);
|
|
|
|
setQPainterPen(color, ls, lw);
|
2007-05-19 10:52:47 +00:00
|
|
|
bool const text_is_antialiased = renderHints() & TextAntialiasing;
|
2016-08-01 19:50:46 +00:00
|
|
|
setRenderHint(Antialiasing,
|
|
|
|
antialias && text_is_antialiased && ls != line_solid_aliased);
|
2014-10-25 21:38:52 +00:00
|
|
|
if (fs == fill_none) {
|
|
|
|
drawPolyline(points.data(), np);
|
|
|
|
} else {
|
|
|
|
QBrush const oldbrush = brush();
|
|
|
|
setBrush(QBrush(color));
|
|
|
|
drawPolygon(points.data(), np, fs == fill_oddeven ?
|
|
|
|
Qt::OddEvenFill : Qt::WindingFill);
|
|
|
|
setBrush(oldbrush);
|
|
|
|
}
|
2007-05-19 10:52:47 +00:00
|
|
|
setRenderHint(Antialiasing, false);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-24 11:49:02 +00:00
|
|
|
void GuiPainter::path(int const * xp, int const * yp,
|
|
|
|
int const * c1x, int const * c1y,
|
|
|
|
int const * c2x, int const * c2y,
|
|
|
|
int np,
|
|
|
|
Color col,
|
|
|
|
fill_style fs,
|
|
|
|
line_style ls,
|
|
|
|
int lw)
|
|
|
|
{
|
|
|
|
QPainterPath bpath;
|
|
|
|
// This is the starting point, so its control points are meaningless
|
|
|
|
bpath.moveTo(xp[0], yp[0]);
|
|
|
|
|
|
|
|
for (int i = 1; i < np; ++i) {
|
|
|
|
bool line = c1x[i] == xp[i - 1] && c1y[i] == yp[i - 1] &&
|
|
|
|
c2x[i] == xp[i] && c2y[i] == yp[i];
|
|
|
|
if (line)
|
|
|
|
bpath.lineTo(xp[i], yp[i]);
|
|
|
|
else
|
|
|
|
bpath.cubicTo(c1x[i], c1y[i], c2x[i], c2y[i], xp[i], yp[i]);
|
|
|
|
}
|
|
|
|
QColor const color = computeColor(col);
|
|
|
|
setQPainterPen(color, ls, lw);
|
|
|
|
bool const text_is_antialiased = renderHints() & TextAntialiasing;
|
2016-05-23 10:38:48 +00:00
|
|
|
setRenderHint(Antialiasing, text_is_antialiased && ls != line_solid_aliased);
|
2016-01-24 11:49:02 +00:00
|
|
|
drawPath(bpath);
|
|
|
|
if (fs != fill_none)
|
|
|
|
fillPath(bpath, QBrush(color));
|
|
|
|
setRenderHint(Antialiasing, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
void GuiPainter::rectangle(int x, int y, int w, int h,
|
2009-02-09 20:10:11 +00:00
|
|
|
Color col,
|
2006-03-05 17:24:44 +00:00
|
|
|
line_style ls,
|
2015-07-14 15:42:45 +00:00
|
|
|
int lw)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-11-01 10:52:51 +00:00
|
|
|
setQPainterPen(computeColor(col), ls, lw);
|
2006-10-23 08:46:09 +00:00
|
|
|
drawRect(x, y, w, h);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-09 20:10:11 +00:00
|
|
|
void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2006-10-23 08:46:09 +00:00
|
|
|
fillRect(x, y, w, h, guiApp->colorCache().get(col));
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
|
2009-02-09 20:10:11 +00:00
|
|
|
int a1, int a2, Color col)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
|
|
|
// LyX usings 1/64ths degree, Qt usings 1/16th
|
2007-11-01 10:52:51 +00:00
|
|
|
setQPainterPen(computeColor(col));
|
2007-05-04 09:24:44 +00:00
|
|
|
bool const do_antialiasing = renderHints() & TextAntialiasing;
|
2007-05-04 08:52:40 +00:00
|
|
|
setRenderHint(Antialiasing, do_antialiasing);
|
2006-10-23 08:46:09 +00:00
|
|
|
drawArc(x, y, w, h, a1 / 4, a2 / 4);
|
2007-03-29 23:08:29 +00:00
|
|
|
setRenderHint(Antialiasing, false);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-08-31 22:16:11 +00:00
|
|
|
void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2007-08-31 22:16:11 +00:00
|
|
|
graphics::GuiImage const & qlimage =
|
|
|
|
static_cast<graphics::GuiImage const &>(i);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2007-10-25 12:41:02 +00:00
|
|
|
fillRectangle(x, y, w, h, Color_graphicsbg);
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2014-10-12 17:23:13 +00:00
|
|
|
QImage const image = qlimage.image();
|
|
|
|
QRectF const drect = QRectF(x, y, w, h);
|
|
|
|
QRectF const srect = QRectF(0, 0, image.width(), image.height());
|
2016-04-30 18:56:57 +00:00
|
|
|
// Bilinear filtering is needed on a rare occasion for instant previews when
|
|
|
|
// the user's configuration mixes low-dpi and high-dpi monitors (#10114).
|
|
|
|
// This filter is optimised by qt on pixel-aligned images, so this does not
|
|
|
|
// affect performances in other cases.
|
|
|
|
setRenderHint(SmoothPixmapTransform);
|
2014-10-12 17:23:13 +00:00
|
|
|
drawImage(drect, image, srect);
|
2016-04-30 18:56:57 +00:00
|
|
|
setRenderHint(SmoothPixmapTransform, false);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-19 16:52:07 +00:00
|
|
|
void GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2016-03-19 16:52:07 +00:00
|
|
|
text(x, y, docstring(1, c), f);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-20 19:02:05 +00:00
|
|
|
void GuiPainter::text(int x, int y, docstring const & s, FontInfo const & f)
|
|
|
|
{
|
2016-05-31 07:13:18 +00:00
|
|
|
text(x, y, s, f, Auto, 0.0, 0.0);
|
2016-03-20 19:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-19 16:52:07 +00:00
|
|
|
void GuiPainter::text(int x, int y, docstring const & s,
|
2016-05-31 07:13:18 +00:00
|
|
|
FontInfo const & f, Direction const dir,
|
2016-03-20 19:02:05 +00:00
|
|
|
double const wordspacing, double const tw)
|
2006-03-05 17:24:44 +00:00
|
|
|
{
|
2014-07-20 16:54:31 +00:00
|
|
|
//LYXERR0("text: x=" << x << ", s=" << s);
|
2017-07-15 23:25:03 +00:00
|
|
|
if (s.empty())
|
2016-03-19 16:52:07 +00:00
|
|
|
return;
|
2007-09-15 12:34:21 +00:00
|
|
|
|
2007-02-26 16:22:54 +00:00
|
|
|
/* Caution: The following ucs4 to QString conversions work for symbol fonts
|
|
|
|
only because they are no real conversions but simple casts in reality.
|
|
|
|
When we want to draw a symbol or calculate the metrics we pass the position
|
|
|
|
of the symbol in the font (as given in lib/symbols) as a char_type to the
|
|
|
|
frontend. This is just wrong, because the symbol is no UCS4 character at
|
|
|
|
all. You can think of this number as the code point of the symbol in a
|
2016-03-20 19:02:05 +00:00
|
|
|
custom symbol encoding. It works because this char_type is later on again
|
|
|
|
interpreted as a position in the font.
|
2007-02-26 16:22:54 +00:00
|
|
|
The correct solution would be to have extra functions for symbols, but that
|
|
|
|
would require to duplicate a lot of frontend and mathed support code.
|
|
|
|
*/
|
2007-02-26 15:13:08 +00:00
|
|
|
QString str = toqstr(s);
|
2006-08-13 22:54:59 +00:00
|
|
|
|
|
|
|
#if 0
|
2006-03-05 17:24:44 +00:00
|
|
|
// HACK: QT3 refuses to show single compose characters
|
2006-06-14 14:23:25 +00:00
|
|
|
// Still needed with Qt4?
|
2006-03-05 17:24:44 +00:00
|
|
|
if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
|
|
|
|
str = ' ' + str;
|
2006-08-13 22:54:59 +00:00
|
|
|
#endif
|
2006-03-05 17:24:44 +00:00
|
|
|
|
2015-07-18 23:22:10 +00:00
|
|
|
QFont ff = getFont(f);
|
|
|
|
ff.setWordSpacing(wordspacing);
|
2014-07-20 22:19:50 +00:00
|
|
|
GuiFontMetrics const & fm = getFontMetrics(f);
|
2006-10-07 16:15:06 +00:00
|
|
|
|
2016-03-20 19:02:05 +00:00
|
|
|
int textwidth = 0;
|
|
|
|
if (tw == 0.0)
|
2018-10-07 12:48:05 +00:00
|
|
|
// Take into account space stretching (word spacing)
|
2016-08-13 18:03:02 +00:00
|
|
|
textwidth = fm.width(s) +
|
|
|
|
static_cast<int>(fm.countExpanders(s) * wordspacing);
|
2016-03-20 19:02:05 +00:00
|
|
|
else
|
|
|
|
textwidth = static_cast<int>(tw);
|
2007-08-13 17:35:09 +00:00
|
|
|
|
2014-05-23 16:59:53 +00:00
|
|
|
textDecoration(f, x, y, textwidth);
|
|
|
|
|
Add caching for the QTextLayout objects we use
The QTextLayout handling is terribly slow on Qt 4.8.7, but some
caching has been added in Qt5 that makes it much faster. For some
reason, it is not that slow with Qt 4.8.1.
Caches are introduced for the three following methods
* width(doctring), controlled by CACHE_METRICS_WIDTH. This cache already
existed, but the code has been cleaned up
* getTextLayout, controlled by CACHE_METRICS_QTEXTLAYOUT (disabled by
default on Qt5, which does its own caching). This is used for pos2x
and x2pos and now for drawing of text too. The previous code used a
trivial caching scheme of the last used QTextLayout, but now they
are properly kept in a QCache. Moreover, the cacheEnabled() property
is enabled for these QTextLayout object (not sure what this does).
* breakAt, controlled by CACHE_METRICS_BREAKAT. This is the only user
of QTextLayout which did not have some kind of caching already.
For some weird reasons related to Argument-dependent look-up, the
qHash(docstring) function has to be defined in std namespace, since
lyx::docstring is actually std::basic_string<wchar_t>.
[NOTE: this version has profiling hooks, enabled by commenting out the line
#define DISABLE_PMPROF
that should eventually be removed.]
2016-07-05 12:06:22 +00:00
|
|
|
setQPainterPen(computeColor(f.realColor()));
|
|
|
|
if (dir != Auto) {
|
2018-10-08 14:03:20 +00:00
|
|
|
auto ptl = fm.getTextLayout(s, dir == RtL, wordspacing);
|
2019-01-03 11:14:27 +00:00
|
|
|
QTextLine const & tline = ptl->lineForTextPosition(0);
|
|
|
|
ptl->draw(this, QPointF(x, y - tline.ascent()));
|
2018-10-08 14:03:20 +00:00
|
|
|
} else {
|
Add caching for the QTextLayout objects we use
The QTextLayout handling is terribly slow on Qt 4.8.7, but some
caching has been added in Qt5 that makes it much faster. For some
reason, it is not that slow with Qt 4.8.1.
Caches are introduced for the three following methods
* width(doctring), controlled by CACHE_METRICS_WIDTH. This cache already
existed, but the code has been cleaned up
* getTextLayout, controlled by CACHE_METRICS_QTEXTLAYOUT (disabled by
default on Qt5, which does its own caching). This is used for pos2x
and x2pos and now for drawing of text too. The previous code used a
trivial caching scheme of the last used QTextLayout, but now they
are properly kept in a QCache. Moreover, the cacheEnabled() property
is enabled for these QTextLayout object (not sure what this does).
* breakAt, controlled by CACHE_METRICS_BREAKAT. This is the only user
of QTextLayout which did not have some kind of caching already.
For some weird reasons related to Argument-dependent look-up, the
qHash(docstring) function has to be defined in std namespace, since
lyx::docstring is actually std::basic_string<wchar_t>.
[NOTE: this version has profiling hooks, enabled by commenting out the line
#define DISABLE_PMPROF
that should eventually be removed.]
2016-07-05 12:06:22 +00:00
|
|
|
if (font() != ff)
|
|
|
|
setFont(ff);
|
|
|
|
drawText(x, y, str);
|
|
|
|
}
|
2008-10-24 22:49:17 +00:00
|
|
|
//LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
|
|
|
|
// << " at " << x << "," << y);
|
2006-03-05 17:24:44 +00:00
|
|
|
}
|
2006-06-25 11:18:56 +00:00
|
|
|
|
|
|
|
|
2016-03-19 16:52:07 +00:00
|
|
|
void GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
2016-03-20 19:02:05 +00:00
|
|
|
double const wordspacing, double const tw)
|
2014-05-23 16:59:53 +00:00
|
|
|
{
|
2016-05-31 07:13:18 +00:00
|
|
|
text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft() ? RtL : LtR,
|
|
|
|
wordspacing, tw);
|
2014-05-23 16:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-19 16:52:07 +00:00
|
|
|
void GuiPainter::text(int x, int y, docstring const & str, Font const & f,
|
|
|
|
Color other, size_type const from, size_type const to,
|
2016-03-20 19:02:05 +00:00
|
|
|
double const wordspacing, double const tw)
|
2014-05-23 16:59:53 +00:00
|
|
|
{
|
|
|
|
GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
|
|
|
|
FontInfo fi = f.fontInfo();
|
2016-05-31 07:13:18 +00:00
|
|
|
Direction const dir = f.isVisibleRightToLeft() ? RtL : LtR;
|
2014-05-23 16:59:53 +00:00
|
|
|
|
|
|
|
// dimensions
|
|
|
|
int const ascent = fm.maxAscent();
|
|
|
|
int const height = fm.maxAscent() + fm.maxDescent();
|
2016-05-31 07:13:18 +00:00
|
|
|
int xmin = fm.pos2x(str, from, dir == RtL, wordspacing);
|
|
|
|
int xmax = fm.pos2x(str, to, dir == RtL, wordspacing);
|
2014-05-23 16:59:53 +00:00
|
|
|
if (xmin > xmax)
|
|
|
|
swap(xmin, xmax);
|
|
|
|
|
|
|
|
// First the part in other color
|
|
|
|
Color const orig = fi.realColor();
|
|
|
|
fi.setPaintColor(other);
|
2015-01-06 17:48:19 +00:00
|
|
|
QRegion const clip(x + xmin, y - ascent, xmax - xmin, height);
|
|
|
|
setClipRegion(clip);
|
2016-05-31 07:13:18 +00:00
|
|
|
text(x, y, str, fi, dir, wordspacing, tw);
|
2014-05-23 16:59:53 +00:00
|
|
|
|
|
|
|
// Then the part in normal color
|
2015-01-06 17:48:19 +00:00
|
|
|
// Note that in Qt5, it is not possible to use Qt::UniteClip,
|
|
|
|
// therefore QRegion is used.
|
2014-05-23 16:59:53 +00:00
|
|
|
fi.setPaintColor(orig);
|
2015-01-06 17:48:19 +00:00
|
|
|
QRegion region(viewport());
|
|
|
|
setClipRegion(region - clip);
|
2016-05-31 07:13:18 +00:00
|
|
|
text(x, y, str, fi, dir, wordspacing, tw);
|
2014-05-23 16:59:53 +00:00
|
|
|
setClipping(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-02 16:02:17 +00:00
|
|
|
void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
|
|
|
|
{
|
|
|
|
if (f.underbar() == FONT_ON)
|
|
|
|
underline(f, x, y, width);
|
|
|
|
if (f.strikeout() == FONT_ON)
|
|
|
|
strikeoutLine(f, x, y, width);
|
2017-04-04 22:01:19 +00:00
|
|
|
if (f.xout() == FONT_ON)
|
|
|
|
crossoutLines(f, x, y, width);
|
2012-05-02 16:02:17 +00:00
|
|
|
if (f.uuline() == FONT_ON)
|
|
|
|
doubleUnderline(f, x, y, width);
|
|
|
|
if (f.uwave() == FONT_ON)
|
|
|
|
// f.color() doesn't work on some circumstances
|
|
|
|
wavyHorizontalLine(x, y, width, f.realColor().baseColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 21:39:51 +00:00
|
|
|
static int max(int a, int b) { return a > b ? a : b; }
|
|
|
|
|
|
|
|
|
|
|
|
void GuiPainter::rectText(int x, int y, docstring const & str,
|
2009-02-09 20:10:11 +00:00
|
|
|
FontInfo const & font, Color back, Color frame)
|
2007-11-23 21:39:51 +00:00
|
|
|
{
|
2017-06-15 13:30:23 +00:00
|
|
|
int width, ascent, descent;
|
2007-11-23 21:39:51 +00:00
|
|
|
|
|
|
|
FontMetrics const & fm = theFontMetrics(font);
|
|
|
|
fm.rectText(str, width, ascent, descent);
|
|
|
|
|
|
|
|
if (back != Color_none)
|
|
|
|
fillRectangle(x + 1, y - ascent + 1, width - 1,
|
|
|
|
ascent + descent - 1, back);
|
|
|
|
|
|
|
|
if (frame != Color_none)
|
|
|
|
rectangle(x, y - ascent, width, ascent + descent, frame);
|
|
|
|
|
2017-06-15 13:30:23 +00:00
|
|
|
// FIXME: let offset depend on font
|
2007-11-23 21:39:51 +00:00
|
|
|
text(x + 3, y, str, font);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-15 13:30:23 +00:00
|
|
|
void GuiPainter::buttonText(int x, int baseline, docstring const & s,
|
|
|
|
FontInfo const & font, Color back, Color frame, int offset)
|
2007-11-23 21:39:51 +00:00
|
|
|
{
|
2017-06-15 13:30:23 +00:00
|
|
|
int width, ascent, descent;
|
2007-11-23 21:39:51 +00:00
|
|
|
|
|
|
|
FontMetrics const & fm = theFontMetrics(font);
|
2017-06-15 13:30:23 +00:00
|
|
|
fm.buttonText(s, offset, width, ascent, descent);
|
2007-11-23 21:39:51 +00:00
|
|
|
|
2017-06-15 13:30:23 +00:00
|
|
|
static int const d = offset / 2;
|
2008-03-02 10:20:13 +00:00
|
|
|
|
2017-06-15 13:30:23 +00:00
|
|
|
fillRectangle(x + d + 1, baseline - ascent + 1, width - offset - 1,
|
|
|
|
ascent + descent - 1, back);
|
|
|
|
rectangle(x + d, baseline - ascent, width - offset, ascent + descent, frame);
|
|
|
|
text(x + offset, baseline, s, font);
|
2007-11-23 21:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int GuiPainter::preeditText(int x, int y, char_type c,
|
|
|
|
FontInfo const & font, preedit_style style)
|
|
|
|
{
|
|
|
|
FontInfo temp_font = font;
|
|
|
|
FontMetrics const & fm = theFontMetrics(font);
|
|
|
|
int ascent = fm.maxAscent();
|
|
|
|
int descent = fm.maxDescent();
|
|
|
|
int height = ascent + descent;
|
|
|
|
int width = fm.width(c);
|
|
|
|
|
|
|
|
switch (style) {
|
|
|
|
case preedit_default:
|
|
|
|
// default unselecting mode.
|
|
|
|
fillRectangle(x, y - height + 1, width, height, Color_background);
|
|
|
|
dashedUnderline(font, x, y - descent + 1, width);
|
|
|
|
break;
|
|
|
|
case preedit_selecting:
|
|
|
|
// We are in selecting mode: white text on black background.
|
|
|
|
fillRectangle(x, y - height + 1, width, height, Color_black);
|
|
|
|
temp_font.setColor(Color_white);
|
|
|
|
break;
|
|
|
|
case preedit_cursor:
|
|
|
|
// The character comes with a cursor.
|
|
|
|
fillRectangle(x, y - height + 1, width, height, Color_background);
|
|
|
|
underline(font, x, y - descent + 1, width);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
text(x, y - descent + 1, c, temp_font);
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
void GuiPainter::underline(FontInfo const & f, int x, int y, int width,
|
|
|
|
line_style ls)
|
2009-05-05 09:26:28 +00:00
|
|
|
{
|
|
|
|
FontMetrics const & fm = theFontMetrics(f);
|
2015-04-14 13:22:11 +00:00
|
|
|
int const pos = fm.underlinePos();
|
2009-05-05 09:26:28 +00:00
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
line(x, y + pos, x + width, y + pos,
|
|
|
|
f.realColor(), ls, fm.lineWidth());
|
2009-05-05 09:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
|
2007-11-23 21:39:51 +00:00
|
|
|
{
|
|
|
|
FontMetrics const & fm = theFontMetrics(f);
|
2015-04-14 13:22:11 +00:00
|
|
|
int const pos = fm.strikeoutPos();
|
2007-11-23 21:39:51 +00:00
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
line(x, y - pos, x + width, y - pos,
|
|
|
|
f.realColor(), line_solid, fm.lineWidth());
|
2007-11-23 21:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 22:01:19 +00:00
|
|
|
void GuiPainter::crossoutLines(FontInfo const & f, int x, int y, int width)
|
|
|
|
{
|
2017-04-10 08:27:08 +00:00
|
|
|
FontInfo tmpf = f;
|
|
|
|
tmpf.setXout(FONT_OFF);
|
|
|
|
|
|
|
|
// the definition of \xout in ulem.sty is
|
|
|
|
// \def\xout{\bgroup \markoverwith{\hbox to.35em{\hss/\hss}}\ULon}
|
|
|
|
// Let's mimick it somewhat.
|
|
|
|
double offset = max(0.35 * theFontMetrics(tmpf).em(), 1);
|
|
|
|
for (int i = 0 ; i < iround(width / offset) ; ++i)
|
|
|
|
text(x + iround(i * offset), y, '/', tmpf);
|
2017-04-04 22:01:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
|
2009-05-03 22:45:14 +00:00
|
|
|
{
|
|
|
|
FontMetrics const & fm = theFontMetrics(f);
|
2015-04-14 13:22:11 +00:00
|
|
|
int const pos1 = fm.underlinePos() + fm.lineWidth();
|
|
|
|
int const pos2 = fm.underlinePos() - fm.lineWidth() + 1;
|
2009-05-03 22:45:14 +00:00
|
|
|
|
2015-04-14 13:22:11 +00:00
|
|
|
line(x, y + pos1, x + width, y + pos1,
|
|
|
|
f.realColor(), line_solid, fm.lineWidth());
|
|
|
|
line(x, y + pos2, x + width, y + pos2,
|
|
|
|
f.realColor(), line_solid, fm.lineWidth());
|
2009-05-03 22:45:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-23 21:39:51 +00:00
|
|
|
void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
|
|
|
|
{
|
|
|
|
FontMetrics const & fm = theFontMetrics(f);
|
|
|
|
|
|
|
|
int const below = max(fm.maxDescent() / 2, 2);
|
|
|
|
int height = max((fm.maxDescent() / 4) - 1, 1);
|
|
|
|
|
|
|
|
if (height >= 2)
|
|
|
|
height += below;
|
|
|
|
|
2007-11-29 22:38:53 +00:00
|
|
|
for (int n = 0; n != height; ++n)
|
2008-08-29 00:31:04 +00:00
|
|
|
line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
|
2007-11-23 21:39:51 +00:00
|
|
|
}
|
|
|
|
|
2009-03-28 16:37:28 +00:00
|
|
|
|
|
|
|
void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
|
|
|
|
{
|
|
|
|
setQPainterPen(computeColor(col));
|
2010-03-21 20:26:30 +00:00
|
|
|
int const step = 2;
|
2009-03-28 16:37:28 +00:00
|
|
|
int const xend = x + width;
|
|
|
|
int height = 1;
|
|
|
|
//FIXME: I am not sure if Antialiasing gives the best effect.
|
|
|
|
//setRenderHint(Antialiasing, true);
|
|
|
|
while (x < xend) {
|
|
|
|
height = - height;
|
|
|
|
drawLine(x, y - height, x + step, y + height);
|
|
|
|
x += step;
|
2010-03-21 20:26:30 +00:00
|
|
|
drawLine(x, y + height, x + step/2, y + height);
|
|
|
|
x += step/2;
|
2009-03-28 16:37:28 +00:00
|
|
|
}
|
|
|
|
//setRenderHint(Antialiasing, false);
|
|
|
|
}
|
|
|
|
|
2006-06-20 08:39:16 +00:00
|
|
|
} // namespace frontend
|
|
|
|
} // namespace lyx
|