Skip to content

Commit

Permalink
Use CSS classes on html list items for checkbox support
Browse files Browse the repository at this point in the history
If we replace the bullet character with a UC checkbox character, it
looks ok in a browser, and the HTML parser can recover the BlockMarker
attribute from the css class.

[ChangeLog][QtGui][Text] Checkbox list items can now be read and written
in both HTML and Markdown, including conversions.

Task-number: QTBUG-103714
Change-Id: Ic6b74512075cd4ac16d6f80fdf55b221447491a9
Reviewed-by: Qt CI Bot <[email protected]>
Reviewed-by: Allan Sandfeld Jensen <[email protected]>
  • Loading branch information
ec1oud committed Jun 11, 2022
1 parent 88f5955 commit eee9d25
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 15 deletions.
16 changes: 15 additions & 1 deletion src/gui/text/qtextdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2372,6 +2372,8 @@ QString QTextHtmlExporter::toHtml(ExportMode mode)
html += "<style type=\"text/css\">\n"_L1;
html += "p, li { white-space: pre-wrap; }\n"_L1;
html += "hr { height: 1px; border-width: 0; }\n"_L1;
html += "li.unchecked::marker { content: \"\\2610\"; }\n"_L1;
html += "li.checked::marker { content: \"\\2612\"; }\n"_L1;
html += "</style>"_L1;
html += "</head><body"_L1;

Expand Down Expand Up @@ -3038,7 +3040,7 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)

html += " style=\""_L1;
html += styleString;
html += "\">"_L1;
html += "\">\n"_L1;
}

html += "<li"_L1;
Expand All @@ -3051,6 +3053,18 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block)

defaultCharFormat.merge(block.charFormat());
}
if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) {
switch (block.blockFormat().marker()) {
case QTextBlockFormat::MarkerType::Checked:
html += " class=\"checked\""_L1;
break;
case QTextBlockFormat::MarkerType::Unchecked:
html += " class=\"unchecked\""_L1;
break;
case QTextBlockFormat::MarkerType::NoMarker:
break;
}
}
}

const QTextBlockFormat blockFormat = block.blockFormat();
Expand Down
8 changes: 8 additions & 0 deletions src/gui/text/qtexthtmlparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,14 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes)
}
}
break;
case Html_li:
if (key == "class"_L1) {
if (value == "unchecked"_L1)
node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Unchecked);
else if (value == "checked"_L1)
node->blockFormat.setMarker(QTextBlockFormat::MarkerType::Checked);
}
break;
case Html_a:
if (key == "href"_L1)
node->charFormat.setAnchorHref(value);
Expand Down
41 changes: 27 additions & 14 deletions tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ void tst_QTextDocument::init()
"<html><head><meta name=\"qrichtext\" content=\"1\" /><meta charset=\"utf-8\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"hr { height: 1px; border-width: 0; }\n"
"li.unchecked::marker { content: \"\\2610\"; }\n"
"li.checked::marker { content: \"\\2612\"; }\n"
"</style></head>"
"<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\">\n");
htmlHead = htmlHead.arg(defaultFont.family())
Expand Down Expand Up @@ -1371,7 +1373,7 @@ void tst_QTextDocument::toHtml_data()
QTest::newRow("lists") << QTextDocumentFragment(&doc)
<<
QString("EMPTYBLOCK") +
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\">\n<li DEFAULTBLOCKSTYLE>Blubb</li>\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
}

{
Expand All @@ -1394,7 +1396,7 @@ void tst_QTextDocument::toHtml_data()
QTest::newRow("charfmt-for-list-item") << QTextDocumentFragment(&doc)
<<
QString("EMPTYBLOCK") +
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li style=\" color:#0000ff;\" DEFAULTBLOCKSTYLE><span style=\" color:#ff0000;\">Blah</span></li></ul>");
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\">\n<li DEFAULTBLOCKSTYLE>Blubb</li>\n<li style=\" color:#0000ff;\" DEFAULTBLOCKSTYLE><span style=\" color:#ff0000;\">Blah</span></li></ul>");
}

{
Expand Down Expand Up @@ -1424,7 +1426,7 @@ void tst_QTextDocument::toHtml_data()
QTest::newRow("list-indent") << QTextDocumentFragment(&doc)
<<
QString("EMPTYBLOCK") +
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\">\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
}

{
Expand Down Expand Up @@ -1712,7 +1714,7 @@ void tst_QTextDocument::toHtml_data()

QTest::newRow("list-ul-margin") << QTextDocumentFragment(&doc)
<< QString("EMPTYBLOCK") +
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\">\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
}
{
CREATE_DOC_AND_CURSOR();
Expand All @@ -1722,12 +1724,12 @@ void tst_QTextDocument::toHtml_data()
cursor.insertHtml(listHtml);

QTest::newRow("nested-lists-one") << QTextDocumentFragment(&doc)
<< QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; "
<< QString("<ul DEFAULTULSTYLE 1;\">\n<li style=\" margin-top:12px; margin-bottom:0px; "
"margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
"item-1</li>\n<li DEFAULTBLOCKSTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li "
"item-1</li>\n<li DEFAULTBLOCKSTYLE>item-2\n<ul DEFAULTULSTYLE 2;\">\n<li "
"DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2\n<ul "
"DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>item-2.2.1</li></ul></li>\n"
"<li DEFAULTBLOCKSTYLE>item-2.3\n<ul DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>"
"DEFAULTULSTYLE 3;\">\n<li DEFAULTBLOCKSTYLE>item-2.2.1</li></ul></li>\n"
"<li DEFAULTBLOCKSTYLE>item-2.3\n<ul DEFAULTULSTYLE 3;\">\n<li DEFAULTBLOCKSTYLE>"
"item-2.3.1</li></ul></li></ul></li>\n<li DEFAULTLASTLISTYLE>item-3</li></ul>");
}
{
Expand All @@ -1736,9 +1738,9 @@ void tst_QTextDocument::toHtml_data()
cursor.insertHtml(listHtml);

QTest::newRow("nested-lists-two") << QTextDocumentFragment(&doc)
<< QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; "
<< QString("<ul DEFAULTULSTYLE 1;\">\n<li style=\" margin-top:12px; margin-bottom:0px; "
"margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
"item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li "
"item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\">\n<li "
"DEFAULTBLOCKSTYLE>item-2.1</li></ul></li></ul>");
}
{
Expand All @@ -1748,9 +1750,9 @@ void tst_QTextDocument::toHtml_data()
cursor.insertHtml(listHtml);

QTest::newRow("nested-lists-three") << QTextDocumentFragment(&doc)
<< QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; "
<< QString("<ul DEFAULTULSTYLE 1;\">\n<li style=\" margin-top:12px; margin-bottom:0px; "
"margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
"item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li "
"item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\">\n<li "
"DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2</li></ul>"
"</li></ul>");
}
Expand All @@ -1761,12 +1763,23 @@ void tst_QTextDocument::toHtml_data()
cursor.insertHtml(listHtml);

QTest::newRow("not-nested-list") << QTextDocumentFragment(&doc)
<< QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; "
<< QString("<ul DEFAULTULSTYLE 1;\">\n<li style=\" margin-top:12px; margin-bottom:0px; "
"margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">"
"item-1.1</li>\n<li DEFAULTBLOCKSTYLE>item-1.2</li></ul>\n<ul DEFAULTULSTYLE 1;\">"
"item-1.1</li>\n<li DEFAULTBLOCKSTYLE>item-1.2</li></ul>\n<ul DEFAULTULSTYLE 1;\">\n"
"<li style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; "
"margin-right:0px; -qt-block-indent:0; text-indent:0px;\">item-2.1</li></ul>");
}
{
CREATE_DOC_AND_CURSOR();
const QString listHtml = "<ul><li>bullet</li><li class=\"unchecked\">unchecked item</li><li class=\"checked\">checked item</li></ul>";
cursor.insertHtml(listHtml);

QTest::newRow("list with and without checkboxes") << QTextDocumentFragment(&doc)
<< QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\">\n"
"<li style=\" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">bullet</li>\n"
"<li class=\"unchecked\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">unchecked item</li>\n"
"<li class=\"checked\" style=\" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">checked item</li></ul>");
}
}

void tst_QTextDocument::toHtml()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ void tst_QTextMarkdownWriter::fromHtml_data()
QTest::newRow("preformats with embedded backticks") <<
"<pre>none `one` ``two``</pre>plain<pre>```three``` ````four````</pre>plain" <<
"```\nnone `one` ``two``\n\n```\nplain\n\n```\n```three``` ````four````\n\n```\nplain\n\n";
QTest::newRow("list items with and without checkboxes") <<
"<ul><li>bullet</li><li class=\"unchecked\">unchecked item</li><li class=\"checked\">checked item</li></ul>" <<
"- bullet\n- [ ] unchecked item\n- [x] checked item\n";
}

void tst_QTextMarkdownWriter::fromHtml()
Expand Down

0 comments on commit eee9d25

Please sign in to comment.