Use real italic slope for slanted caret

This commit is contained in:
Yuriy Skalko 2020-09-23 11:18:08 +03:00 committed by Jean-Marc Lasgouttes
parent 0bda5e5b8d
commit e47092044b
4 changed files with 57 additions and 18 deletions

View File

@ -77,6 +77,8 @@ public:
virtual int strikeoutPos() const = 0; virtual int strikeoutPos() const = 0;
/// return true if font is not upright (italic or oblique) /// return true if font is not upright (italic or oblique)
virtual bool italic() const = 0; virtual bool italic() const = 0;
/// return slope for italic font
virtual double italicSlope() const = 0;
/// return the width of the char in the font /// return the width of the char in the font
virtual int width(char_type c) const = 0; virtual int width(char_type c) const = 0;

View File

@ -20,11 +20,15 @@
#include "support/convert.h" #include "support/convert.h"
#include "support/lassert.h" #include "support/lassert.h"
#include "support/lyxlib.h" #include "support/lyxlib.h"
#include "support/debug.h"
#define DISABLE_PMPROF #define DISABLE_PMPROF
#include "support/pmprof.h" #include "support/pmprof.h"
#include <QByteArray> #include <QByteArray>
#include <QRawFont>
#include <QtEndian>
#include <QtMath>
using namespace std; using namespace std;
using namespace lyx::support; using namespace lyx::support;
@ -113,6 +117,24 @@ GuiFontMetrics::GuiFontMetrics(QFont const & font)
breakat_cache_(cache_metrics_breakat_size), breakat_cache_(cache_metrics_breakat_size),
qtextlayout_cache_(cache_metrics_qtextlayout_size) qtextlayout_cache_(cache_metrics_qtextlayout_size)
{ {
// Determine italic slope
double const defaultSlope = tan(qDegreesToRadians(19.0));
QRawFont raw = QRawFont::fromFont(font);
QByteArray post(raw.fontTable("post"));
if (post.length() == 0) {
slope_ = defaultSlope;
LYXERR(Debug::FONT, "Screen font doesn't have 'post' table.");
} else {
// post table description:
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
int32_t italicAngle = qFromBigEndian(*reinterpret_cast<int32_t *>(post.data() + 4));
double angle = italicAngle / 65536.0; // Fixed-point 16.16 to floating-point
slope_ = -tan(qDegreesToRadians(angle));
// Correct italic fonts with zero slope
if (slope_ == 0.0 && font.italic())
slope_ = defaultSlope;
LYXERR(Debug::FONT, "Italic slope: " << slope_);
}
} }
@ -167,6 +189,12 @@ bool GuiFontMetrics::italic() const
} }
double GuiFontMetrics::italicSlope() const
{
return slope_;
}
namespace { namespace {
int const outOfLimitMetric = -10000; int const outOfLimitMetric = -10000;
} }

View File

@ -43,6 +43,7 @@ public:
virtual int underlinePos() const; virtual int underlinePos() const;
virtual int strikeoutPos() const; virtual int strikeoutPos() const;
virtual bool italic() const; virtual bool italic() const;
virtual double italicSlope() const;
virtual int width(char_type c) const; virtual int width(char_type c) const;
virtual int ascent(char_type c) const; virtual int ascent(char_type c) const;
virtual int descent(char_type c) const; virtual int descent(char_type c) const;
@ -85,6 +86,9 @@ private:
/// Metrics on the font /// Metrics on the font
QFontMetrics metrics_; QFontMetrics metrics_;
/// Slope of italic font
double slope_;
/// Cache of char widths /// Cache of char widths
mutable QHash<char_type, int> width_cache_; mutable QHash<char_type, int> width_cache_;
/// Cache of string widths /// Cache of string widths

View File

@ -130,7 +130,7 @@ namespace frontend {
class CaretWidget { class CaretWidget {
public: public:
CaretWidget() : rtl_(false), l_shape_(false), completable_(false), CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
x_(0), caret_width_(0), slant_(false), ascent_(0) x_(0), caret_width_(0), slant_(false), ascent_(0), slope_(0)
{} {}
/* Draw the caret. Parameter \c horiz_offset is not 0 when there /* Draw the caret. Parameter \c horiz_offset is not 0 when there
@ -146,18 +146,18 @@ public:
int const lx = rtl_ ? x_ - rect_.left() : rect_.right() - x_; int const lx = rtl_ ? x_ - rect_.left() : rect_.right() - x_;
int const bot = rect_.bottom(); int const bot = rect_.bottom();
int const dir = rtl_ ? -1 : 1; int const dir = rtl_ ? -1 : 1;
// this is almost equal to tan(14 * PI / 180)
qreal const slope = 0.25;
// draw caret box // draw caret box
if (slant_ && !rtl_) { if (slant_ && !rtl_) {
// slanted (14 degree angle) // slanted
QPainterPath path; QPainterPath path;
path.moveTo(x + ascent_ * slope, y); path.moveTo(x + ascent_ * slope_, y);
path.lineTo(x - (rect_.height() - ascent_) * slope, y + rect_.height()); path.lineTo(x - (rect_.height() - ascent_) * slope_,
path.lineTo(x + dir * caret_width_ - (rect_.height() - ascent_) * slope, y + rect_.height());
path.lineTo(x + dir * caret_width_ - (rect_.height() - ascent_) * slope_,
y + rect_.height()); y + rect_.height());
path.lineTo(x + dir * caret_width_ + ascent_ * slope, y); path.lineTo(x + dir * caret_width_ + ascent_ * slope_, y);
painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::Antialiasing, true);
painter.fillPath(path, color_); painter.fillPath(path, color_);
painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::Antialiasing, false);
@ -176,7 +176,7 @@ public:
int const m = y + rect_.height() / 2; int const m = y + rect_.height() / 2;
int const d = TabIndicatorWidth - 1; int const d = TabIndicatorWidth - 1;
// offset for slanted carret // offset for slanted carret
int const sx = (slant_ && !rtl_) ? (ascent_ - (rect_.height() / 2 - d)) * slope : 0; int const sx = (slant_ && !rtl_) ? (ascent_ - (rect_.height() / 2 - d)) * slope_ : 0;
painter.drawLine(x + dir * (caret_width_ + 1) + sx, m - d, painter.drawLine(x + dir * (caret_width_ + 1) + sx, m - d,
x + dir * (caret_width_ + d + 1) + sx, m); x + dir * (caret_width_ + d + 1) + sx, m);
painter.drawLine(x + dir * (caret_width_ + 1) + sx, m + d, painter.drawLine(x + dir * (caret_width_ + 1) + sx, m + d,
@ -185,7 +185,7 @@ public:
} }
void update(int x, int y, int h, bool l_shape, void update(int x, int y, int h, bool l_shape,
bool rtl, bool completable, bool slant, int ascent) bool rtl, bool completable, bool slant, int ascent, double slope)
{ {
color_ = guiApp->colorCache().get(Color_cursor); color_ = guiApp->colorCache().get(Color_cursor);
l_shape_ = l_shape; l_shape_ = l_shape;
@ -194,6 +194,7 @@ public:
x_ = x; x_ = x;
slant_ = slant; slant_ = slant;
ascent_ = ascent; ascent_ = ascent;
slope_ = slope;
// extension to left and right // extension to left and right
int l = 0; int l = 0;
@ -245,6 +246,8 @@ private:
bool slant_; bool slant_;
/// the fontmetrics ascent for drawing slanted caret /// the fontmetrics ascent for drawing slanted caret
int ascent_; int ascent_;
/// the slope for drawing slanted caret
double slope_;
}; };
@ -644,10 +647,11 @@ void GuiWorkArea::Private::updateCaretGeometry()
Point point; Point point;
int h = 0; int h = 0;
buffer_view_->caretPosAndHeight(point, h); buffer_view_->caretPosAndHeight(point, h);
Cursor & cur = buffer_view_->cursor();
// RTL or not RTL // RTL or not RTL
bool l_shape = false; bool l_shape = false;
Font const & realfont = buffer_view_->cursor().real_current_font; Font const & realfont = cur.real_current_font;
FontMetrics const & fm = theFontMetrics(realfont.fontInfo()); FontMetrics const & fm = theFontMetrics(realfont.fontInfo());
BufferParams const & bp = buffer_view_->buffer().params(); BufferParams const & bp = buffer_view_->buffer().params();
bool const samelang = realfont.language() == bp.language; bool const samelang = realfont.language() == bp.language;
@ -661,22 +665,23 @@ void GuiWorkArea::Private::updateCaretGeometry()
l_shape = false; l_shape = false;
// show caret on screen // show caret on screen
Cursor & cur = buffer_view_->cursor();
bool completable = cur.inset().showCompletionCursor() bool completable = cur.inset().showCompletionCursor()
&& completer_->completionAvailable() && completer_->completionAvailable()
&& !completer_->popupVisible() && !completer_->popupVisible()
&& !completer_->inlineVisible(); && !completer_->inlineVisible();
caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable, // use slanted caret for italics in text edit mode
// use slanted caret for italics in text edit mode // except for selections because the selection rect does not slant
fm.italic() && buffer_view_->cursor().inTexted() int slant = fm.italic() && buffer_view_->cursor().inTexted()
// except for selections because the selection rect does not slant && !buffer_view_->cursor().selection();
&& !buffer_view_->cursor().selection(), fm.maxAscent()); double slope = fm.italicSlope();
caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable, slant,
fm.maxAscent(), slope);
needs_caret_geometry_update_ = false; needs_caret_geometry_update_ = false;
} }
void GuiWorkArea::Private::showCaret() void GuiWorkArea::Private::showCaret()
{ {
if (caret_visible_) if (caret_visible_)