From 4f5c8fecaca5d875b19bc62fd8d8264f16eaedcd Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 27 Nov 2020 14:39:46 +0100 Subject: [PATCH] Write out the HTML correctly for nested lists When we are having nested lists then we need to ensure that the HTML is outputted correctly so that the closing list and item tags are placed in the right order. [ChangeLog][QtGui][QTextDocument] The output of toHtml() now handles nested lists correctly. Fixes: QTBUG-88374 Change-Id: I88afba0f897aeef78d4835a3124097fe6fd4d55e Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 72a5151403f107c445e20cf548ca2e7309c88ce7) Reviewed-by: Qt Cherry-pick Bot --- src/gui/text/qtextdocument.cpp | 29 +++++++++- src/gui/text/qtextdocument_p.h | 1 + .../text/qtextdocument/tst_qtextdocument.cpp | 58 +++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index e302a1e170f..8f4bca0df8b 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -3063,10 +3063,12 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) if (fragmentMarkers && block.position() + block.length() == QTextDocumentPrivate::get(doc)->length()) html += QLatin1String(""); + QString closeTags; + if (pre) html += QLatin1String(""); else if (list) - html += QLatin1String(""); + closeTags += QLatin1String(""); else { int headingLevel = blockFormat.headingLevel(); if (headingLevel > 0 && headingLevel <= 6) @@ -3078,9 +3080,30 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) if (list) { if (list->itemNumber(block) == list->count() - 1) { // last item? close list if (isOrderedList(list->format().style())) - html += QLatin1String(""); + closeTags += QLatin1String(""); else - html += QLatin1String(""); + closeTags += QLatin1String(""); + } + const QTextBlock nextBlock = block.next(); + // If the next block is the beginning of a new deeper nested list, then we don't + // want to close the current list item just yet. This should be closed when this + // item is fully finished + if (nextBlock.isValid() && nextBlock.textList() && + nextBlock.textList()->itemNumber(nextBlock) == 0 && + nextBlock.textList()->format().indent() > list->format().indent()) { + QString lastTag; + if (!closingTags.isEmpty() && list->itemNumber(block) == list->count() - 1) + lastTag = closingTags.takeLast(); + lastTag.prepend(closeTags); + closingTags << lastTag; + } else if (list->itemNumber(block) == list->count() - 1) { + // If we are at the end of the list now then we can add in the closing tags for that + // current block + html += closeTags; + if (!closingTags.isEmpty()) + html += closingTags.takeLast(); + } else { + html += closeTags; } } diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index adebd0251f9..b26d7657ec9 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -438,6 +438,7 @@ private: QTextCharFormat defaultCharFormat; const QTextDocument *doc; bool fragmentMarkers; + QStringList closingTags; }; QT_END_NAMESPACE diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp index a0489e287cf..b8140585460 100644 --- a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp +++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp @@ -1716,6 +1716,59 @@ void tst_QTextDocument::toHtml_data() << QString("EMPTYBLOCK") + QString("
  • Blah
"); } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "
  • item-1
  • item-2
    • item-2.1
    • item-2.2" + "
      • item-2.2.1
    • item-2.3
      • item-2.3.1" + "
  • item-3
"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-one") << QTextDocumentFragment(&doc) + << QString("
  • " + "item-1
  • \n
  • item-2\n
    • item-2.1
    • \n
    • item-2.2\n
      • item-2.2.1
    • \n" + "
    • item-2.3\n
      • " + "item-2.3.1
  • \n
  • item-3
"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "
  • item-1
  • item-2
    • item-2.1
"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-two") << QTextDocumentFragment(&doc) + << QString("
  • " + "item-1
  • \n
  • item-2\n
    • item-2.1
"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "
  • item-1
  • item-2
    • item-2.1
    • item-2.2" + "
"; + cursor.insertHtml(listHtml); + + QTest::newRow("nested-lists-three") << QTextDocumentFragment(&doc) + << QString("
  • " + "item-1
  • \n
  • item-2\n
    • item-2.1
    • \n
    • item-2.2
    " + "
"); + } + { + CREATE_DOC_AND_CURSOR(); + const QString listHtml = "
  • item-1.1
  • item-1.2
" + "
  • item-2.1
"; + cursor.insertHtml(listHtml); + + QTest::newRow("not-nested-list") << QTextDocumentFragment(&doc) + << QString("
  • " + "item-1.1
  • \n
  • item-1.2
\n
    " + "
  • item-2.1
"); + } } void tst_QTextDocument::toHtml() @@ -1730,6 +1783,11 @@ void tst_QTextDocument::toHtml() expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"); expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); expectedOutput.replace("EMPTYBLOCK", "


\n"); + expectedOutput.replace("DEFAULTULSTYLE", "style=\"margin-top: 0px; margin-bottom: 0px; " + "margin-left: 0px; margin-right: 0px; -qt-list-indent:"); + expectedOutput.replace("DEFAULTLASTLISTYLE", "style=\" margin-top:0px; margin-bottom:12px; " + "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""); + if (expectedOutput.endsWith(QLatin1Char('\n'))) expectedOutput.chop(1); expectedOutput.append(htmlTail);