diff options
Diffstat (limited to 'src/gui')
22 files changed, 210 insertions, 39 deletions
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 9da6acd0a7..1cd0e8e0aa 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -140,7 +140,7 @@ QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetyp qsizetype dummy; if (mul_overflow(height, qsizetype(sizeof(uchar *)), &dummy)) return invalid; // why is this here? -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +#if 1 || QT_VERSION < QT_VERSION_CHECK(6,0,0) // ### can only fix this if QImage dimensions are not int anymore // Disallow images where width * depth calculations might overflow if (width > (INT_MAX - 31) / depth) return invalid; diff --git a/src/gui/itemmodels/qstandarditemmodel.h b/src/gui/itemmodels/qstandarditemmodel.h index a9ee25da75..435b66591c 100644 --- a/src/gui/itemmodels/qstandarditemmodel.h +++ b/src/gui/itemmodels/qstandarditemmodel.h @@ -344,8 +344,11 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - // Qt 6: add override keyword - bool clearItemData(const QModelIndex &index); + bool clearItemData(const QModelIndex &index) +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + override +#endif + ; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; diff --git a/src/gui/kernel/qinputdevicemanager.cpp b/src/gui/kernel/qinputdevicemanager.cpp index 6e4e5a9c93..11442407e1 100644 --- a/src/gui/kernel/qinputdevicemanager.cpp +++ b/src/gui/kernel/qinputdevicemanager.cpp @@ -75,13 +75,13 @@ int QInputDeviceManager::deviceCount(DeviceType type) const int QInputDeviceManagerPrivate::deviceCount(QInputDeviceManager::DeviceType type) const { - return m_deviceCount.value(type); + return m_deviceCount[type]; } void QInputDeviceManagerPrivate::setDeviceCount(QInputDeviceManager::DeviceType type, int count) { Q_Q(QInputDeviceManager); - if (m_deviceCount.value(type) != count) { + if (m_deviceCount[type] != count) { m_deviceCount[type] = count; emit q->deviceListChanged(type); } diff --git a/src/gui/kernel/qinputdevicemanager_p.h b/src/gui/kernel/qinputdevicemanager_p.h index 74494d712b..1d20b102e3 100644 --- a/src/gui/kernel/qinputdevicemanager_p.h +++ b/src/gui/kernel/qinputdevicemanager_p.h @@ -69,7 +69,9 @@ public: DeviceTypePointer, DeviceTypeKeyboard, DeviceTypeTouch, - DeviceTypeTablet + DeviceTypeTablet, + + NumDeviceTypes }; QInputDeviceManager(QObject *parent = nullptr); diff --git a/src/gui/kernel/qinputdevicemanager_p_p.h b/src/gui/kernel/qinputdevicemanager_p_p.h index 0a91252fbc..871f9315c3 100644 --- a/src/gui/kernel/qinputdevicemanager_p_p.h +++ b/src/gui/kernel/qinputdevicemanager_p_p.h @@ -52,10 +52,11 @@ // #include <QtGui/private/qtguiglobal_p.h> -#include <QtCore/qmap.h> #include <private/qobject_p.h> #include "qinputdevicemanager_p.h" +#include <array> + QT_BEGIN_NAMESPACE class Q_GUI_EXPORT QInputDeviceManagerPrivate : public QObjectPrivate @@ -68,7 +69,7 @@ public: int deviceCount(QInputDeviceManager::DeviceType type) const; void setDeviceCount(QInputDeviceManager::DeviceType type, int count); - QMap<QInputDeviceManager::DeviceType, int> m_deviceCount; + std::array<int, QInputDeviceManager::NumDeviceTypes> m_deviceCount; Qt::KeyboardModifiers keyboardModifiers; }; diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 13d986073e..21e41979af 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -1385,16 +1385,16 @@ QGradient::QGradient(Preset preset) setCoordinateMode(ObjectMode); setSpread(PadSpread); - const QJsonValue start = presetData[QLatin1Literal("start")]; - const QJsonValue end = presetData[QLatin1Literal("end")]; - m_data.linear.x1 = start[QLatin1Literal("x")].toDouble(); - m_data.linear.y1 = start[QLatin1Literal("y")].toDouble(); - m_data.linear.x2 = end[QLatin1Literal("x")].toDouble(); - m_data.linear.y2 = end[QLatin1Literal("y")].toDouble(); + const QJsonValue start = presetData[QLatin1String("start")]; + const QJsonValue end = presetData[QLatin1String("end")]; + m_data.linear.x1 = start[QLatin1String("x")].toDouble(); + m_data.linear.y1 = start[QLatin1String("y")].toDouble(); + m_data.linear.x2 = end[QLatin1String("x")].toDouble(); + m_data.linear.y2 = end[QLatin1String("y")].toDouble(); for (const QJsonValue &stop : presetData[QLatin1String("stops")].toArray()) { - setColorAt(stop[QLatin1Literal("position")].toDouble(), - QColor(QRgb(stop[QLatin1Literal("color")].toInt()))); + setColorAt(stop[QLatin1String("position")].toDouble(), + QColor(QRgb(stop[QLatin1String("color")].toInt()))); } cachedPresets.insert(preset, *this); diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp index 82bb617733..5967c8d3de 100644 --- a/src/gui/text/qdistancefield.cpp +++ b/src/gui/text/qdistancefield.cpp @@ -904,6 +904,9 @@ QDistanceField::QDistanceField(const QDistanceField &other) d = other.d; } +QDistanceField &QDistanceField::operator=(const QDistanceField &) + = default; + QDistanceField::QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution) { setGlyph(font, glyph, doubleResolution); diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h index 1a1b6866a2..dc5d5a9a02 100644 --- a/src/gui/text/qdistancefield_p.h +++ b/src/gui/text/qdistancefield_p.h @@ -95,6 +95,7 @@ public: QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false); QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution = false); QDistanceField(const QDistanceField &other); + QDistanceField &operator=(const QDistanceField &other); bool isNull() const; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index c800cea3f6..899801ca39 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2709,6 +2709,12 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment) if (imgFmt.hasProperty(QTextFormat::ImageName)) emitAttribute("src", imgFmt.name()); + if (imgFmt.hasProperty(QTextFormat::ImageAltText)) + emitAttribute("alt", imgFmt.stringProperty(QTextFormat::ImageAltText)); + + if (imgFmt.hasProperty(QTextFormat::ImageTitle)) + emitAttribute("title", imgFmt.stringProperty(QTextFormat::ImageTitle)); + if (imgFmt.hasProperty(QTextFormat::ImageWidth)) emitAttribute("width", QString::number(imgFmt.width())); diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index aef4ea1522..1905d9a1b1 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -715,6 +715,10 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes() case Html_img: { QTextImageFormat fmt; fmt.setName(currentNode->imageName); + if (!currentNode->text.isEmpty()) + fmt.setProperty(QTextFormat::ImageTitle, currentNode->text); + if (!currentNode->imageAlt.isEmpty()) + fmt.setProperty(QTextFormat::ImageAltText, currentNode->imageAlt); fmt.merge(currentNode->charFormat); diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 895b9034e0..090c6cc4ce 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -564,6 +564,9 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) \value BlockTrailingHorizontalRulerWidth The width of a horizontal ruler element. \value HeadingLevel The level of a heading, for example 1 corresponds to an HTML H1 tag; otherwise 0. This enum value has been added in Qt 5.12. + \value BlockCodeFence The character that was used in the "fences" around a Markdown code block. + If the code block was indented rather than fenced, the block should not have this property. + This enum value has been added in Qt 5.14. \value BlockQuoteLevel The depth of nested quoting on this block: 1 means the block is a top-level block quote. Blocks that are not block quotes should not have this property. @@ -659,7 +662,13 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) Image properties - \value ImageName + \value ImageName The filename or source of the image. + \value ImageTitle The title attribute of an HTML image tag, or + the quoted string that comes after the URL in a Markdown image link. + This enum value has been added in Qt 5.14. + \value ImageAltText The alt attribute of an HTML image tag, or + the image description in a Markdown image link. + This enum value has been added in Qt 5.14. \value ImageWidth \value ImageHeight \value ImageQuality diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index a631309ae0..a91461dcae 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -178,6 +178,7 @@ public: HeadingLevel = 0x1070, BlockQuoteLevel = 0x1080, BlockCodeLanguage = 0x1090, + BlockCodeFence = 0x1091, BlockMarker = 0x10A0, // character properties @@ -253,6 +254,8 @@ public: // image properties ImageName = 0x5000, + ImageTitle = 0x5001, + ImageAltText = 0x5002, ImageWidth = 0x5010, ImageHeight = 0x5011, ImageQuality = 0x5014, diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 37051502fa..642f0893b4 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -1556,6 +1556,10 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) } else if (key == QLatin1String("height")) { node->imageHeight = -2; // register that there is a value for it. setFloatAttribute(&node->imageHeight, value); + } else if (key == QLatin1String("alt")) { + node->imageAlt = value; + } else if (key == QLatin1String("title")) { + node->text = value; } break; case Html_tr: @@ -1631,6 +1635,10 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) else if (key == QLatin1String("type")) linkType = value; break; + case Html_pre: + if (key == QLatin1String("class") && value.startsWith(QLatin1String("language-"))) + node->blockFormat.setProperty(QTextFormat::BlockCodeLanguage, value.mid(9)); + break; default: break; } diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index 73dac38b82..6ce294d211 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -184,6 +184,7 @@ struct QTextHtmlParserNode { QString textListNumberPrefix; QString textListNumberSuffix; QString imageName; + QString imageAlt; qreal imageWidth; qreal imageHeight; QTextLength width; diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp index b96acba0e6..8a9bb3953c 100644 --- a/src/gui/text/qtextmarkdownimporter.cpp +++ b/src/gui/text/qtextmarkdownimporter.cpp @@ -165,12 +165,13 @@ int QTextMarkdownImporter::cbEnterBlock(int blockType, void *det) MD_BLOCK_CODE_DETAIL *detail = static_cast<MD_BLOCK_CODE_DETAIL *>(det); m_codeBlock = true; m_blockCodeLanguage = QLatin1String(detail->lang.text, int(detail->lang.size)); + m_blockCodeFence = detail->fence_char; QString info = QLatin1String(detail->info.text, int(detail->info.size)); m_needsInsertBlock = true; if (m_blockQuoteDepth) - qCDebug(lcMD, "CODE lang '%s' info '%s' inside QUOTE %d", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockQuoteDepth); + qCDebug(lcMD, "CODE lang '%s' info '%s' fenced with '%c' inside QUOTE %d", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockCodeFence, m_blockQuoteDepth); else - qCDebug(lcMD, "CODE lang '%s' info '%s'", qPrintable(m_blockCodeLanguage), qPrintable(info)); + qCDebug(lcMD, "CODE lang '%s' info '%s' fenced with '%c'", qPrintable(m_blockCodeLanguage), qPrintable(info), m_blockCodeFence); } break; case MD_BLOCK_H: { MD_BLOCK_H_DETAIL *detail = static_cast<MD_BLOCK_H_DETAIL *>(det); @@ -326,6 +327,7 @@ int QTextMarkdownImporter::cbLeaveBlock(int blockType, void *detail) case MD_BLOCK_CODE: { m_codeBlock = false; m_blockCodeLanguage.clear(); + m_blockCodeFence = 0; if (m_blockQuoteDepth) qCDebug(lcMD, "CODE ended inside QUOTE %d", m_blockQuoteDepth); else @@ -362,15 +364,10 @@ int QTextMarkdownImporter::cbEnterSpan(int spanType, void *det) } break; case MD_SPAN_IMG: { m_imageSpan = true; + m_imageFormat = QTextImageFormat(); MD_SPAN_IMG_DETAIL *detail = static_cast<MD_SPAN_IMG_DETAIL *>(det); - QString src = QString::fromUtf8(detail->src.text, int(detail->src.size)); - QString title = QString::fromUtf8(detail->title.text, int(detail->title.size)); - QTextImageFormat img; - img.setName(src); - if (m_needsInsertBlock) - insertBlock(); - qCDebug(lcMD) << "image" << src << "title" << title << "relative to" << m_doc->baseUrl(); - m_cursor->insertImage(img); + m_imageFormat.setName(QString::fromUtf8(detail->src.text, int(detail->src.size))); + m_imageFormat.setProperty(QTextFormat::ImageTitle, QString::fromUtf8(detail->title.text, int(detail->title.size))); break; } case MD_SPAN_CODE: @@ -406,8 +403,6 @@ int QTextMarkdownImporter::cbLeaveSpan(int spanType, void *detail) int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size) { - if (m_imageSpan) - return 0; // it's the alt-text if (m_needsInsertBlock) insertBlock(); #if QT_CONFIG(regularexpression) @@ -481,6 +476,17 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size) break; } + if (m_imageSpan) { + // TODO we don't yet support alt text with formatting, because of the cases where m_cursor + // already inserted the text above. Rather need to accumulate it in case we need it here. + m_imageFormat.setProperty(QTextFormat::ImageAltText, s); + qCDebug(lcMD) << "image" << m_imageFormat.name() + << "title" << m_imageFormat.stringProperty(QTextFormat::ImageTitle) + << "alt" << s << "relative to" << m_doc->baseUrl(); + m_cursor->insertImage(m_imageFormat); + return 0; // no error + } + if (!s.isEmpty()) m_cursor->insertText(s); if (m_cursor->currentList()) { @@ -536,6 +542,8 @@ void QTextMarkdownImporter::insertBlock() } if (m_codeBlock) { blockFormat.setProperty(QTextFormat::BlockCodeLanguage, m_blockCodeLanguage); + if (m_blockCodeFence) + blockFormat.setProperty(QTextFormat::BlockCodeFence, QString(QLatin1Char(m_blockCodeFence))); charFormat.setFont(m_monoFont); } else { blockFormat.setTopMargin(m_paragraphMargin); diff --git a/src/gui/text/qtextmarkdownimporter_p.h b/src/gui/text/qtextmarkdownimporter_p.h index f905aa0f87..1b8c2ca354 100644 --- a/src/gui/text/qtextmarkdownimporter_p.h +++ b/src/gui/text/qtextmarkdownimporter_p.h @@ -124,7 +124,9 @@ private: int m_tableCol = -1; // because relative cell movements (e.g. m_cursor->movePosition(QTextCursor::NextCell)) don't work int m_paragraphMargin = 0; int m_blockType = 0; + char m_blockCodeFence = 0; Features m_features; + QTextImageFormat m_imageFormat; QTextListFormat m_listFormat; QTextBlockFormat::MarkerType m_markerType = QTextBlockFormat::NoMarker; bool m_needsInsertBlock = false; diff --git a/src/gui/text/qtextmarkdownwriter.cpp b/src/gui/text/qtextmarkdownwriter.cpp index 02643acdca..f351c8d20b 100644 --- a/src/gui/text/qtextmarkdownwriter.cpp +++ b/src/gui/text/qtextmarkdownwriter.cpp @@ -55,6 +55,7 @@ Q_LOGGING_CATEGORY(lcMDW, "qt.text.markdown.writer") static const QChar Space = QLatin1Char(' '); static const QChar Newline = QLatin1Char('\n'); static const QChar LineBreak = QChar(0x2028); +static const QChar DoubleQuote = QLatin1Char('"'); static const QChar Backtick = QLatin1Char('`'); static const QChar Period = QLatin1Char('.'); @@ -133,6 +134,24 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame) writeFrame(iterator.currentFrame()); else { // no frame, it's a block QTextBlock block = iterator.currentBlock(); + // Look ahead and detect some cases when we should + // suppress needless blank lines, when there will be a big change in block format + bool nextIsDifferent = false; + bool ending = false; + { + QTextFrame::iterator next = iterator; + ++next; + if (next.atEnd()) { + nextIsDifferent = true; + ending = true; + } else { + QTextBlockFormat format = iterator.currentBlock().blockFormat(); + QTextBlockFormat nextFormat = next.currentBlock().blockFormat(); + if (nextFormat.indent() != format.indent() || + nextFormat.property(QTextFormat::BlockCodeLanguage) != format.property(QTextFormat::BlockCodeLanguage)) + nextIsDifferent = true; + } + } if (table) { QTextTableCell cell = table->cellAt(block.position()); if (tableRow < cell.row()) { @@ -149,7 +168,7 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame) if (lastWasList) m_stream << Newline; } - int endingCol = writeBlock(block, !table, table && tableRow == 0); + int endingCol = writeBlock(block, !table, table && tableRow == 0, nextIsDifferent); m_doubleNewlineWritten = false; if (table) { QTextTableCell cell = table->cellAt(block.position()); @@ -161,11 +180,19 @@ void QTextMarkdownWriter::writeFrame(const QTextFrame *frame) m_stream << QString(paddingLen, Space); for (int col = cell.column(); col < spanEndCol; ++col) m_stream << "|"; - } else if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) { + } else if (m_fencedCodeBlock && ending) { + m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space) + << m_codeBlockFence << Newline << Newline; + m_codeBlockFence.clear(); + } else if (m_indentedCodeBlock && nextIsDifferent) { m_stream << Newline; } else if (endingCol > 0) { - m_stream << Newline << Newline; - m_doubleNewlineWritten = true; + if (block.textList() || block.blockFormat().hasProperty(QTextFormat::BlockCodeLanguage)) { + m_stream << Newline; + } else { + m_stream << Newline << Newline; + m_doubleNewlineWritten = true; + } } lastWasList = block.textList(); } @@ -258,11 +285,13 @@ static void maybeEscapeFirstChar(QString &s) } } -int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ignoreFormat) +int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ignoreFormat, bool ignoreEmpty) { + if (block.text().isEmpty() && ignoreEmpty) + return 0; const int ColumnLimit = 80; QTextBlockFormat blockFmt = block.blockFormat(); - bool indentedCodeBlock = false; + bool missedBlankCodeBlockLine = false; if (block.textList()) { // it's a list-item auto fmt = block.textList()->format(); const int listLevel = fmt.indent(); @@ -323,7 +352,28 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign } else if (blockFmt.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) { m_stream << "- - -\n"; // unambiguous horizontal rule, not an underline under a heading return 0; + } else if (blockFmt.hasProperty(QTextFormat::BlockCodeFence) || blockFmt.stringProperty(QTextFormat::BlockCodeLanguage).length() > 0) { + // It's important to preserve blank lines in code blocks. But blank lines in code blocks + // inside block quotes are getting preserved anyway (along with the "> " prefix). + if (!blockFmt.hasProperty(QTextFormat::BlockQuoteLevel)) + missedBlankCodeBlockLine = true; // only if we don't get any fragments below + if (!m_fencedCodeBlock) { + QString fenceChar = blockFmt.stringProperty(QTextFormat::BlockCodeFence); + if (fenceChar.isEmpty()) + fenceChar = QLatin1String("`"); + m_codeBlockFence = QString(3, fenceChar.at(0)); + // A block quote can contain an indented code block, but not vice-versa. + m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space) << m_codeBlockFence + << Space << blockFmt.stringProperty(QTextFormat::BlockCodeLanguage) << Newline; + m_fencedCodeBlock = true; + } } else if (!blockFmt.indent()) { + if (m_fencedCodeBlock) { + m_stream << m_linePrefix << QString(m_wrappedLineIndent, Space) + << m_codeBlockFence << Newline; + m_fencedCodeBlock = false; + m_codeBlockFence.clear(); + } m_wrappedLineIndent = 0; m_linePrefix.clear(); if (blockFmt.hasProperty(QTextFormat::BlockQuoteLevel)) { @@ -336,7 +386,7 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign if (blockFmt.hasProperty(QTextFormat::BlockCodeLanguage)) { // A block quote can contain an indented code block, but not vice-versa. m_linePrefix += QString(4, Space); - indentedCodeBlock = true; + m_indentedCodeBlock = true; } } if (blockFmt.headingLevel()) @@ -357,6 +407,7 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign bool strikeOut = false; QString backticks(Backtick); for (QTextBlock::Iterator frag = block.begin(); !frag.atEnd(); ++frag) { + missedBlankCodeBlockLine = false; QString fragmentText = frag.fragment().text(); while (fragmentText.endsWith(Newline)) fragmentText.chop(1); @@ -372,7 +423,14 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign QTextCharFormat fmt = frag.fragment().charFormat(); if (fmt.isImageFormat()) { QTextImageFormat ifmt = fmt.toImageFormat(); - QString s = QLatin1String("![image](") + ifmt.name() + QLatin1Char(')'); + QString desc = ifmt.stringProperty(QTextFormat::ImageAltText); + if (desc.isEmpty()) + desc = QLatin1String("image"); + QString s = QLatin1String("![") + desc + QLatin1String("](") + ifmt.name(); + QString title = ifmt.stringProperty(QTextFormat::ImageTitle); + if (!title.isEmpty()) + s += Space + DoubleQuote + title + DoubleQuote; + s += QLatin1Char(')'); if (wrap && col + s.length() > ColumnLimit) { m_stream << Newline << wrapIndentString; col = m_wrappedLineIndent; @@ -393,7 +451,7 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign bool monoFrag = fontInfo.fixedPitch(); QString markers; if (!ignoreFormat) { - if (monoFrag != mono && !indentedCodeBlock) { + if (monoFrag != mono && !m_indentedCodeBlock && !m_fencedCodeBlock) { if (monoFrag) backticks = QString(adjacentBackticksCount(fragmentText) + 1, Backtick); markers += backticks; @@ -493,6 +551,8 @@ int QTextMarkdownWriter::writeBlock(const QTextBlock &block, bool wrap, bool ign m_stream << "~~"; col += 2; } + if (missedBlankCodeBlockLine) + m_stream << Newline; return col; } diff --git a/src/gui/text/qtextmarkdownwriter_p.h b/src/gui/text/qtextmarkdownwriter_p.h index 96ceb445cd..90310250ac 100644 --- a/src/gui/text/qtextmarkdownwriter_p.h +++ b/src/gui/text/qtextmarkdownwriter_p.h @@ -67,7 +67,7 @@ public: bool writeAll(const QTextDocument *document); void writeTable(const QAbstractItemModel *table); - int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat); + int writeBlock(const QTextBlock &block, bool table, bool ignoreFormat, bool ignoreEmpty); void writeFrame(const QTextFrame *frame); private: @@ -82,9 +82,12 @@ private: QTextDocument::MarkdownFeatures m_features; QMap<QTextList *, ListInfo> m_listInfo; QString m_linePrefix; + QString m_codeBlockFence; int m_wrappedLineIndent = 0; int m_lastListIndent = 1; bool m_doubleNewlineWritten = false; + bool m_indentedCodeBlock = false; + bool m_fencedCodeBlock = false; }; QT_END_NAMESPACE diff --git a/src/gui/vulkan/qplatformvulkaninstance.cpp b/src/gui/vulkan/qplatformvulkaninstance.cpp index 6201d3ec11..9d044bfd58 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.cpp +++ b/src/gui/vulkan/qplatformvulkaninstance.cpp @@ -85,4 +85,9 @@ void QPlatformVulkanInstance::presentQueued(QWindow *window) Q_UNUSED(window); } +void QPlatformVulkanInstance::setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters) +{ + Q_UNUSED(filters); +} + QT_END_NAMESPACE diff --git a/src/gui/vulkan/qplatformvulkaninstance.h b/src/gui/vulkan/qplatformvulkaninstance.h index 9f34803f7b..d47c59b5db 100644 --- a/src/gui/vulkan/qplatformvulkaninstance.h +++ b/src/gui/vulkan/qplatformvulkaninstance.h @@ -78,6 +78,7 @@ public: virtual PFN_vkVoidFunction getInstanceProcAddr(const char *name) = 0; virtual bool supportsPresent(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, QWindow *window) = 0; virtual void presentQueued(QWindow *window); + virtual void setDebugFilters(const QVector<QVulkanInstance::DebugFilter> &filters); private: QScopedPointer<QPlatformVulkanInstancePrivate> d_ptr; diff --git a/src/gui/vulkan/qvulkaninstance.cpp b/src/gui/vulkan/qvulkaninstance.cpp index 2941bfd01f..0605d88cca 100644 --- a/src/gui/vulkan/qvulkaninstance.cpp +++ b/src/gui/vulkan/qvulkaninstance.cpp @@ -269,6 +269,7 @@ public: VkResult errorCode; QScopedPointer<QVulkanFunctions> funcs; QHash<VkDevice, QVulkanDeviceFunctions *> deviceFuncs; + QVector<QVulkanInstance::DebugFilter> debugFilters; }; bool QVulkanInstancePrivate::ensureVulkan() @@ -570,6 +571,7 @@ bool QVulkanInstance::create() d_ptr->extensions = d_ptr->platformInst->enabledExtensions(); d_ptr->errorCode = VK_SUCCESS; d_ptr->funcs.reset(new QVulkanFunctions(this)); + d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); return true; } @@ -785,6 +787,50 @@ void QVulkanInstance::presentQueued(QWindow *window) d_ptr->platformInst->presentQueued(window); } +/*! + \typedef QVulkanInstance::DebugFilter + + Typedef for debug filtering callback functions. + + \sa installDebugOutputFilter(), removeDebugOutputFilter() + */ + +/*! + Installs a \a filter function that is called for every Vulkan debug + message. When the callback returns \c true, the message is stopped (filtered + out) and will not appear on the debug output. + + \note Filtering is only effective when NoDebugOutputRedirect is not + \l{setFlags()}{set}. Installing filters has no effect otherwise. + + \note This function can be called before create(). + + \sa removeDebugOutputFilter() + */ +void QVulkanInstance::installDebugOutputFilter(DebugFilter filter) +{ + if (!d_ptr->debugFilters.contains(filter)) { + d_ptr->debugFilters.append(filter); + if (d_ptr->platformInst) + d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); + } +} + +/*! + Removes a \a filter function previously installed by + installDebugOutputFilter(). + + \note This function can be called before create(). + + \sa installDebugOutputFilter() + */ +void QVulkanInstance::removeDebugOutputFilter(DebugFilter filter) +{ + d_ptr->debugFilters.removeOne(filter); + if (d_ptr->platformInst) + d_ptr->platformInst->setDebugFilters(d_ptr->debugFilters); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVulkanLayer &layer) { diff --git a/src/gui/vulkan/qvulkaninstance.h b/src/gui/vulkan/qvulkaninstance.h index f28975f911..70f2fd5102 100644 --- a/src/gui/vulkan/qvulkaninstance.h +++ b/src/gui/vulkan/qvulkaninstance.h @@ -188,6 +188,11 @@ public: void presentQueued(QWindow *window); + typedef bool (*DebugFilter)(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, + size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage); + void installDebugOutputFilter(DebugFilter filter); + void removeDebugOutputFilter(DebugFilter filter); + private: QScopedPointer<QVulkanInstancePrivate> d_ptr; Q_DISABLE_COPY(QVulkanInstance) |