qdoc: Highlight selected section / entry in the documentation

This commit adds id tags with the anchor reference as the value
to html entities for documentation section titles and function
signatures, properties etc. for both C++ and QML documentation
pages.

Together with new CSS rules, we can dynamically highlight the
title that the user clicked on. This helps to locate the item
of interest on a crowded page or when the page cannot be
scrolled down enough to place the selected item on top.

Change-Id: I7d1db2ed4e12779e1a9e571996ee65c3befa4e7a
Reviewed-by: Martin Smith <martin.smith@digia.com>
Reviewed-by: Venugopal Shivashankar <venugopal.shivashankar@digia.com>
This commit is contained in:
Topi Reinio 2014-09-02 15:52:35 +02:00 committed by Topi Reiniö
parent 675d354815
commit 2e667c9171
3 changed files with 86 additions and 36 deletions

View File

@ -97,6 +97,10 @@ a[href*="http://"], a[href*="ftp://"], a[href*="https://"] {
text-height: 24px; text-height: 24px;
} }
.flags:target {
background-color: #FFFFD6;
}
/* /*
------------------------------- -------------------------------
NOTE styles NOTE styles
@ -327,6 +331,10 @@ h2, p.h2 {
max-width: 99%; max-width: 99%;
} }
h2:target {
background-color: #F2F3D4;
}
h3 { h3 {
font: 500 14px/1.2 Arial; font: 500 14px/1.2 Arial;
font-weight: 100; font-weight: 100;
@ -353,6 +361,10 @@ h3.fn, span.fn {
margin-top: 45px; margin-top: 45px;
} }
h3.fn:target {
background-color: #F6F6D6;
}
.name { .name {
color: #1A1A1A color: #1A1A1A
} }
@ -413,6 +425,10 @@ table, pre {
color: #66666E; color: #66666E;
} }
table tr:target {
background-color: #F6F6D6;
}
table thead { table thead {
text-align: left; text-align: left;
padding-left: 20px; padding-left: 20px;

View File

@ -48,6 +48,10 @@ links
text-height: 24px; text-height: 24px;
} }
.flags:target {
background-color: #FFFFD6;
}
/* /*
------------------------------- -------------------------------
NOTE styles NOTE styles
@ -204,6 +208,10 @@ h2, p.h2 {
overflow: hidden; overflow: hidden;
} }
h2:target {
background-color: #F2F3D4;
}
h3 { h3 {
font: 500 14px/1.2 Arial; font: 500 14px/1.2 Arial;
font-weight: 100; font-weight: 100;
@ -212,6 +220,10 @@ h3 {
margin-top: 30px; margin-top: 30px;
} }
h3.fn:target {
background-color: #F6F6D6;
}
h3.fn, span.fn { h3.fn, span.fn {
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
@ -293,6 +305,10 @@ table, pre {
color: #66666E; color: #66666E;
} }
table tr:target {
background-color: #F6F6D6;
}
table thead { table thead {
text-align: left; text-align: left;
padding-left: 20px; padding-left: 20px;

View File

@ -996,10 +996,16 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark
break; break;
case Atom::SectionRight: case Atom::SectionRight:
break; break;
case Atom::SectionHeadingLeft: case Atom::SectionHeadingLeft: {
out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>'); int unit = atom->string().toInt() + hOffset(relative);
out() << "<h" + QString::number(unit) + QLatin1Char(' ');
if (unit < 3) {
out() << "id=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) << "\"";
}
out() << ">";
inSectionHeading_ = true; inSectionHeading_ = true;
break; break;
}
case Atom::SectionHeadingRight: case Atom::SectionHeadingRight:
out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n"; out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n";
inSectionHeading_ = false; inSectionHeading_ = false;
@ -1217,10 +1223,9 @@ void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
else { else {
if (!s->members.isEmpty()) { if (!s->members.isEmpty()) {
// out() << "<hr />\n"; // out() << "<hr />\n";
out() << "<a name=\"" QString ref = registerRef((*s).name.toLower());
<< registerRef((*s).name.toLower()) out() << "<a name=\"" << ref << "\"></a>" << divNavTop << "\n";
<< "\"></a>" << divNavTop << "\n"; out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n";
generateSection(s->members, inner, marker, CodeMarker::Summary); generateSection(s->members, inner, marker, CodeMarker::Summary);
} }
if (!s->reimpMembers.isEmpty()) { if (!s->reimpMembers.isEmpty()) {
@ -1255,13 +1260,14 @@ void HtmlGenerator::generateClassLikeNode(InnerNode* inner, CodeMarker* marker)
out() << "</ul>\n"; out() << "</ul>\n";
} }
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; QString detailsRef = registerRef("details");
out() << "<a name=\"" << detailsRef << "\"></a>" << divNavTop << '\n';
if (!inner->doc().isEmpty()) { if (!inner->doc().isEmpty()) {
generateExtractionMark(inner, DetailedDescriptionMark); generateExtractionMark(inner, DetailedDescriptionMark);
//out() << "<hr />\n" //out() << "<hr />\n"
out() << "<div class=\"descr\">\n" // QTBUG-9504 out() << "<div class=\"descr\">\n" // QTBUG-9504
<< "<h2>" << "Detailed Description" << "</h2>\n"; << "<h2 id=\"" << detailsRef << "\">" << "Detailed Description" << "</h2>\n";
generateBody(inner, marker); generateBody(inner, marker);
out() << "</div>\n"; // QTBUG-9504 out() << "</div>\n"; // QTBUG-9504
generateAlsoList(inner, marker); generateAlsoList(inner, marker);
@ -1366,16 +1372,18 @@ void HtmlGenerator::generateQmlTypePage(QmlClassNode* qcn, CodeMarker* marker)
s = sections.constBegin(); s = sections.constBegin();
while (s != sections.constEnd()) { while (s != sections.constEnd()) {
out() << "<a name=\"" << registerRef((*s).name.toLower()) QString ref = registerRef((*s).name.toLower());
out() << "<a name=\"" << ref
<< "\"></a>" << divNavTop << '\n'; << "\"></a>" << divNavTop << '\n';
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
generateQmlSummary(*s, qcn, marker); generateQmlSummary(*s, qcn, marker);
++s; ++s;
} }
generateExtractionMark(qcn, DetailedDescriptionMark); generateExtractionMark(qcn, DetailedDescriptionMark);
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; QString detailsRef = registerRef("details");
out() << "<h2>" << "Detailed Description" << "</h2>\n"; out() << "<a name=\"" << detailsRef << "\"></a>" << divNavTop << '\n';
out() << "<h2 id=\"" << detailsRef << "\">" << "Detailed Description" << "</h2>\n";
generateBody(qcn, marker); generateBody(qcn, marker);
ClassNode* cn = qcn->classNode(); ClassNode* cn = qcn->classNode();
if (cn) if (cn)
@ -1500,8 +1508,9 @@ void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker)
sections = marker->sections(dn, CodeMarker::Summary, CodeMarker::Okay); sections = marker->sections(dn, CodeMarker::Summary, CodeMarker::Okay);
s = sections.constBegin(); s = sections.constBegin();
while (s != sections.constEnd()) { while (s != sections.constEnd()) {
out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n'; QString ref = registerRef((*s).name);
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
generateSectionList(*s, dn, marker, CodeMarker::Summary); generateSectionList(*s, dn, marker, CodeMarker::Summary);
++s; ++s;
} }
@ -1539,6 +1548,7 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke
QList<Section> sections; QList<Section> sections;
QList<Section>::const_iterator s; QList<Section>::const_iterator s;
QString fullTitle = cn->fullTitle(); QString fullTitle = cn->fullTitle();
QString ref;
generateHeader(fullTitle, cn, marker); generateHeader(fullTitle, cn, marker);
generateTableOfContents(cn,marker,0); generateTableOfContents(cn,marker,0);
@ -1553,15 +1563,17 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke
NodeMap nm; NodeMap nm;
cn->getMemberNamespaces(nm); cn->getMemberNamespaces(nm);
if (!nm.isEmpty()) { if (!nm.isEmpty()) {
out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n'; ref = registerRef("namespaces");
out() << "<h2>Namespaces</h2>\n"; out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
out() << "<h2 id=\"" << ref << "\">Namespaces</h2>\n";
generateAnnotatedList(cn, marker, nm); generateAnnotatedList(cn, marker, nm);
} }
nm.clear(); nm.clear();
cn->getMemberClasses(nm); cn->getMemberClasses(nm);
if (!nm.isEmpty()) { if (!nm.isEmpty()) {
out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n'; ref = registerRef("classes");
out() << "<h2>Classes</h2>\n"; out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
out() << "<h2 id=\"" << ref << "\">Classes</h2>\n";
generateAnnotatedList(cn, marker, nm); generateAnnotatedList(cn, marker, nm);
} }
nm.clear(); nm.clear();
@ -1570,8 +1582,9 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke
sections = marker->sections(cn, CodeMarker::Summary, CodeMarker::Okay); sections = marker->sections(cn, CodeMarker::Summary, CodeMarker::Okay);
s = sections.constBegin(); s = sections.constBegin();
while (s != sections.constEnd()) { while (s != sections.constEnd()) {
out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n'; ref = registerRef((*s).name);
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
generateSectionList(*s, cn, marker, CodeMarker::Summary); generateSectionList(*s, cn, marker, CodeMarker::Summary);
++s; ++s;
} }
@ -1579,9 +1592,10 @@ void HtmlGenerator::generateCollectionNode(CollectionNode* cn, CodeMarker* marke
Text brief = cn->doc().briefText(); Text brief = cn->doc().briefText();
if (cn->isModule() && !brief.isEmpty()) { if (cn->isModule() && !brief.isEmpty()) {
generateExtractionMark(cn, DetailedDescriptionMark); generateExtractionMark(cn, DetailedDescriptionMark);
out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; ref = registerRef("details");
out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
out() << "<div class=\"descr\">\n"; // QTBUG-9504 out() << "<div class=\"descr\">\n"; // QTBUG-9504
out() << "<h2>" << "Detailed Description" << "</h2>\n"; out() << "<h2 id=\"" << ref << "\">" << "Detailed Description" << "</h2>\n";
} }
else { else {
generateExtractionMark(cn, DetailedDescriptionMark); generateExtractionMark(cn, DetailedDescriptionMark);
@ -2509,9 +2523,10 @@ QString HtmlGenerator::generateQmlMemberFile(QmlClassNode* qcn,
QList<Section>::const_iterator s = sections.constBegin(); QList<Section>::const_iterator s = sections.constBegin();
while (s != sections.constEnd()) { while (s != sections.constEnd()) {
out() << "<a name=\"" << registerRef((*s).name.toLower()) QString ref = registerRef((*s).name.toLower());
out() << "<a name=\"" << ref
<< "\"></a>" << divNavTop << '\n'; << "\"></a>" << divNavTop << '\n';
out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; out() << "<h2 id=\"" << ref << "\">" << protectEnc((*s).name) << "</h2>\n";
generateQmlSummary(*s, qcn, marker); generateQmlSummary(*s, qcn, marker);
++s; ++s;
} }
@ -3815,13 +3830,14 @@ void HtmlGenerator::generateDetailedMember(const Node *node,
generateMacRef(node, marker); generateMacRef(node, marker);
#endif #endif
generateExtractionMark(node, MemberMark); generateExtractionMark(node, MemberMark);
QString nodeRef = refForNode(node);
if (node->type() == Node::Enum if (node->type() == Node::Enum
&& (enume = static_cast<const EnumNode *>(node))->flagsType()) { && (enume = static_cast<const EnumNode *>(node))->flagsType()) {
#ifdef GENERATE_MAC_REFS #ifdef GENERATE_MAC_REFS
generateMacRef(enume->flagsType(), marker); generateMacRef(enume->flagsType(), marker);
#endif #endif
out() << "<h3 class=\"flags\">"; out() << "<h3 class=\"flags\" id=\"" << nodeRef << "\">";
out() << "<a name=\"" + refForNode(node) + "\"></a>"; out() << "<a name=\"" + nodeRef + "\"></a>";
generateSynopsis(enume, relative, marker, CodeMarker::Detailed); generateSynopsis(enume, relative, marker, CodeMarker::Detailed);
out() << "<br/>"; out() << "<br/>";
generateSynopsis(enume->flagsType(), generateSynopsis(enume->flagsType(),
@ -3831,8 +3847,8 @@ void HtmlGenerator::generateDetailedMember(const Node *node,
out() << "</h3>\n"; out() << "</h3>\n";
} }
else { else {
out() << "<h3 class=\"fn\">"; out() << "<h3 class=\"fn\" id=\"" << nodeRef << "\">";
out() << "<a name=\"" + refForNode(node) + "\"></a>"; out() << "<a name=\"" + nodeRef + "\"></a>";
generateSynopsis(node, relative, marker, CodeMarker::Detailed); generateSynopsis(node, relative, marker, CodeMarker::Detailed);
out() << "</h3>" << divNavTop << '\n'; out() << "</h3>" << divNavTop << '\n';
} }
@ -4051,6 +4067,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
#endif #endif
generateExtractionMark(node, MemberMark); generateExtractionMark(node, MemberMark);
out() << "<div class=\"qmlitem\">"; out() << "<div class=\"qmlitem\">";
QString nodeRef = refForNode(node);
if (node->type() == Node::QmlPropertyGroup) { if (node->type() == Node::QmlPropertyGroup) {
const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node); const QmlPropertyGroupNode* qpgn = static_cast<const QmlPropertyGroupNode*>(node);
NodeList::ConstIterator p = qpgn->childNodes().constBegin(); NodeList::ConstIterator p = qpgn->childNodes().constBegin();
@ -4058,17 +4075,18 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
out() << "<table class=\"qmlname\">"; out() << "<table class=\"qmlname\">";
QString heading = qpgn->name() + " group"; QString heading = qpgn->name() + " group";
out() << "<tr valign=\"top\" class=\"even\">"; out() << "<tr valign=\"top\" class=\"even\" id=\"" << nodeRef << "\">";
out() << "<th class=\"centerAlign\"><p>"; out() << "<th class=\"centerAlign\"><p>";
out() << "<a name=\"" + refForNode(qpgn) + "\"></a>"; out() << "<a name=\"" + nodeRef + "\"></a>";
out() << "<b>" << heading << "</b>"; out() << "<b>" << heading << "</b>";
out() << "</p></th></tr>"; out() << "</p></th></tr>";
while (p != qpgn->childNodes().constEnd()) { while (p != qpgn->childNodes().constEnd()) {
if ((*p)->type() == Node::QmlProperty) { if ((*p)->type() == Node::QmlProperty) {
qpn = static_cast<QmlPropertyNode*>(*p); qpn = static_cast<QmlPropertyNode*>(*p);
out() << "<tr valign=\"top\" class=\"odd\">"; nodeRef = refForNode(qpn);
out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
out() << "<td class=\"tblQmlPropNode\"><p>"; out() << "<td class=\"tblQmlPropNode\"><p>";
out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; out() << "<a name=\"" + nodeRef + "\"></a>";
if (!qpn->isWritable()) if (!qpn->isWritable())
out() << "<span class=\"qmlreadonly\">read-only</span>"; out() << "<span class=\"qmlreadonly\">read-only</span>";
@ -4086,7 +4104,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
qpn = static_cast<QmlPropertyNode*>(node); qpn = static_cast<QmlPropertyNode*>(node);
out() << "<div class=\"qmlproto\">"; out() << "<div class=\"qmlproto\">";
out() << "<table class=\"qmlname\">"; out() << "<table class=\"qmlname\">";
out() << "<tr valign=\"top\" class=\"odd\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
out() << "<td class=\"tblQmlPropNode\"><p>"; out() << "<td class=\"tblQmlPropNode\"><p>";
out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; out() << "<a name=\"" + refForNode(qpn) + "\"></a>";
if (!qpn->isReadOnlySet()) { if (!qpn->isReadOnlySet()) {
@ -4106,7 +4124,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
const FunctionNode* qsn = static_cast<const FunctionNode*>(node); const FunctionNode* qsn = static_cast<const FunctionNode*>(node);
out() << "<div class=\"qmlproto\">"; out() << "<div class=\"qmlproto\">";
out() << "<table class=\"qmlname\">"; out() << "<table class=\"qmlname\">";
out() << "<tr valign=\"top\" class=\"odd\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<td class=\"tblQmlFuncNode\"><p>";
out() << "<a name=\"" + refForNode(qsn) + "\"></a>"; out() << "<a name=\"" + refForNode(qsn) + "\"></a>";
generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false); generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false);
@ -4118,7 +4136,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
const FunctionNode* qshn = static_cast<const FunctionNode*>(node); const FunctionNode* qshn = static_cast<const FunctionNode*>(node);
out() << "<div class=\"qmlproto\">"; out() << "<div class=\"qmlproto\">";
out() << "<table class=\"qmlname\">"; out() << "<table class=\"qmlname\">";
out() << "<tr valign=\"top\" class=\"odd\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<td class=\"tblQmlFuncNode\"><p>";
out() << "<a name=\"" + refForNode(qshn) + "\"></a>"; out() << "<a name=\"" + refForNode(qshn) + "\"></a>";
generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false); generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false);
@ -4130,7 +4148,7 @@ void HtmlGenerator::generateDetailedQmlMember(Node *node,
const FunctionNode* qmn = static_cast<const FunctionNode*>(node); const FunctionNode* qmn = static_cast<const FunctionNode*>(node);
out() << "<div class=\"qmlproto\">"; out() << "<div class=\"qmlproto\">";
out() << "<table class=\"qmlname\">"; out() << "<table class=\"qmlname\">";
out() << "<tr valign=\"top\" class=\"odd\">"; out() << "<tr valign=\"top\" class=\"odd\" id=\"" << nodeRef << "\">";
out() << "<td class=\"tblQmlFuncNode\"><p>"; out() << "<td class=\"tblQmlFuncNode\"><p>";
out() << "<a name=\"" + refForNode(qmn) + "\"></a>"; out() << "<a name=\"" + refForNode(qmn) + "\"></a>";
generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false); generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false);