forked from qt-creator/qt-creator
ExtensionManager: Interpret description text as MarkDown
Task-number: QTCREATORBUG-31199 Fixes: QTCREATORBUG-31182 Change-Id: Iff0ec94db67b3afcad024b2fec4797805f1123e8 Reviewed-by: Cristian Adam <cristian.adam@qt.io>
This commit is contained in:
@@ -46,6 +46,8 @@
|
|||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QSignalMapper>
|
#include <QSignalMapper>
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QTextBlock>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@@ -421,8 +423,9 @@ ExtensionManagerWidget::ExtensionManagerWidget()
|
|||||||
m_secondaryDescriptionWidget = new CollapsingWidget;
|
m_secondaryDescriptionWidget = new CollapsingWidget;
|
||||||
|
|
||||||
m_headingWidget = new HeadingWidget;
|
m_headingWidget = new HeadingWidget;
|
||||||
m_description = tfLabel(contentTF, false);
|
m_description = new QLabel;
|
||||||
m_description->setWordWrap(true);
|
m_description->setWordWrap(true);
|
||||||
|
m_description->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||||
m_linksTitle = sectionTitle(h6CapitalTF, Tr::tr("More information"));
|
m_linksTitle = sectionTitle(h6CapitalTF, Tr::tr("More information"));
|
||||||
m_links = tfLabel(contentTF, false);
|
m_links = tfLabel(contentTF, false);
|
||||||
m_links->setOpenExternalLinks(true);
|
m_links->setOpenExternalLinks(true);
|
||||||
@@ -528,6 +531,47 @@ ExtensionManagerWidget::ExtensionManagerWidget()
|
|||||||
updateView({});
|
updateView({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString markdownToHtml(const QString &markdown)
|
||||||
|
{
|
||||||
|
QTextDocument doc;
|
||||||
|
doc.setMarkdown(markdown);
|
||||||
|
doc.setDefaultFont(contentTF.font());
|
||||||
|
|
||||||
|
for (QTextBlock block = doc.begin(); block != doc.end(); block = block.next()) {
|
||||||
|
QTextBlockFormat blockFormat = block.blockFormat();
|
||||||
|
if (blockFormat.hasProperty(QTextFormat::HeadingLevel))
|
||||||
|
blockFormat.setTopMargin(SpacingTokens::ExVPaddingGapXl);
|
||||||
|
else
|
||||||
|
blockFormat.setLineHeight(contentTF.lineHeight(), QTextBlockFormat::FixedHeight);
|
||||||
|
blockFormat.setBottomMargin(SpacingTokens::VGapL);
|
||||||
|
QTextCursor cursor(block);
|
||||||
|
cursor.mergeBlockFormat(blockFormat);
|
||||||
|
const TextFormat headingTf = blockFormat.headingLevel() == 1 ? h5TF : h6TF;
|
||||||
|
const QFont headingFont = headingTf.font();
|
||||||
|
for (auto it = block.begin(); !(it.atEnd()); ++it) {
|
||||||
|
QTextFragment fragment = it.fragment();
|
||||||
|
if (fragment.isValid()) {
|
||||||
|
QTextCharFormat charFormat = fragment.charFormat();
|
||||||
|
cursor.setPosition(fragment.position());
|
||||||
|
cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
|
||||||
|
if (blockFormat.hasProperty(QTextFormat::HeadingLevel)) {
|
||||||
|
charFormat.setFontFamilies(headingFont.families());
|
||||||
|
charFormat.setFontWeight(headingFont.weight());
|
||||||
|
charFormat.setFontPointSize(headingFont.pointSizeF());
|
||||||
|
charFormat.setForeground(headingTf.color());
|
||||||
|
} else if (charFormat.isAnchor()) {
|
||||||
|
charFormat.setForeground(creatorColor(Theme::Token_Text_Accent));
|
||||||
|
} else {
|
||||||
|
charFormat.setForeground(contentTF.color());
|
||||||
|
}
|
||||||
|
cursor.setCharFormat(charFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc.toHtml();
|
||||||
|
}
|
||||||
|
|
||||||
void ExtensionManagerWidget::updateView(const QModelIndex ¤t)
|
void ExtensionManagerWidget::updateView(const QModelIndex ¤t)
|
||||||
{
|
{
|
||||||
m_headingWidget->update(current);
|
m_headingWidget->update(current);
|
||||||
@@ -556,28 +600,15 @@ void ExtensionManagerWidget::updateView(const QModelIndex ¤t)
|
|||||||
const TextData textData = current.data(RoleDescriptionText).value<TextData>();
|
const TextData textData = current.data(RoleDescriptionText).value<TextData>();
|
||||||
const bool hasDescription = !textData.isEmpty();
|
const bool hasDescription = !textData.isEmpty();
|
||||||
if (hasDescription) {
|
if (hasDescription) {
|
||||||
const QString headerCssTemplate =
|
QString descriptionMarkdown;
|
||||||
";margin-top:%1;margin-bottom:%2;padding-top:0;padding-bottom:0;";
|
|
||||||
const QString h4Css = fontToCssProperties(uiFont(UiElementH4))
|
|
||||||
+ headerCssTemplate.arg(0).arg(SpacingTokens::VGapL);
|
|
||||||
const QString h5Css = fontToCssProperties(uiFont(UiElementH5))
|
|
||||||
+ headerCssTemplate.arg(SpacingTokens::ExVPaddingGapXl)
|
|
||||||
.arg(SpacingTokens::VGapL);
|
|
||||||
QString descriptionHtml;
|
|
||||||
for (const TextData::Type &text : textData) {
|
for (const TextData::Type &text : textData) {
|
||||||
if (text.second.isEmpty())
|
if (!text.first.isEmpty()) {
|
||||||
continue;
|
const QLatin1String headingMark(descriptionMarkdown.isEmpty() ? "#" : "\n\n##");
|
||||||
const QString paragraph =
|
descriptionMarkdown.append(headingMark + " " + text.first + "\n");
|
||||||
QString::fromLatin1("<div style=\"%1\">%2</div>%3")
|
|
||||||
.arg(descriptionHtml.isEmpty() ? h4Css : h5Css)
|
|
||||||
.arg(text.first)
|
|
||||||
.arg(toContentParagraph(text.second.join("<br/>")));
|
|
||||||
descriptionHtml.append(paragraph);
|
|
||||||
}
|
}
|
||||||
descriptionHtml.prepend(QString::fromLatin1("<body style=\"color:%1;\">")
|
descriptionMarkdown.append(text.second.join("\n"));
|
||||||
.arg(creatorColor(Theme::Token_Text_Default).name()));
|
}
|
||||||
descriptionHtml.append("</body>");
|
m_description->setText(markdownToHtml(descriptionMarkdown));
|
||||||
m_description->setText(descriptionHtml);
|
|
||||||
}
|
}
|
||||||
m_description->setVisible(hasDescription);
|
m_description->setVisible(hasDescription);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user