From 6492541df306e20d16e3a64d9df174c99c847945 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 18 Jun 2019 16:08:06 +0200 Subject: Support copy-pasting foreground images within same document When using a texture for foreground color in text and copy-pasting the text inside the same QTextEdit, the formatting would disappear. Fixing this in a general way would require implementing some other carrier format in the mime data than HTML, such as e.g ODF, but it can quite easily be fixed for the case where the data is pasted in the same document, or even different documents as long as they have a reference to the image in the formats. [ChangeLog][QtWidgets][QTextEdit] Added support for copy-pasting foreground brushes with textures within same document. Task-number: QTBUG-75931 Change-Id: I8b39dce289c64eea39e25cb8eb207e2534bcd2eb Reviewed-by: Eirik Aavitsland --- src/gui/text/qcssparser.cpp | 1 + src/gui/text/qcssparser_p.h | 1 + src/gui/text/qtextdocument.cpp | 16 +++++++-- src/gui/text/qtextdocument_p.h | 1 + src/gui/text/qtexthtmlparser.cpp | 42 ++++++++++++++++++++++ src/gui/text/qtexthtmlparser_p.h | 1 + .../widgets/widgets/qtextedit/tst_qtextedit.cpp | 31 ++++++++++++++++ 7 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index dc7e128bcd..b5489c7ed9 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -67,6 +67,7 @@ struct QCssKnownValue static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, + { "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey }, { "-qt-line-height-type", QtLineHeightType }, { "-qt-list-indent", QtListIndent }, { "-qt-list-number-prefix", QtListNumberPrefix }, diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index 62578f75e5..b0fa4be682 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -196,6 +196,7 @@ enum Property { LineHeight, QtLineHeightType, FontKerning, + QtForegroundTextureCacheKey, NumProperties }; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 757e2086e5..2ec3f41f42 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2470,9 +2470,19 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) if (format.foreground() != defaultCharFormat.foreground() && format.foreground().style() != Qt::NoBrush) { - html += QLatin1String(" color:"); - html += colorValue(format.foreground().color()); - html += QLatin1Char(';'); + QBrush brush = format.foreground(); + if (brush.style() == Qt::TexturePattern) { + const bool isPixmap = qHasPixmapTexture(brush); + const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey(); + + html += QLatin1String(" -qt-fg-texture-cachekey:"); + html += QString::number(cacheKey); + html += QLatin1String(";"); + } else { + html += QLatin1String(" color:"); + html += colorValue(brush.color()); + html += QLatin1Char(';'); + } attributesEmitted = true; } diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index a8e17bfc08..df00fb7d84 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -357,6 +357,7 @@ public: void mergeCachedResources(const QTextDocumentPrivate *priv); + friend struct QTextHtmlParserNode; friend class QTextHtmlExporter; friend class QTextCursor; }; diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 642f0893b4..0b1a23f399 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1335,6 +1335,17 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector default: break; } break; + + case QCss::QtForegroundTextureCacheKey: + { + if (resourceProvider != nullptr && resourceProvider->docHandle() != nullptr) { + bool ok; + qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok); + if (ok) + applyForegroundImage(searchKey, resourceProvider); + } + break; + } default: break; } } @@ -1367,6 +1378,37 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector #endif // QT_NO_CSSPARSER +void QTextHtmlParserNode::applyForegroundImage(qint64 searchKey, const QTextDocument *resourceProvider) +{ + QTextDocumentPrivate *priv = resourceProvider->docHandle(); + for (int i = 0; i < priv->formats.numFormats(); ++i) { + QTextCharFormat format = priv->formats.charFormat(i); + if (format.isValid()) { + QBrush brush = format.foreground(); + if (brush.style() == Qt::TexturePattern) { + const bool isPixmap = qHasPixmapTexture(brush); + + if (isPixmap && QCoreApplication::instance()->thread() != QThread::currentThread()) { + qWarning("Can't apply QPixmap outside of GUI thread"); + return; + } + + const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey(); + if (cacheKey == searchKey) { + QBrush b; + if (isPixmap) + b.setTexture(brush.texture()); + else + b.setTextureImage(brush.textureImage()); + b.setStyle(Qt::TexturePattern); + charFormat.setForeground(b); + } + } + } + } + +} + void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider) { if (!url.isEmpty() && resourceProvider) { diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 6ce294d211..c174b54a61 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -251,6 +251,7 @@ struct QTextHtmlParserNode { void setListStyle(const QVector &cssValues); #endif + void applyForegroundImage(qint64 cacheKey, const QTextDocument *resourceProvider); void applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider); bool hasOnlyWhitespace() const; diff --git a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp index 3669935823..81682dc027 100644 --- a/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp +++ b/tests/auto/widgets/widgets/qtextedit/tst_qtextedit.cpp @@ -160,6 +160,7 @@ private slots: void selectionChanged(); #ifndef QT_NO_CLIPBOARD void copyPasteBackgroundImage(); + void copyPasteForegroundImage(); #endif void setText(); void cursorRect(); @@ -1904,6 +1905,36 @@ void tst_QTextEdit::copyPasteBackgroundImage() ba.texture().cacheKey() == bb.texture().cacheKey()); QFile::remove(QLatin1String("foo.png")); } + +void tst_QTextEdit::copyPasteForegroundImage() +{ + ed->clear(); + + QPixmap pix(20, 20); + pix.fill(Qt::blue); + + QTextCharFormat fmt; + { + QBrush textureBrush; + { + textureBrush.setTexture(pix); + } + textureBrush.setStyle(Qt::TexturePattern); + fmt.setForeground(textureBrush); + } + ed->textCursor().insertText("Foobar", fmt); + + ed->moveCursor(QTextCursor::Start); + ed->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); + + ed->copy(); + ed->clear(); + ed->paste(); + + QBrush brush = ed->textCursor().charFormat().foreground(); + QCOMPARE(brush.style(), Qt::TexturePattern); + QCOMPARE(brush.texture().cacheKey(), pix.cacheKey()); +} #endif void tst_QTextEdit::setText() -- cgit v1.2.3