diff --git a/src/plugins/help/CMakeLists.txt b/src/plugins/help/CMakeLists.txt index 05d2b81152a..41dac7e5622 100644 --- a/src/plugins/help/CMakeLists.txt +++ b/src/plugins/help/CMakeLists.txt @@ -17,6 +17,7 @@ add_qtc_plugin(Help helpviewer.cpp helpviewer.h helpwidget.cpp helpwidget.h localhelpmanager.cpp localhelpmanager.h + offline-dark.css openpagesmanager.cpp openpagesmanager.h openpagesswitcher.cpp openpagesswitcher.h openpageswidget.cpp openpageswidget.h diff --git a/src/plugins/help/help.qrc b/src/plugins/help/help.qrc index b00e06409b4..84b6776caaa 100644 --- a/src/plugins/help/help.qrc +++ b/src/plugins/help/help.qrc @@ -8,5 +8,6 @@ images/mode_help_mask@2x.png images/macos_touchbar_help.png images/macos_touchbar_help@2x.png + offline-dark.css diff --git a/src/plugins/help/litehtmlhelpviewer.cpp b/src/plugins/help/litehtmlhelpviewer.cpp index 0e8a9939017..f966c018f92 100644 --- a/src/plugins/help/litehtmlhelpviewer.cpp +++ b/src/plugins/help/litehtmlhelpviewer.cpp @@ -9,8 +9,10 @@ #include #include +#include #include +#include #include #include #include @@ -24,15 +26,55 @@ using namespace Help::Internal; const int kMaxHistoryItems = 20; -static QByteArray getData(const QUrl &url) +static void setLight(QWidget *widget) { - // TODO: this is just a hack for Qt documentation + QPalette p = widget->palette(); + p.setColor(QPalette::Base, Qt::white); + p.setColor(QPalette::Text, Qt::black); + widget->setPalette(p); +} + +static void setPaletteFromTheme(QWidget *widget) +{ + if (Utils::creatorTheme()) + widget->setPalette(Utils::creatorTheme()->palette()); +} + +static bool isDarkTheme() +{ + return Utils::creatorTheme() && Utils::creatorTheme()->flag(Utils::Theme::DarkUserInterface); +} + +static QByteArray getData(const QUrl &url, QWidget *widget) +{ + // This is a hack for Qt documentation, // which decides to use a simpler CSS if the viewer does not have JavaScript - // which was a hack to decide if we are viewing in QTextBrowser or QtWebEngine et al + // which was a hack to decide if we are viewing in QTextBrowser or QtWebEngine et al. + // Force it to use the "normal" offline CSS even without JavaScript, since litehtml can + // handle that, and inject a dark themed CSS into Qt documentation for dark Qt Creator themes QUrl actualUrl = url; QString path = url.path(QUrl::FullyEncoded); static const char simpleCss[] = "/offline-simple.css"; if (path.endsWith(simpleCss)) { + if (isDarkTheme()) { + // check if dark CSS is shipped with documentation + QString darkPath = path; + darkPath.replace(simpleCss, "/offline-dark.css"); + actualUrl.setPath(darkPath); + LocalHelpManager::HelpData data = LocalHelpManager::helpData(actualUrl); + if (!data.resolvedUrl.isValid() || data.data.isEmpty()) { + // fallback + QFile css(":/help/offline-dark.css"); + if (css.open(QIODevice::ReadOnly)) + data.data = css.readAll(); + } + if (!data.data.isEmpty()) { + // we found the dark style + // set background dark (by using theme palette) + setPaletteFromTheme(widget); + return data.data; + } + } path.replace(simpleCss, "/offline.css"); actualUrl.setPath(path); } @@ -44,7 +86,7 @@ LiteHtmlHelpViewer::LiteHtmlHelpViewer(QWidget *parent) : HelpViewer(parent) , m_viewer(new QLiteHtmlWidget) { - m_viewer->setResourceHandler([](const QUrl &url) { return getData(url); }); + m_viewer->setResourceHandler([this](const QUrl &url) { return getData(url, this); }); m_viewer->setFrameStyle(QFrame::NoFrame); m_viewer->viewport()->installEventFilter(this); connect(m_viewer, &QLiteHtmlWidget::linkClicked, this, [this](const QUrl &url) { @@ -113,6 +155,8 @@ void LiteHtmlHelpViewer::setSource(const QUrl &url) void LiteHtmlHelpViewer::setHtml(const QString &html) { + // We control the html, so use theme palette + setPaletteFromTheme(this); m_viewer->setUrl({"about:invalid"}); m_viewer->setHtml(html); } @@ -252,8 +296,12 @@ void LiteHtmlHelpViewer::setSourceInternal(const QUrl &url, std::optional v QUrl newUrlWithoutFragment = url; newUrlWithoutFragment.setFragment({}); m_viewer->setUrl(url); - if (currentUrlWithoutFragment != newUrlWithoutFragment) - m_viewer->setHtml(QString::fromUtf8(getData(url))); + if (currentUrlWithoutFragment != newUrlWithoutFragment) { + // We do not expect the documentation to support dark themes, so start with light palette. + // We override this if we find Qt's dark style + setLight(this); + m_viewer->setHtml(QString::fromUtf8(getData(url, this))); + } if (vscroll) m_viewer->verticalScrollBar()->setValue(*vscroll); else diff --git a/src/plugins/help/offline-dark.css b/src/plugins/help/offline-dark.css new file mode 100644 index 00000000000..559811b5295 --- /dev/null +++ b/src/plugins/help/offline-dark.css @@ -0,0 +1,770 @@ +body { + font: normal 400 14px/1.2 Arial; + margin-top: 50px; + font-family: Arial, Helvetica; + text-align: left; + margin-left: 5px; + margin-right: 5px; + background-color: #2E2F30; + color: #d0d0d0 +} + +p { + line-height: 20px +} + +img { + margin-left: 0px; + max-width: 800px; + height: auto; + filter: brightness(85%); +} + +.content .border img { + box-shadow:3px 3px 8px 3px rgba(200,200,200,0.5) +} + +.content .border .player { + box-shadow:3px 3px 8px 3px rgba(200,200,200,0.5) +} + +.content .indexboxcont li { + font: normal bold 13px/1 Verdana + } + +.content .normallist li { + font: normal 13px/1 Verdana + } + +.descr { + margin-top: 35px; + margin-bottom: 45px; + margin-left: 5px; + text-align: left; + vertical-align: top; +} + +.name { + max-width: 75%; + font-weight: 100; +} + +tt { + text-align: left +} + +/* +----------- +links +----------- +*/ + +a:link { + color: #9a9ef9; + text-decoration: none; + text-align: left; +} + +a.qa-mark:target:before { + content: "***"; + color: #ff0000; +} + +a:hover { + color: #9a9ef9; + text-decoration:underline; + text-align: left; +} + +a:visited { + color: #9a9ef9; + text-align: left; +} + +a:visited:hover { + color: #9a9ef9; + text-align: left; +} + +/* +----------- +offline viewing: HTML links display an icon +----------- +*/ + +a[href*="http://"]::before, +a[href*="ftp://"]::before, +a[href*="https://"]::before { + content: url(../images/ico_out.png); + padding-right: 5px; +} + +.video a { + background: none; +} + +.flags { + text-decoration: none; + text-height: 24px; +} + +.flags:target { + background-color: #FFFFD6; +} + +/* +------------------------------- +NOTE styles +------------------------------- +*/ +.admonition { + padding: 5px 0 5px 40px; + border: #404142 1px solid; +} + +.admonition.note, .admonition.important { + background: #353637 3px 6px no-repeat url(../images/ico_note.png); +} + +.admonition.warning { + background: #353637 3px 6px no-repeat url(../images/ico_note_attention.png); +} +/* +------------------------------- +Top navigation +------------------------------- +*/ + +.qtref { + display: block; + position: relative; + height: 15px; + z-index: 1; + font-size: 11px; + padding-right: 10px; + float: right; +} + +.naviNextPrevious { + clear: both; + display: block; + position: relative; + text-align: right; + top: -30px; + float: right; + height: 20px; + z-index: 1; + padding-right: 10px; + padding-top: 2px; + vertical-align: top; + margin: 0px; +} + +.naviNextPrevious > a:first-child { + background-image: url(../images/btn_prev.png); + background-repeat: no-repeat; + background-position: left; + padding-left: 20px; + height: 20px; + padding-left: 20px; + } + +.naviNextPrevious > a:last-child { + background-image: url(../images/btn_next.png); + background-repeat: no-repeat; + background-position: right; + padding-right: 20px; + height: 20px; + margin-left: 30px; + } + +.naviSeparator { display: none } +/* +----------- +footer and license +----------- +*/ + +.footer { + text-align: left; + padding-top: 45px; + padding-left: 5px; + margin-top: 45px; + margin-bottom: 45px; + font-size: 10px; + border-top: 1px solid #404142; +} + +.footer p { + line-height: 14px; + font-size: 11px; + padding: 0; + margin: 0; +} + +.footer a[href*="http://"], a[href*="ftp://"], a[href*="https://"] { + font-weight: bold; +} + +.footerNavi { + width: auto; + text-align: right; + margin-top: 50px; + z-index: 1; +} + +.navigationbar { + display: block; + position: relative; + border-top: 1px solid #404142; + border-bottom: 1px solid #404142; + background-color: #353637; + z-index: 1; + height: 20px; + padding-left: 7px; + margin: 0px; + padding-top: 2px; + margin-left: -5px; + margin-right: -5px; +} + +.navigationbar .first { + background: url(../images/home.png); + background-position: left; + background-repeat: no-repeat; + padding-left: 20px; + } + +.navigationbar ul { + margin: 0px; + padding: 0px; + } + + .navigationbar ul li { + list-style-type: none; + padding-top: 2px; + padding-left: 4px; + margin: 0; + height: 20px; + } + +.navigationbar li { + float: left + } + + .navigationbar li a, .navigationbar td a { + display: block; + text-decoration: none; + background: url(../images/arrow_bc.png); + background-repeat: no-repeat; + background-position: right; + padding-right: 17px; + } + +table.buildversion { + float: right; + margin-top: -18px !important; +} + +.navigationbar table { + border-radius: 0; + border: 0 none; + background-color: #F2F2F2; + margin: 0; +} + +.navigationbar table td { + padding: 0; + border: 0 none; +} + +#buildversion { + font-style: italic; + float: right; + margin-right: 5px; +} + +#buildversion a { + background: none; +} + +/* + +/* table of content +no display +*/ + +/* +----------- +headers +----------- +*/ + +@media screen { + .title { + color: #d0d0d0; + font-size: 20px; + font-weight: normal; + left: 0; + padding-bottom: 15px; + padding-left: 10px; + padding-top: 15px; + position: absolute; + right: 0; + top: 0; + background-color: #2E2F30; + border-bottom: 1px #404142 solid; + font-weight: bold; + margin-left: 0px; + margin-right: 0px; + } + .subtitle, .small-subtitle { + display: block; + clear: left; + } +} + +h1 { + margin: 0 +} + +h2, p.h2 { + font: 500 16px/1.2 Arial; + font-weight: 100; + background-color: #353637; + padding: 4px; + margin-bottom: 15px; + margin-top: 30px; + border-top: #404142 1px solid; + border-bottom: #404142 1px solid; + max-width: 99%; +} + +h2:target { + background-color: #F2F3D4; +} + +h3 { + font: 500 14px/1.2 Arial; + font-weight: 100; + text-decoration: underline; + margin-bottom: 15px; + margin-top: 30px; +} + +h3.fn, span.fn { + border-width: 1px; + border-style: solid; + border-color: #404142; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #353637; + word-spacing: 3px; + padding: 5px 5px; + text-decoration: none; + font-weight: bold; + max-width: 75%; + font-size: 14px; + margin: 0px; + margin-top: 30px; +} +.fngroup h3.fngroupitem { + margin-bottom: 5px; +} +h3.fn code { + float: right; +} +h3.fn:target { + background-color: #F6F6D6; +} + +.name { + color: #d0d0d0 +} + +.type { + color: #a4a6a8 +} + +/* +----------------- +table styles +----------------- +*/ + +.table img { + border: none; + margin-left: 0px; + -moz-box-shadow: 0px 0px 0px #2E2F30; + -webkit-box-shadow: 0px 0px 0px #2E2F30; + box-shadow: 0px 0px 0px #2E2F30; +} + +/* table with border alternative colors*/ + +table, pre, .LegaleseLeft { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #353637; + border: 1px solid #404142; + border-collapse: separate; + margin-bottom: 25px; + margin-left: 15px; + font-size: 12px; + line-height: 1.2; +} + + table tr.even { + background-color: #2E2F30; + color: #a4a6a8; + } + + table tr.odd { + background-color: #353637; + color: #a4a6a8; + } + + table tr:target { + background-color: #F6F6D6; + } + + table thead { + text-align: left; + padding-left: 20px; + background-color: #2E2F30; + border-left: none; + border-right: none; + } + + table thead th { + padding-top: 5px; + padding-left: 10px; + padding-bottom: 5px; + border-bottom: 2px solid #404142; + padding-right: 10px; + } + + table th { + text-align: left; + padding-left: 20px; + } + + table td { + padding: 3px 15px 3px 20px; + border-bottom: #404142 dotted 1px; + } + + table p { + margin: 0px + } + +.LegaleseLeft { + font-family: monospace; + white-space: pre-wrap; +} +/* table bodless & white*/ + +.borderless { + border-radius: 0px 0px 0px 0px; + background-color: #2E2F30; + border: 1px solid #2E2F30; +} + +.borderless tr { + background-color: #2E2F30; + color: #d0d0d0; + } + +.borderless td { + border: none; + border-bottom: #2E2F30 dotted 1px; + } + +/* +----------- +List +----------- +*/ + +ul { + margin-top: 10px; +} + +li { + margin-bottom: 10px; + padding-left: 8px; + list-style: outside; + text-align: left; +} + + ul > li { + list-style-type: square; + } + +ol { + margin: 10px; + padding: 0; +} + +ol.A > li { + list-style-type: upper-alpha; +} + +ol.a > li{ + list-style-type: lower-alpha; +} + +ol > li { + margin-left: 30px; + padding-left: 8px; + list-style-type: decimal; +} + +ol.A > li { + list-style-type: upper-alpha; +} + +ol.a > li { + list-style-type: lower-alpha; +} + +ol.i > li { + list-style-type: lower-roman; +} + +ol.I > li { + list-style-type: upper-roman; +} + +.centerAlign { + text-align: left +} + +.cpp, .LegaleseLeft { + display: block; + margin: 10px; + overflow: auto; + padding: 20px 20px 20px 20px; +} + +.js { + display: block; + margin: 10px; + overflow: auto; + padding: 20px 20px 20px 20px; +} + +.memItemLeft { + padding-right: 3px +} + +.memItemRight { + padding: 3px 15px 3px 0 +} + +.qml { + display: block; + margin: 10px; + overflow: auto; + padding: 20px 20px 20px 20px; +} + +.qmlextra { + padding-left: 5px; + float: right; + color: #254117; +} + +.rightAlign { + padding: 3px 5px 3px 10px; + text-align: right; +} + +.qmldoc { + margin-left: 15px +} + +.flowList { + padding: 25px +} +.flowList dd { + display: inline-block; + margin-left: 10px; + width: 255px; + line-height: 1.15em; + overflow-x: hidden; + text-overflow: ellipsis +} +.alphaChar { + font-size: 2em; + position: relative +} +/* +----------- +Content table +----------- +*/ + +@media screen { + .toc { + float: right; + clear: right; + vertical-align: top; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background: #353637; + background-position: top; + background-repeat: repeat-x; + border: 1px solid #404142; + padding-left: 5px; + padding-bottom: 10px; + height: auto; + width: 200px; + text-align: left; + margin-left: 20px; + margin-top: 5px; + } +} + + +.toc h3 { + text-decoration: none +} + +.toc h3 { + font: 500 14px/1.2 Arial; + font-weight: 100; + padding: 0px; + margin: 0px; + padding-top: 5px; + padding-left: 5px; +} + +.toc ul { + padding-left: 10px; + padding-right: 5px; +} + +.toc ul li { + margin-left: 15px; + marker-offset: 0px; + margin-bottom: 8px; + padding-left: 0px; + } + +.toc .level1 { + border: none +} + +.toc .level2 { + border: none; + margin-left: 25px; +} + +.level3 { + border: none; + margin-left: 30px; +} + +.clearfix { + clear: both +} + +/* +----------- +Landing page +----------- +*/ + +.col-group { + white-space: nowrap; + vertical-align: top; +} + + +.landing h2 { + background-color: transparent; + border: none; + margin-bottom: 0px; + font-size: 18px; +} + +.landing a, .landing li { + font-size: 13px; + font-weight: bold !important; +} + +.col-1 { + display: inline-block; + white-space: normal; + width: 70%; + height: 100%; + float: left; +} + +.col-2 { + display: inline-block; + white-space: normal; + width: 20%; + margin-left: 5%; + position: relative; + top: -20px; +} + +.col-1 h1 { + margin: 20px 0 0 0; + } + +.col-1 h2 { + font-size: 18px; + font-weight: bold !important; +} + +.landingicons { + display: inline-block; + width: 100%; +} + +.icons1of3 { + display: inline-block; + width: 33.3333%; + float: left; +} + +.icons1of3 h2, .doc-column h2 { + font-size: 15px; + margin: 0px; + padding: 0px; +} + +div.multi-column { + position: relative; +} + +div.multi-column div { + display: -moz-inline-box; + display: inline-block; + vertical-align: top; + margin-top: 1em; + margin-right: 4em; + width: 24em; +} + +.mainContent .video { + width:40%; + max-width:640px; + margin: 15px 0 0 15px; + position:relative; + display:table +} + +.mainContent .video iframe { + width:100%; + height:100%; + position:absolute; + top:0; + left:0 +}