diff --git a/docs/src/_static/css/custom.css b/docs/src/_static/css/custom.css
index 0f1b2e541c..b236db1e3d 100644
--- a/docs/src/_static/css/custom.css
+++ b/docs/src/_static/css/custom.css
@@ -246,39 +246,89 @@ div.body {
font-size: 75%;
}
-/* These replace colors present in `pygments.css` which is used in code highlighting.
- * These are too dark to be readlable in DARK mode. They include:
- * .highlight .nf -- function names
- * .highlight .nl -- code labels
- * .descname .n -- API documentation function names
- * .highlight .p -- Punctuation
- * .highlight -- Plain text in a `.. code-block:: none` block
- * The first 2 were created by lightening the `pygments.css` colors without changing their
- * angle on the color wheel. The added attribute "conditional" also limits this change to
- * DARK MODE only instead of both light and dark modes.
- */
-/* Name.Function */
-html[data-theme="dark"] .highlight .nf {
- color: #ccd285;
+/*-------------------------------------------------------------------------
+ * Custom Banners...
+ *
+ * ...are inserted between these two elements at the top of the page:
+Skip to content
+
+
+
+
+ ...page content...
+ *-------------------------------------------------------------------------*/
+.lv-custom-banner-list {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow-x: auto;
+ border-top: 4px solid darkgreen;
}
-/* Name.Label */
-html[data-theme="dark"] .highlight .nl {
- color: #0043e2;
+.lv-custom-banner {
+ box-sizing: border-box;
+ padding: .5rem;
+ min-width: 100%;
+ text-align: center;
+ border-bottom: 4px solid darkgreen;
+ margin: 0;
}
-/* Name */
-html[data-theme="dark"] .descname .n {
- color: #0a44de;
+/* Hyperlinks within banners (when banner label had a hyperlink in it) */
+.lv-custom-banner a {
+ color: var(--color-announcement-text);
+ text-decoration-color: var(--color-announcement-text);
}
-/* Punctuation */
-html[data-theme="dark"] .highlight .p {
- color: #5c7c72
+.lv-custom-banner a:hover {
+ color: var(--color-announcement-text);
+ text-decoration-color: var(--color-link--hover);
}
-/* Plain text. */
-html[data-theme="dark"] .highlight {
- background: #ffffff; color: #bfbfbf
+/* Paragraphs within banner HTML (when Banners text contains
elements). */
+.lv-custom-banner p {
+ margin: 0;
}
+/* Banner priorities */
+.lv-custom-banner.highest-priority {
+ background-image: linear-gradient(45deg, black, darkred);
+ color: white;
+ padding: 18px 3em;
+}
+
+.lv-custom-banner.high-priority {
+ background-image: linear-gradient(45deg, black, darkgreen);
+ color: white;
+ padding: 14px 3em;
+}
+
+.lv-custom-banner.normal-priority {
+ background-image: linear-gradient(45deg, black, #5e5e5e);
+ color: white;
+ padding: 10px 3em;
+}
+
+.lv-custom-banner.low-priority {
+ background-image: linear-gradient(45deg, black, darkblue);
+ color: white;
+ padding: 8px 3em;
+}
+
+.lv-custom-banner.lowest-priority {
+ background-image: linear-gradient(45deg, black, var(--color-sidebar-background));
+ color: var(--color-content-foreground);
+ padding: 8px 3em;
+}
diff --git a/docs/src/_static/js/custom.js b/docs/src/_static/js/custom.js
index fa1327d60f..cc905bbdbc 100644
--- a/docs/src/_static/js/custom.js
+++ b/docs/src/_static/js/custom.js
@@ -1,5 +1,33 @@
-/* API collapsing */
document.addEventListener('DOMContentLoaded', (event) => {
+ /*---------------------------------------------------------------------
+ * For API pages:
+ *
+ * Documented code elements on API pages each have a structure that looks
+ * like this example for a function:
+ *
+ *
function signature // code highlighting done with child elements
+ * - documentation
// documentation, initially closed
+ *
+ *
+ * where `_state_` is either "expanded" or "unexpanded". When "unexpanded"
+ * class is present, the
element containing the documentation is hidden
+ * by CSS in `custom.css`. Without that class present (i.e. when "expanded"
+ * class is present instead), the element reverts to its normal state
+ * of being visible.
+ *
+ * The job of the code below is to:
+ * 1. Add "unexpanded" class to all elements...
+ * 2. ...except if the URL had a hash string ("...#identifier_string")
+ * that matches the immediate - child element's id attribute, in which
+ * case the "expanded" class is added instead. (This usually indicates
+ * the user navigated there by clicking a hyperlinked code element in
+ * one of the documentation pages.)
+ * 3. Add an open/close button
+ * element just before each code element to toggle its expanded/unexpanded
+ * class. (The click-able arrow icon before it is set in `custom.css`
+ * based on the
element's "expanded" or "unexpanded" class.)
+ *---------------------------------------------------------------------*/
document.querySelectorAll("dl.cpp").forEach(cppListing => {
const dt = cppListing.querySelector("dt");
let shouldBeExpanded = false;
@@ -16,42 +44,158 @@ document.addEventListener('DOMContentLoaded', (event) => {
dt.insertBefore(button, dt.firstChild);
});
- fetch('https://lvgl.io/home-banner.txt') // Replace with your URL
- .then(response => {
- // Check if the request was successful
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
- // Read the response as text
- return response.text();
- })
- .then(data => {
+ /*---------------------------------------------------------------------
+ * Display any current custom banner in `banner.json` at top of each page.
+ *---------------------------------------------------------------------
+ * Custom banners are inserted between these two elements at top of page.
+ Skip to content
- const section = document.querySelector('.wy-nav-content-wrap');
+
+
+ Important announcement one!
+
+
+ Important announcement two!
+
+
+ Important announcement three!
+
+
- //Add a div
- const newDiv = document.createElement('div');
- newDiv.style="background-image: linear-gradient(45deg, black, #5e5e5e); color: white; border-bottom: 4px solid #e10010; padding-inline:3em"
- section.insertBefore(newDiv, section.firstChild);
+
+ ...page content...
+ *---------------------------------------------------------------------*/
+ let bannerJsonUrl = 'https://lvgl.io/data/banner.json';
+ let bannerContainerClass = 'lv-custom-banner-list';
+ let bannerClass = 'lv-custom-banner';
+ /* Note: banner priority property can have only one of these values:
+ * ("highest" | "high" | "normal" | "low" | "lowest").
+ * If not present, the default is "normal-priority". This controls banner styling. */
+ let priorityPropVals = ["highest", "high", "normal", "low", "lowest"];
+ let defaultPrioPropVal = priorityPropVals[2];
+ let priorityClassSuffix = "-priority";
+ /* Sorting json banners in priority order.
+ * `a` and `b` are BANNER objects from incoming `banner.json`. */
+ function prio_compare(a, b) {
+ let aPrioPropStr = a.hasOwnProperty('priority') ? a.priority : defaultPrioPropVal;
+ let bPrioPropStr = b.hasOwnProperty('priority') ? b.priority : defaultPrioPropVal;
+ let aPrio = 0;
+ let bPrio = 0;
- //Add a p to the div
- const newP = document.createElement('p');
- newP.style="padding-block:12px; margin-block:0px;align-content: center;align-items: center;"
- newP.innerHTML = data
- newDiv.insertBefore(newP, newDiv.firstChild);
-
- const children = newDiv.querySelectorAll('*');
-
- // Iterate over each child
- children.forEach(child => {
- // Check if the child has an id
- if (child.id) {
- // Prepend 'docs-' to the id
- child.id = 'docs-' + child.id;
+ /* Establish numeric values for `a` and `b`. */
+ for (var i = 0; i < 5; i++) {
+ if (aPrioPropStr === priorityPropVals[i]) {
+ aPrio = i;
+ break;
+ }
}
- })
- }) .catch(error => {
- console.error('Fetch error: ' + error.message);
- });
-})
+
+ for (var i = 0; i < 5; i++) {
+ if (bPrioPropStr === priorityPropVals[i]) {
+ bPrio = i;
+ break;
+ }
+ }
+
+ /* Correctness Proof
+ * -----------------
+ * < 0 = `a` should come before `b`.
+ * > 0 = `a` should come after `b`.
+ * 0 or NaN = a === b.
+ *
+ * Example: a === "highest-priority"; b === "normal-priority".
+ * aPrio === 0 ; bPrio === 2.
+ * aPrio - bPrio === -2 means (`a` should come before `b`).
+ */
+ return aPrio - bPrio;
+ }
+
+ fetch(bannerJsonUrl)
+ .then(response => {
+ if (response.ok) {
+ return response.json();
+ } else {
+ /* Note: per OOSC2, it is not appropriate to throw an exception for a
+ * situation that is being checked for. Sometimes the banner file will
+ * not be there, in which case, we simply return an empty array object. */
+ return [];
+ }
+ })
+ /* JSON file was fetched successfully.... */
+ .then(json => {
+ if (!typeof json === "array") {
+ /* Data structure not recognized. */
+ } else {
+ /* console.log('JSON is an array.'); */
+ /* Does it contain any banners? */
+ if (json.length === 0) {
+ console.log('JSON has no banners -- nothing to do.');
+ } else {
+ /* Note: `div.page` is unique to Furo theme. */
+ const page = document.querySelector('div.page');
+ const pgParent = page.parentElement;
+
+ /* Create and insert banner container. */
+ const newDiv = document.createElement('div');
+ newDiv.classList.add(bannerContainerClass);
+ pgParent.insertBefore(newDiv, page);
+
+ /* Create a
or an element for each banner.
+ * First, sort them in priority order with "highest-priority" being
+ * at the top. The JSON is an ARRAY of BANNER objects.
+ * `prio_compare()` knows how to compare them.
+ *
+ * If the BANNER object has a "url" property, then
+ * encapsulate banner in an anchor element that will send
+ * user to designated URL.
+ */
+ json.sort(prio_compare);
+
+ for (var i = 0; i < json.length; i++) {
+ let banner = json[i];
+
+ if (banner.hasOwnProperty('label')) {
+ let priorityClass = '';
+ let newElement = null;
+
+ if (banner.hasOwnProperty('url')) {
+ newElement = document.createElement('a');
+ newElement.setAttribute('href', banner.url)
+ } else {
+ newElement = document.createElement('p');
+ }
+
+ if (banner.hasOwnProperty('priority')) {
+ priorityClass = banner.priority + priorityClassSuffix;
+ } else {
+ priorityClass = defaultPrioPropVal + priorityClassSuffix;
+ }
+
+ newElement.innerHTML = banner.label;
+ newElement.classList.add(bannerClass);
+ newElement.classList.add(priorityClass);
+ newDiv.appendChild(newElement);
+ }
+ }
+
+ /* Finally, we need to tell the page element that its `min-hight`
+ * is 100% minus the hight of all the banners, including the one
+ * supplied by `conf.py` in `conf.html_theme_options.announcement`
+ * if one is present === var(--header-height).
+ *
+ * This extends short pages by just enough to place [PREV] and [NEXT]
+ * buttoms and footer at bottom of page without scrolling.
+ *
+ * Note: this overrides the `min-height` property set for this
+ * element in `furo.css`, which is: calc(100% - var(--header-height)).
+ * It additionally subtracts height of banner list.
+ * */
+ let height = newDiv.offsetHeight;
+ page.style['min-height'] = `calc(100% - var(--header-height) - ${height}px)`;
+ }
+ }
+ }) .catch(error => {
+ console.error('Fetch error: ' + error.message);
+ });
+});