diff --git a/Doxyfile b/Doxyfile index 9b9aac614..e36f0d70d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1375,7 +1375,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = doc/doxygen/html/header.html # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank Doxygen will generate a standard @@ -1385,7 +1385,7 @@ HTML_HEADER = # that Doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = doc/doxygen/html/footer.html # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1415,7 +1415,7 @@ HTML_STYLESHEET = # documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = doxygen_style.css +HTML_EXTRA_STYLESHEET = doc/doxygen/css/doxygen_style.css # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1425,7 +1425,7 @@ HTML_EXTRA_STYLESHEET = doxygen_style.css # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = doc/doxygen/js/graph_toggle.js # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. diff --git a/doc/doxygen/css/doxygen_style.css b/doc/doxygen/css/doxygen_style.css new file mode 100644 index 000000000..617359ea8 --- /dev/null +++ b/doc/doxygen/css/doxygen_style.css @@ -0,0 +1,88 @@ +/* === Accent Tuning for Doxygen Dark Mode === */ + +/* Main accent color */ +:root { + --main-accent: #33a946; +} + +/* Links */ +a, a:visited { + color: var(--main-accent) !important; +} + +a:hover { + color: #4fd467 !important; /* lighter green */ +} + +/* Project title */ +#projectname { + color: var(--main-accent) !important; +} + +/* Current sidebar item */ +#side-nav .selected { + border-left: 3px solid var(--main-accent) !important; +} + +/* Tabs */ +.tablist li.current { + background: var(--main-accent) !important; + color: #fff !important; +} + +/* Search box focus */ +#MSearchField:focus { + outline: 1px solid var(--main-accent) !important; +} + +/* Code block border highlight */ +pre, code { + border: 1px solid #333; +} + +pre.fragment { + border-color: var(--main-accent) !important; +} + +/* Graph toggle (caller / callee two-button) */ +.graph-toggle { + display: inline-flex; + margin: 0.5em 0; + border: 1px solid #444; + border-radius: 6px; + overflow: hidden; + background: #111; +} + +.graph-toggle button { + flex: 1 1 50%; + padding: 0.35em 0.8em; + background: transparent; + color: #aaa; + border: 0; + cursor: pointer; + font-size: 0.95em; +} + +.graph-toggle button:hover:not(:disabled) { + background: #222; + color: #fff; +} + +.graph-toggle button.active { + background: var(--main-accent); + color: #fff; +} + +.graph-toggle button:disabled, +.graph-toggle button.disabled { + opacity: 0.45; + cursor: default; +} + +/* small accessibility focus ring */ +.graph-toggle button:focus { + outline: 2px solid rgba(51, 169, 70, 0.3); + outline-offset: 1px; +} + diff --git a/doc/doxygen/html/footer.html b/doc/doxygen/html/footer.html new file mode 100644 index 000000000..8ea57bf3b --- /dev/null +++ b/doc/doxygen/html/footer.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/doc/doxygen/html/header.html b/doc/doxygen/html/header.html new file mode 100644 index 000000000..261581add --- /dev/null +++ b/doc/doxygen/html/header.html @@ -0,0 +1,81 @@ + + + + + + + + + $projectname: $title + $title + + + + + + + + + + + + + $treeview + $search + $mathjax + $darkmode + + $extrastylesheet + + + +
+ + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber + +
+ +
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/doc/doxygen/js/graph_toggle.js b/doc/doxygen/js/graph_toggle.js new file mode 100644 index 000000000..3d8fa83d0 --- /dev/null +++ b/doc/doxygen/js/graph_toggle.js @@ -0,0 +1,147 @@ +document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll(".memdoc").forEach(memdoc => { + let callContent = null, callHeader = null; + let callerContent = null, callerHeader = null; + + memdoc.querySelectorAll("div.dynheader").forEach(header => { + const text = (header.textContent || "").trim().toLowerCase(); + let content = header.nextElementSibling; + let tries = 0; + while (content && !content.classList.contains("dyncontent") && tries < 8) { + content = content.nextElementSibling; + tries++; + } + if (!content) return; + + if (text.includes("caller")) { + callerContent = content; + callerHeader = header; + } else if (text.includes("call graph") || text.includes("callgraph") || text.includes("call graph for")) { + callContent = content; + callHeader = header; + } else if (text.includes("call")) { + if (!callContent) { + callContent = content; + callHeader = header; + } + } + }); + + if (!callContent && !callerContent) return; + if (memdoc.querySelector(".graph-toggle")) return; + + const toggle = document.createElement("div"); + toggle.className = "graph-toggle"; + + const callerBtn = document.createElement("button"); + callerBtn.type = "button"; + callerBtn.textContent = "Caller Graph"; + const callBtn = document.createElement("button"); + callBtn.type = "button"; + callBtn.textContent = "Call Graph"; + + toggle.appendChild(callerBtn); + toggle.appendChild(callBtn); + + const firstHeader = memdoc.querySelector("div.dynheader"); + memdoc.insertBefore(toggle, firstHeader || memdoc.firstChild); + + // hide everything initially + if (callerContent) { + callerContent.style.display = "none"; + callerHeader.style.display = "none"; + } + if (callContent) { + callContent.style.display = "none"; + callHeader.style.display = "none"; + } + + // disable missing buttons + if (!callerContent) { + callerBtn.disabled = true; + callerBtn.classList.add("disabled"); + } + if (!callContent) { + callBtn.disabled = true; + callBtn.classList.add("disabled"); + } + + // track current state + let current = null; // "caller", "call", "both", null=hidden + + function setActive(type) { + if (type === "caller") { + if (current === "caller") { // hide it + if (callerContent) { + callerContent.style.display = "none"; + callerHeader.style.display = "none"; + } + current = null; + } else if (current === "call") { // show both + if (callerContent) { + callerContent.style.display = "block"; + callerHeader.style.display = "block"; + } + current = "both"; + } else if (current === "both") { // hide caller only → call only + if (callerContent) { + callerContent.style.display = "none"; + callerHeader.style.display = "none"; + } + current = "call"; + } else { // nothing visible → show caller + if (callerContent) { + callerContent.style.display = "block"; + callerHeader.style.display = "block"; + } + if (callContent) { + callContent.style.display = "none"; + callHeader.style.display = "none"; + } + current = "caller"; + } + } else if (type === "call") { + if (current === "call") { // hide it + if (callContent) { + callContent.style.display = "none"; + callHeader.style.display = "none"; + } + current = null; + } else if (current === "caller") { // show both + if (callContent) { + callContent.style.display = "block"; + callHeader.style.display = "block"; + } + current = "both"; + } else if (current === "both") { // hide call only → caller only + if (callContent) { + callContent.style.display = "none"; + callHeader.style.display = "none"; + } + current = "caller"; + } else { // nothing visible → show call only + if (callContent) { + callContent.style.display = "block"; + callHeader.style.display = "block"; + } + if (callerContent) { + callerContent.style.display = "none"; + callerHeader.style.display = "none"; + } + current = "call"; + } + } + + // update button styles + callerBtn.classList.toggle("active", current === "caller" || current === "both"); + callBtn.classList.toggle("active", current === "call" || current === "both"); + } + + callerBtn.addEventListener("click", () => { + if (!callerBtn.disabled) setActive("caller"); + }); + callBtn.addEventListener("click", () => { + if (!callBtn.disabled) setActive("call"); + }); + }); +}); diff --git a/doxygen_style.css b/doxygen_style.css deleted file mode 100644 index b9ee841dd..000000000 --- a/doxygen_style.css +++ /dev/null @@ -1,45 +0,0 @@ -/* === Accent Tuning for Doxygen Dark Mode === */ - -/* Main accent color */ -:root { - --main-accent: #33a946; -} - -/* Links */ -a, a:visited { - color: var(--main-accent) !important; -} - -a:hover { - color: #4fd467 !important; /* lighter green */ -} - -/* Project title */ -#projectname { - color: var(--main-accent) !important; -} - -/* Current sidebar item */ -#side-nav .selected { - border-left: 3px solid var(--main-accent) !important; -} - -/* Tabs */ -.tablist li.current { - background: var(--main-accent) !important; - color: #fff !important; -} - -/* Search box focus */ -#MSearchField:focus { - outline: 1px solid var(--main-accent) !important; -} - -/* Code block border highlight */ -pre, code { - border: 1px solid #333; -} - -pre.fragment { - border-color: var(--main-accent) !important; -}