From 310daae53926628f80c08e4415b94b90ad525c8f Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Dec 2017 13:05:20 +0100 Subject: preserve HTML heading level in QTextBlockFormat; demonstrate in example When reading an HTML file with

for example, we still set the font size as before (that's how it always was done), but now it remembers that it came from an H1 tag, so it writes

... rather than

... This will help with the upcoming Markdown format, where heading level is saved but the font is not. Now the style combobox in examples/widgets/richtext/textedit can set list item type, heading type or "standard" formatting, and also shows the current formatting of the line that has the cursor. It was always a shortcoming in this example that it only allowed setting the current line's block format but had no feedback to show the current format. Change-Id: I0a7849b74f23fea84d3375c487c3a6b9f43240c1 Reviewed-by: Lars Knoll --- examples/widgets/richtext/textedit/example.html | 14 +-- examples/widgets/richtext/textedit/textedit.cpp | 130 ++++++++++++++++-------- src/gui/text/qtextdocument.cpp | 15 ++- src/gui/text/qtextdocumentfragment.cpp | 36 ++++++- src/gui/text/qtextdocumentfragment_p.h | 1 + src/gui/text/qtextformat.h | 6 ++ 6 files changed, 149 insertions(+), 53 deletions(-) diff --git a/examples/widgets/richtext/textedit/example.html b/examples/widgets/richtext/textedit/example.html index e3a56d1154..99090a697f 100644 --- a/examples/widgets/richtext/textedit/example.html +++ b/examples/widgets/richtext/textedit/example.html @@ -1,14 +1,14 @@ QTextEdit Example -

QTextEdit

+

QTextEdit

The QTextEdit widget is an advanced editor that supports formatted rich text. It can be used to display HTML and other rich document formats. Internally, QTextEdit uses the QTextDocument class to describe both the high-level structure of each document and the low-level formatting of paragraphs.

If you are viewing this document in the textedit example, you can edit this document to explore Qt's rich text editing features. We have included some comments in each of the following sections to encourage you to experiment.

-

Font and Paragraph Styles

+

Font and Paragraph Styles

QTextEdit supports bold, italic, and underlined font styles, and can display multicolored text. Font families such as Times New Roman and Courier can also be used directly. If you place the cursor in a region of styled text, the controls in the tool bars will change to reflect the current style.

Paragraphs can be formatted so that the text is left-aligned, right-aligned, centered, or fully justified.

Try changing the alignment of some text and resize the editor to see how the text layout changes.

-

Lists

+

Lists

Different kinds of lists can be included in rich text documents. Standard bullet lists can be nested, using different symbols for each level of the list:

  • Disc symbols are typically used for top-level list items.
  • Circle symbols can be used to distinguish between items in lower-level lists.
@@ -24,10 +24,10 @@ p, li { white-space: pre-wrap; }

The list will automatically be renumbered if you add or remove items. Try adding new sections to the above list or removing existing item to see the numbers change.

-

Images

+

Images

Inline images are treated like ordinary ranges of characters in the text editor, so they flow with the surrounding text. Images can also be selected in the same way as text, making it easy to cut, copy, and paste them.

Try to select this image by clicking and dragging over it with the mouse, or use the text cursor to select it by holding down Shift and using the arrow keys. You can then cut or copy it, and paste it into different parts of this document.

-

Tables

+

Tables

QTextEdit can arrange and format tables, supporting features such as row and column spans, text formatting within cells, and size constraints for columns.

@@ -72,8 +72,8 @@ p, li { white-space: pre-wrap; }

Try adding text to the cells in the table and experiment with the alignment of the paragraphs.

-

Hyperlinks

+

Hyperlinks

QTextEdit is designed to support hyperlinks between documents, and this feature is used extensively in Qt Assistant. Hyperlinks are automatically created when an HTML file is imported into an editor. Since the rich text framework supports hyperlinks natively, they can also be created programatically.

-

Undo and Redo

+

Undo and Redo

Full support for undo and redo operations is built into QTextEdit and the underlying rich text framework. Operations on a document can be packaged together to make editing a more comfortable experience for the user.

Try making changes to this document and press Ctrl+Z to undo them. You can always recover the original contents of the document.

diff --git a/examples/widgets/richtext/textedit/textedit.cpp b/examples/widgets/richtext/textedit/textedit.cpp index fe4ee4f499..002574bcd7 100644 --- a/examples/widgets/richtext/textedit/textedit.cpp +++ b/examples/widgets/richtext/textedit/textedit.cpp @@ -348,6 +348,12 @@ void TextEdit::setupTextActions() comboStyle->addItem("Ordered List (Alpha upper)"); comboStyle->addItem("Ordered List (Roman lower)"); comboStyle->addItem("Ordered List (Roman upper)"); + comboStyle->addItem("Heading 1"); + comboStyle->addItem("Heading 2"); + comboStyle->addItem("Heading 3"); + comboStyle->addItem("Heading 4"); + comboStyle->addItem("Heading 5"); + comboStyle->addItem("Heading 6"); connect(comboStyle, QOverload::of(&QComboBox::activated), this, &TextEdit::textStyle); @@ -575,44 +581,56 @@ void TextEdit::textSize(const QString &p) void TextEdit::textStyle(int styleIndex) { QTextCursor cursor = textEdit->textCursor(); + QTextListFormat::Style style = QTextListFormat::ListStyleUndefined; + + switch (styleIndex) { + case 1: + style = QTextListFormat::ListDisc; + break; + case 2: + style = QTextListFormat::ListCircle; + break; + case 3: + style = QTextListFormat::ListSquare; + break; + case 4: + style = QTextListFormat::ListDecimal; + break; + case 5: + style = QTextListFormat::ListLowerAlpha; + break; + case 6: + style = QTextListFormat::ListUpperAlpha; + break; + case 7: + style = QTextListFormat::ListLowerRoman; + break; + case 8: + style = QTextListFormat::ListUpperRoman; + break; + default: + break; + } - if (styleIndex != 0) { - QTextListFormat::Style style = QTextListFormat::ListDisc; - - switch (styleIndex) { - default: - case 1: - style = QTextListFormat::ListDisc; - break; - case 2: - style = QTextListFormat::ListCircle; - break; - case 3: - style = QTextListFormat::ListSquare; - break; - case 4: - style = QTextListFormat::ListDecimal; - break; - case 5: - style = QTextListFormat::ListLowerAlpha; - break; - case 6: - style = QTextListFormat::ListUpperAlpha; - break; - case 7: - style = QTextListFormat::ListLowerRoman; - break; - case 8: - style = QTextListFormat::ListUpperRoman; - break; - } + cursor.beginEditBlock(); - cursor.beginEditBlock(); + QTextBlockFormat blockFmt = cursor.blockFormat(); - QTextBlockFormat blockFmt = cursor.blockFormat(); + if (style == QTextListFormat::ListStyleUndefined) { + blockFmt.setObjectIndex(-1); + int headingLevel = styleIndex >= 9 ? styleIndex - 9 + 1 : 0; // H1 to H6, or Standard + blockFmt.setHeadingLevel(headingLevel); + cursor.setBlockFormat(blockFmt); + int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; // H1 to H6: +3 to -2 + QTextCharFormat fmt; + fmt.setFontWeight(headingLevel ? QFont::Bold : QFont::Normal); + fmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment); + cursor.select(QTextCursor::LineUnderCursor); + cursor.mergeCharFormat(fmt); + textEdit->mergeCurrentCharFormat(fmt); + } else { QTextListFormat listFmt; - if (cursor.currentList()) { listFmt = cursor.currentList()->format(); } else { @@ -620,18 +638,11 @@ void TextEdit::textStyle(int styleIndex) blockFmt.setIndent(0); cursor.setBlockFormat(blockFmt); } - listFmt.setStyle(style); - cursor.createList(listFmt); - - cursor.endEditBlock(); - } else { - // #### - QTextBlockFormat bfmt; - bfmt.setObjectIndex(-1); - cursor.mergeBlockFormat(bfmt); } + + cursor.endEditBlock(); } void TextEdit::textColor() @@ -666,6 +677,41 @@ void TextEdit::currentCharFormatChanged(const QTextCharFormat &format) void TextEdit::cursorPositionChanged() { alignmentChanged(textEdit->alignment()); + QTextList *list = textEdit->textCursor().currentList(); + if (list) { + switch (list->format().style()) { + case QTextListFormat::ListDisc: + comboStyle->setCurrentIndex(1); + break; + case QTextListFormat::ListCircle: + comboStyle->setCurrentIndex(2); + break; + case QTextListFormat::ListSquare: + comboStyle->setCurrentIndex(3); + break; + case QTextListFormat::ListDecimal: + comboStyle->setCurrentIndex(4); + break; + case QTextListFormat::ListLowerAlpha: + comboStyle->setCurrentIndex(5); + break; + case QTextListFormat::ListUpperAlpha: + comboStyle->setCurrentIndex(6); + break; + case QTextListFormat::ListLowerRoman: + comboStyle->setCurrentIndex(7); + break; + case QTextListFormat::ListUpperRoman: + comboStyle->setCurrentIndex(8); + break; + default: + comboStyle->setCurrentIndex(-1); + break; + } + } else { + int headingLevel = textEdit->textCursor().blockFormat().headingLevel(); + comboStyle->setCurrentIndex(headingLevel ? headingLevel + 8 : 0); + } } void TextEdit::clipboardDataChanged() diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index d95932f4db..e58cd02af5 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2906,7 +2906,11 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) html += QLatin1Char('>'); html += QLatin1String(" 0 && headingLevel <= 6) + html += QLatin1String(""); else if (list) html += QLatin1String(""); - else - html += QLatin1String("

"); + else { + int headingLevel = blockFormat.headingLevel(); + if (headingLevel > 0 && headingLevel <= 6) + html += QLatin1String("
'); + else + html += QLatin1String("

"); + } if (list) { if (list->itemNumber(block) == list->count() - 1) { // last item? close list diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index ea37695f4e..3ad49b3f88 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -420,7 +420,7 @@ static QTextListFormat::Style nextListStyle(QTextListFormat::Style style) } QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider) - : indent(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode) + : indent(0), headingLevel(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode) { cursor = QTextCursor(doc); wsm = QTextHtmlParserNode::WhiteSpaceNormal; @@ -747,8 +747,28 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes() return ContinueWithNextNode; } + case Html_h1: + headingLevel = 1; + break; + case Html_h2: + headingLevel = 2; + break; + case Html_h3: + headingLevel = 3; + break; + case Html_h4: + headingLevel = 4; + break; + case Html_h5: + headingLevel = 5; + break; + case Html_h6: + headingLevel = 6; + break; + default: break; } + return ContinueWithCurrentNode; } @@ -832,6 +852,15 @@ bool QTextHtmlImporter::closeTag() } } break; + case Html_h1: + case Html_h2: + case Html_h3: + case Html_h4: + case Html_h5: + case Html_h6: + headingLevel = 0; + blockTagClosed = true; + break; default: if (closedNode->isBlock()) blockTagClosed = true; @@ -1093,6 +1122,11 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode() modifiedBlockFormat = true; } + if (headingLevel) { + block.setHeadingLevel(headingLevel); + modifiedBlockFormat = true; + } + if (currentNode->blockFormat.propertyCount() > 0) { modifiedBlockFormat = true; block.merge(currentNode->blockFormat); diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h index e8699545f7..02a6a429fa 100644 --- a/src/gui/text/qtextdocumentfragment_p.h +++ b/src/gui/text/qtextdocumentfragment_p.h @@ -152,6 +152,7 @@ private: friend class QTypeInfo; QVector lists; int indent; + int headingLevel; // insert a named anchor the next time we emit a char format, // either in a block or in regular text diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index 28c3035537..8f8d3d4299 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -175,6 +175,7 @@ public: LineHeightType = 0x1049, BlockNonBreakableLines = 0x1050, BlockTrailingHorizontalRulerWidth = 0x1060, + HeadingLevel = 0x1070, // character properties FirstFontProperty = 0x1FE0, @@ -624,6 +625,11 @@ public: inline int indent() const { return intProperty(BlockIndent); } + inline void setHeadingLevel(int alevel) + { setProperty(HeadingLevel, alevel); } + inline int headingLevel() const + { return intProperty(HeadingLevel); } + inline void setLineHeight(qreal height, int heightType) { setProperty(LineHeight, height); setProperty(LineHeightType, heightType); } inline qreal lineHeight(qreal scriptLineHeight, qreal scaling) const; -- cgit v1.2.3