diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index b70b29e54e..9519894076 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -6245,6 +6245,91 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) return pixmap; } +// Patch: Improved underline with SpellCheck style for macOS and Windows. +// Added implementation of underline drawing from Chrome. +static QPixmap generateChromeSpellcheckPixmap(qreal descent, qreal factor, const QPen &pen) { + QString key = QLatin1String("ChromeUnderline-") + % pen.color().name() + % HexString(factor) + % HexString(pen.widthF()); + + QPixmap pixmap; + if (QPixmapCache::find(key, pixmap)) { + return pixmap; + } + // https://chromium.googlesource.com/chromium/src/+/refs/heads/master/third_party/blink/renderer/core/paint/document_marker_painter.cc + +#ifdef Q_OS_MAC + + constexpr qreal kMarkerHeight = 3; + + const qreal height = kMarkerHeight * factor; + const qreal width = height * 2; + + pixmap = QPixmap(qCeil(width), qFloor(height) * 2); + pixmap.setDevicePixelRatio(qApp->devicePixelRatio()); + pixmap.fill(Qt::transparent); + { + QPainter imgPainter(&pixmap); + imgPainter.setPen(Qt::NoPen); + imgPainter.setBrush(pen.color()); + imgPainter.setRenderHints( + QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + imgPainter.drawEllipse(0, 0, qFloor(height), qFloor(height)); + } + +#else + + constexpr qreal kMarkerWidth = 4; + constexpr qreal kMarkerHeight = 2; + + const auto x1 = (kMarkerWidth * -3 / 8) * factor; + const auto y1 = (kMarkerHeight * 3 / 4) * factor; + + const auto cY = (kMarkerHeight * 1 / 4) * factor; + + const auto c1X1 = (kMarkerWidth * -1 / 8) * factor; + const auto c1X2 = (kMarkerWidth * 3 / 8) * factor; + const auto c1X3 = (kMarkerWidth * 7 / 8) * factor; + + const auto c2X1 = (kMarkerWidth * 1 / 8) * factor; + const auto c2X2 = (kMarkerWidth * 5 / 8) * factor; + const auto c2X3 = (kMarkerWidth * 9 / 8) * factor; + + QPainterPath path; + path.moveTo(x1, y1); + path.cubicTo(c1X1, y1, + c1X1, cY, + c2X1, cY); + path.cubicTo(c1X2, cY, + c1X2, y1, + c2X2, y1); + path.cubicTo(c1X3, y1, + c1X3, cY, + c2X3, cY); + + pixmap = QPixmap(kMarkerWidth * factor, kMarkerHeight * factor * 2); + pixmap.fill(Qt::transparent); + { + QPen wavePen = pen; + wavePen.setCapStyle(Qt::RoundCap); + wavePen.setJoinStyle(Qt::RoundJoin); + wavePen.setWidthF(1 * factor); + + QPainter imgPainter(&pixmap); + imgPainter.setPen(std::move(wavePen)); + imgPainter.setRenderHint(QPainter::Antialiasing); + imgPainter.translate(0, descent - (kMarkerHeight * factor)); + imgPainter.drawPath(std::move(path)); + } + +#endif + + QPixmapCache::insert(std::move(key), pixmap); + + return pixmap; +} + static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextEngine *textEngine, QTextCharFormat::UnderlineStyle underlineStyle, QTextItem::RenderFlags flags, qreal width, @@ -6262,7 +6347,9 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const pen.setWidthF(fe->lineThickness().toReal()); pen.setCapStyle(Qt::FlatCap); - QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y()); + // Patch: Improved underline with SpellCheck style for macOS and Windows. + // Slightly move the beginning of the underline to the right. + QLineF line(qFloor(pos.x() + 1), pos.y(), qFloor(pos.x() + width), pos.y()); bool wasCompatiblePainting = painter->renderHints() & QPainter::Qt4CompatiblePainting; @@ -6273,13 +6360,29 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const const qreal underlineOffset = fe->underlinePosition().toReal(); if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { - QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); - if (theme) - underlineStyle = QTextCharFormat::UnderlineStyle(theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt()); - if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved - underlineStyle = QTextCharFormat::WaveUnderline; - } + const qreal fontFactor = qreal(charFormat.font().pixelSize()) / qreal(10.); + painter->save(); + painter->translate(0, pos.y() + 1); + const qreal maxHeight = fe->descent().toReal() - qreal(1); + + QColor uc = charFormat.underlineColor(); + if (uc.isValid()) + pen.setColor(uc); + const QPixmap wave = generateChromeSpellcheckPixmap(maxHeight, fontFactor, pen); + const int descent = qFloor(maxHeight); + + painter->setBrushOrigin(painter->brushOrigin().x(), 0); +#ifdef Q_OS_MAC + const auto h = wave.height() / 2; + painter->drawTiledPixmap( + QRectF(pos.x(), (descent - h) / 2., qCeil(width), h), + wave); +#else + painter->fillRect(pos.x(), 0, qCeil(width), descent, wave); +#endif + painter->restore(); + } else if (underlineStyle == QTextCharFormat::WaveUnderline) { painter->save(); painter->translate(0, pos.y() + 1);