From 1ad78f5f29ef00abae4f718265fa4bc0c35b60f9 Mon Sep 17 00:00:00 2001 From: Topi Reinio <topi.reinio@digia.com> Date: Tue, 21 Oct 2014 10:22:45 +0200 Subject: [PATCH] qdoc: Prepare QDoc for the new style on qt.io The new template and CSS have some requirements that need changes in the generated .html: - Generate a new div 'sidebar' and place the TOC (if one exists) inside it, allowing the template to extend the sidebar contents dynamically. Do this for all pages except index.html. - Change the DOCTYPE declaration to be html5-compliant - Replace <tt> tags with <code> to be html5-compliant - Add a new config variable HTML.prologue - this allows the template to insert custom html into beginning of the page, before the page title but after any navigation or table-of-contents items. - Wrap tables inside <div> elements. This allows for better-working CSS design for small-screen devices. - Write out extra parameters first when outputting function synopsis to have better styling. - Inject zero-width-space characters into function names to allow the browser break up long function signatures in a nice manner. - Edit the CSS for the offline style to adapt to above changes. Task-number: QTBUG-42086 Change-Id: I3075cdc11bcb07a66150388519263fd721c8002b Reviewed-by: Martin Smith <martin.smith@digia.com> --- doc/global/template/style/offline.css | 19 ++++- src/tools/qdoc/cppcodemarker.cpp | 12 +-- src/tools/qdoc/htmlgenerator.cpp | 109 +++++++++++++++----------- src/tools/qdoc/htmlgenerator.h | 3 + 4 files changed, 90 insertions(+), 53 deletions(-) diff --git a/doc/global/template/style/offline.css b/doc/global/template/style/offline.css index 126df9d806f..dc0a6d1ec92 100644 --- a/doc/global/template/style/offline.css +++ b/doc/global/template/style/offline.css @@ -359,7 +359,9 @@ h3.fn, span.fn { margin: 0px; margin-top: 45px; } - +h3.fn code { + float: right; +} h3.fn:target { background-color: #F6F6D6; } @@ -705,8 +707,21 @@ Landing page float: left; } -.icons1of3 h2 { +.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; +} diff --git a/src/tools/qdoc/cppcodemarker.cpp b/src/tools/qdoc/cppcodemarker.cpp index 48218da5134..92b6ccca6af 100644 --- a/src/tools/qdoc/cppcodemarker.cpp +++ b/src/tools/qdoc/cppcodemarker.cpp @@ -136,7 +136,7 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, if ((style == Detailed) && !node->parent()->name().isEmpty() && (node->type() != Node::Property) && !node->isQmlNode()) - name.prepend(taggedNode(node->parent()) + "::"); + name.prepend(taggedNode(node->parent()) + "::​"); switch (node->type()) { case Node::Namespace: @@ -212,7 +212,7 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, bracketed += "slot"; } if (!bracketed.isEmpty()) - extra += " [" + bracketed.join(' ') + QLatin1Char(']'); + extra += QLatin1Char('[') + bracketed.join(' ') + QStringLiteral("] "); } break; case Node::Enum: @@ -283,13 +283,13 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, if (style == Summary) { if (node->status() == Node::Preliminary) { - extra += " (preliminary)"; + extra += "(preliminary) "; } else if (node->status() == Node::Deprecated) { - extra += " (deprecated)"; + extra += "(deprecated) "; } else if (node->status() == Node::Obsolete) { - extra += " (obsolete)"; + extra += "(obsolete) "; } } @@ -297,7 +297,7 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, extra.prepend("<@extra>"); extra.append("</@extra>"); } - return synopsis + extra; + return extra + synopsis; } /*! diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index 5b8ccd9902d..bd8d45ab534 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -121,7 +121,7 @@ void HtmlGenerator::initializeGenerator(const Config &config) { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" }, { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" }, { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" }, - { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" }, + { ATOM_FORMATTING_TELETYPE, "<code>", "</code>" }, // <tt> tag is not supported in HTML5 { ATOM_FORMATTING_UICONTROL, "<b>", "</b>" }, { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" }, { 0, 0, 0 } @@ -149,6 +149,10 @@ void HtmlGenerator::initializeGenerator(const Config &config) postPostHeader = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTPOSTHEADER); + prologue = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_PROLOGUE); + footer = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER); @@ -845,7 +849,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark out() << "<dl>\n"; } else if (atom->string() == ATOM_LIST_VALUE) { - out() << "<table class=\"valuelist\">"; + out() << "<div class=\"table\"><table class=\"valuelist\">"; threeColumnEnumValueTable_ = isThreeColumnEnumValueTable(atom); if (threeColumnEnumValueTable_) { if (++numTableRows_ % 2 == 1) @@ -897,7 +901,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark // ### Trenton QString t= protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(),relative))); - out() << "<tr><td class=\"topAlign\"><tt>" << t << "</tt>"; + out() << "<tr><td class=\"topAlign\"><code>" << t << "</code>"; if (relative->type() == Node::Enum) { out() << "</td><td class=\"topAlign\">"; @@ -907,7 +911,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark if (itemValue.isEmpty()) out() << '?'; else - out() << "<tt>" << protectEnc(itemValue) << "</tt>"; + out() << "<code>" << protectEnc(itemValue) << "</code>"; } skipAhead = 1; } @@ -952,7 +956,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark out() << "</dl>\n"; } else if (atom->string() == ATOM_LIST_VALUE) { - out() << "</table>\n"; + out() << "</table></div>\n"; } else { out() << "</ol>\n"; @@ -1040,7 +1044,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark else if (p2.contains("%")) width = p2; } - out() << "<table class=\"" << attr << "\""; + out() << "<div class=\"table\"><table class=\"" << attr << "\""; if (!width.isEmpty()) out() << " width=\"" << width << "\""; out() << ">\n "; @@ -1048,7 +1052,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark } break; case Atom::TableRight: - out() << "</table>\n"; + out() << "</table></div>\n"; break; case Atom::TableHeaderLeft: out() << "<thead><tr class=\"qt-style\">"; @@ -1456,8 +1460,7 @@ void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker) Generate the TOC for the new doc format. Don't generate a TOC for the home page. */ - if ((dn->name() != QString("index.html")) && - (dn->name() != QString("qtexamplesandtutorials.html"))) + if ((dn->name() != QStringLiteral("index.html"))) generateTableOfContents(dn,marker,0); generateTitle(fullTitle, @@ -1707,8 +1710,8 @@ void HtmlGenerator::generateHeader(const QString& title, #else out() << QString("<?xml version=\"1.0\"?>\n"); #endif - out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; - out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage); + out() << "<!DOCTYPE html>\n"; + out() << QString("<html lang=\"%1\">\n").arg(naturalLanguage); out() << "<head>\n"; out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"; if (node && !node->doc().location().isEmpty()) @@ -1775,7 +1778,8 @@ void HtmlGenerator::generateHeader(const QString& title, out() << QString(postHeader).replace("\\" + COMMAND_VERSION, qdb_->version()); generateNavigationBar(title,node,marker); - out() << "<li id=\"buildversion\">\n" << buildversion << "</li>\n"; + if (!buildversion.isEmpty()) + out() << "<li id=\"buildversion\">" << buildversion << "</li>\n"; out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, qdb_->version()); navigationLinks.clear(); @@ -1849,6 +1853,7 @@ void HtmlGenerator::generateTitle(const QString& title, const Node *relative, CodeMarker *marker) { + out() << QString(prologue).replace("\\" + COMMAND_VERSION, qdb_->version()); if (!title.isEmpty()) out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n"; if (!subTitle.isEmpty()) { @@ -1993,7 +1998,7 @@ void HtmlGenerator::generateRequisites(InnerNode *inner, CodeMarker *marker) if (!requisites.isEmpty()) { //generate the table - out() << "<table class=\"alignedsummary\">\n"; + out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; QStringList::ConstIterator i; for (i = requisiteorder.begin(); i != requisiteorder.constEnd(); ++i) { @@ -2011,7 +2016,7 @@ void HtmlGenerator::generateRequisites(InnerNode *inner, CodeMarker *marker) out() << "</td></tr>"; } } - out() << "</table>"; + out() << "</table></div>"; } } @@ -2112,7 +2117,7 @@ void HtmlGenerator::generateQmlRequisites(QmlClassNode *qcn, CodeMarker *marker) if (!requisites.isEmpty()) { //generate the table - out() << "<table class=\"alignedsummary\">\n"; + out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; QStringList::ConstIterator i; for (i = requisiteorder.begin(); i != requisiteorder.constEnd(); ++i) { @@ -2130,7 +2135,7 @@ void HtmlGenerator::generateQmlRequisites(QmlClassNode *qcn, CodeMarker *marker) out() << "</td></tr>"; } } - out() << "</table>"; + out() << "</table></div>"; } } @@ -2176,12 +2181,10 @@ void HtmlGenerator::generateTableOfContents(const Node *node, QList<Atom*> toc; if (node->doc().hasTableOfContents()) toc = node->doc().tableOfContents(); - if (toc.isEmpty() && !sections && !node->isModule()) - return; - - //turn off table of contents if HTML.tocdepth is set to 0 - if (tocDepth == 0) + if (tocDepth == 0 || (toc.isEmpty() && !sections && !node->isModule())) { + generateSidebar(); return; + } QStringList sectionNumber; int detailsBase = 0; @@ -2190,6 +2193,7 @@ void HtmlGenerator::generateTableOfContents(const Node *node, inContents_ = true; inLink_ = true; + out() << "<div class=\"sidebar\">\n"; out() << "<div class=\"toc\">\n"; out() << "<h3><a name=\"toc\">Contents</a></h3>\n"; sectionNumber.append("1"); @@ -2287,10 +2291,21 @@ void HtmlGenerator::generateTableOfContents(const Node *node, } out() << "</ul>\n"; out() << "</div>\n"; + out() << "<div class=\"sidebar-content\" id=\"sidebar-content\"></div>"; + out() << "</div>\n"; inContents_ = false; inLink_ = false; } +/*! + Outputs a placeholder div where the style can add customized sidebar content. + */ +void HtmlGenerator::generateSidebar() { + out() << "<div class=\"sidebar\">"; + out() << "<div class=\"sidebar-content\" id=\"sidebar-content\"></div>"; + out() << "</div>\n"; +} + QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker) { @@ -2307,6 +2322,7 @@ QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, beginSubPage(inner, fileName); QString title = "List of All Members for " + inner->name(); generateHeader(title, inner, marker); + generateSidebar(); generateTitle(title, Text(), SmallSubTitle, inner, marker); out() << "<p>This is the complete list of members for "; generateFullName(inner, 0); @@ -2338,6 +2354,7 @@ QString HtmlGenerator::generateAllQmlMembersFile(QmlClassNode* qml_cn, CodeMarke beginSubPage(qml_cn, fileName); QString title = "List of All Members for " + qml_cn->name(); generateHeader(title, qml_cn, marker); + generateSidebar(); generateTitle(title, Text(), SmallSubTitle, qml_cn, marker); out() << "<p>This is the complete list of members for "; generateFullName(qml_cn, 0); @@ -2422,6 +2439,7 @@ QString HtmlGenerator::generateLowStatusMemberFile(InnerNode *inner, beginSubPage(inner, fileName); generateHeader(title, inner, marker); + generateSidebar(); generateTitle(title, Text(), SmallSubTitle, inner, marker); if (status == CodeMarker::Compat) { @@ -2498,6 +2516,7 @@ QString HtmlGenerator::generateQmlMemberFile(QmlClassNode* qcn, beginSubPage(qcn, fileName); generateHeader(title, qcn, marker); + generateSidebar(); generateTitle(title, Text(), SmallSubTitle, qcn, marker); out() << "<p><b>The following members of QML type " @@ -2615,7 +2634,7 @@ void HtmlGenerator::generateAnnotatedList(const Node *relative, } if (allInternal) return; - out() << "<table class=\"annotated\">\n"; + out() << "<div class=\"table\"><table class=\"annotated\">\n"; int row = 0; NodeList nodes = nm.values(); foreach (const Node* node, nodes) { @@ -2651,7 +2670,7 @@ void HtmlGenerator::generateAnnotatedList(const Node *relative, } out() << "</tr>\n"; } - out() << "</table>\n"; + out() << "</table></div>\n"; } /*! @@ -2902,8 +2921,8 @@ void HtmlGenerator::generateQmlItem(const Node *node, if (summary) marked.replace("@name>", "b>"); - marked.replace("<@extra>", "<tt>"); - marked.replace("</@extra>", "</tt>"); + marked.replace("<@extra>", "<code>"); + marked.replace("</@extra>", "</code>"); if (summary) { marked.remove("<@type>"); @@ -3027,11 +3046,11 @@ void HtmlGenerator::generateSection(const NodeList& nl, alignNames = false; } if (alignNames) { - out() << "<table class=\"alignedsummary\">\n"; + out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; } else { if (twoColumn) - out() << "<table class=\"propsummary\">\n" + out() << "<div class=\"table\"><table class=\"propsummary\">\n" << "<tr><td class=\"topAlign\">"; out() << "<ul>\n"; } @@ -3062,11 +3081,11 @@ void HtmlGenerator::generateSection(const NodeList& nl, ++m; } if (alignNames) - out() << "</table>\n"; + out() << "</table></div>\n"; else { out() << "</ul>\n"; if (twoColumn) - out() << "</td></tr>\n</table>\n"; + out() << "</td></tr>\n</table></div>\n"; } } } @@ -3088,11 +3107,11 @@ void HtmlGenerator::generateSectionList(const Section& section, alignNames = false; } if (alignNames) { - out() << "<table class=\"alignedsummary\">\n"; + out() << "<div class=\"table\"><table class=\"alignedsummary\">\n"; } else { if (twoColumn) - out() << "<table class=\"propsummary\">\n" + out() << "<div class=\"table\"><table class=\"propsummary\">\n" << "<tr><td class=\"topAlign\">"; out() << "<ul>\n"; } @@ -3128,11 +3147,11 @@ void HtmlGenerator::generateSectionList(const Section& section, ++m; } if (alignNames) - out() << "</table>\n"; + out() << "</table></div>\n"; else { out() << "</ul>\n"; if (twoColumn) - out() << "</td></tr>\n</table>\n"; + out() << "</td></tr>\n</table></div>\n"; } } @@ -3196,8 +3215,8 @@ void HtmlGenerator::generateSynopsis(const Node *node, extraRegExp.setMinimal(true); marked.remove(extraRegExp); } else { - marked.replace("<@extra>", "<tt>"); - marked.replace("</@extra>", "</tt>"); + marked.replace("<@extra>", "<code>"); + marked.replace("</@extra>", "</code>"); } if (style != CodeMarker::Detailed) { @@ -4058,7 +4077,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node); NodeList::ConstIterator p = qpgn->childNodes().constBegin(); out() << "<div class=\"qmlproto\">"; - out() << "<table class=\"qmlname\">"; + out() << "<div class=\"table\"><table class=\"qmlname\">"; QString heading = qpgn->name() + " group"; out() << "<tr valign=\"top\" class=\"even\" id=\"" << nodeRef << "\">"; @@ -4083,13 +4102,13 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, } ++p; } - out() << "</table>"; + out() << "</table></div>"; out() << "</div>"; } else if (node->type() == Node::QmlProperty) { qpn = static_cast<QmlPropertyNode*>(node); out() << "<div class=\"qmlproto\">"; - out() << "<table class=\"qmlname\">"; + out() << "<div class=\"table\"><table class=\"qmlname\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">"; out() << "<td class=\"tblQmlPropNode\"><p>"; out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; @@ -4103,43 +4122,43 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node, out() << "<span class=\"qmldefault\">default</span>"; generateQmlItem(qpn, relative, marker, false); out() << "</p></td></tr>"; - out() << "</table>"; + out() << "</table></div>"; out() << "</div>"; } else if (node->type() == Node::QmlSignal) { const FunctionNode* qsn = static_cast<const FunctionNode*>(node); out() << "<div class=\"qmlproto\">"; - out() << "<table class=\"qmlname\">"; + out() << "<div class=\"table\"><table class=\"qmlname\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">"; out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<a name=\"" + refForNode(qsn) + "\"></a>"; generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false); out() << "</p></td></tr>"; - out() << "</table>"; + out() << "</table></div>"; out() << "</div>"; } else if (node->type() == Node::QmlSignalHandler) { const FunctionNode* qshn = static_cast<const FunctionNode*>(node); out() << "<div class=\"qmlproto\">"; - out() << "<table class=\"qmlname\">"; + out() << "<div class=\"table\"><table class=\"qmlname\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">"; out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<a name=\"" + refForNode(qshn) + "\"></a>"; generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false); out() << "</p></td></tr>"; - out() << "</table>"; + out() << "</table></div>"; out() << "</div>"; } else if (node->type() == Node::QmlMethod) { const FunctionNode* qmn = static_cast<const FunctionNode*>(node); out() << "<div class=\"qmlproto\">"; - out() << "<table class=\"qmlname\">"; + out() << "<div class=\"table\"><table class=\"qmlname\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">"; out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<a name=\"" + refForNode(qmn) + "\"></a>"; generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false); out() << "</p></td></tr>"; - out() << "</table>"; + out() << "</table></div>"; out() << "</div>"; } out() << "<div class=\"qmldoc\">"; diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index 616a9893614..40360da02ef 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -144,6 +144,7 @@ private: void generateTableOfContents(const Node *node, CodeMarker *marker, QList<Section>* sections = 0); + void generateSidebar(); QString generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker); QString generateAllQmlMembersFile(QmlClassNode* qml_cn, CodeMarker* marker); @@ -238,6 +239,7 @@ private: QString endHeader; QString postHeader; QString postPostHeader; + QString prologue; QString footer; QString address; bool pleaseGenerateMacRef; @@ -272,6 +274,7 @@ public: #define HTMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me #define HTMLGENERATOR_POSTHEADER "postheader" #define HTMLGENERATOR_POSTPOSTHEADER "postpostheader" +#define HTMLGENERATOR_PROLOGUE "prologue" #define HTMLGENERATOR_NONAVIGATIONBAR "nonavigationbar" #define HTMLGENERATOR_NOSUBDIRS "nosubdirs" #define HTMLGENERATOR_TOCDEPTH "tocdepth" -- GitLab