From 9c142581f1520a0134f3888b8e7359e5ee0d91f5 Mon Sep 17 00:00:00 2001
From: Arkerthan <arkerthan@gmail.com>
Date: Sun, 17 May 2020 16:26:17 +0200
Subject: [PATCH] add proper styling to quick menu

---
 src/003-assets/CSS/quicklinks.css |  51 ++++++++++++
 src/gui/quicklinks.js             | 133 ++++++++++++++++++++----------
 2 files changed, 142 insertions(+), 42 deletions(-)
 create mode 100644 src/003-assets/CSS/quicklinks.css

diff --git a/src/003-assets/CSS/quicklinks.css b/src/003-assets/CSS/quicklinks.css
new file mode 100644
index 00000000000..20050739652
--- /dev/null
+++ b/src/003-assets/CSS/quicklinks.css
@@ -0,0 +1,51 @@
+div.quick-links {
+    margin-bottom: 2px; /* the last menu link has a bottom outline, make enough space for it */
+}
+
+div.quick-links div.toggle {
+    position: relative; /* so ::before works in relation to this element */
+    background-color: #4c4c4c;
+}
+
+div.quick-links div.toggle:hover {
+    background-color: #7f7f7f;
+}
+
+div.quick-links div.toggle::before {
+    font-family: "tme-fa-icons";
+    position: absolute;
+    left: 5px;
+    content: "\e818";
+}
+
+div.quick-links div.collapsed div.toggle::before {
+    content: "\e81a";
+}
+
+div.quick-links div.content {
+    margin: 0 5px;
+}
+
+div.quick-links div.collapsed div.content {
+    display: none
+}
+
+div.quick-links div.menu-link {
+    background-color: #2b2b2b;
+    border: solid #3c3c3c;
+    border-width: 0 2px;
+    margin: 0 2px;
+    /* we cannot detect if an element has something after it, so we just add a shadow to every element. Only for the
+        last element the shadow is not hidden behind another element. */
+    box-shadow: 0 2px 0 0 #3c3c3c;
+}
+
+/* give the very first link an upper border */
+div.quick-links > div.menu-link:first-child {
+    border-top-width: 2px;
+}
+
+/* give the link directly after an opened toggle an upper border */
+div.quick-links div:not(.menu-link):not(.collapsed) + div.menu-link {
+    border-top-width: 2px;
+}
diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js
index 64880cd344d..a7cc8614f1e 100644
--- a/src/gui/quicklinks.js
+++ b/src/gui/quicklinks.js
@@ -10,7 +10,7 @@ App.UI.quickMenu = (function() {
 
 	// if property name is a passage name, then it's a link, otherwise only text.
 	// Only two values are allowed: true or an object following the same rules
-	const layout = {
+	const layout = addOtherCategory({
 		Main: true,
 		Manage: {
 			"Manage Personal Affairs": true,
@@ -25,8 +25,22 @@ App.UI.quickMenu = (function() {
 			Schoolroom: true,
 			"Servants' Quarters": true,
 			Spa: true
+		},
+		deepTest: {
+			Options: true,
+			deeper: {
+				"Summary Options": true,
+				"Description Options": true
+			},
+			"Costs Budget": true,
 		}
-	};
+	});
+
+	/**
+	 * The DOM element of name of the currently played passage
+	 * @type {HTMLElement}
+	 */
+	let currentPassageDOM = null;
 
 	// setup hotkeys list, upper/lower case is important!
 	const hotkeys = cleanHotkeys({
@@ -99,23 +113,16 @@ App.UI.quickMenu = (function() {
 		}
 
 		// quick menu
-		// we need to modify jumpTo, but not the original
-		const jumpToCopy = jumpTo.slice();
-		fragment.append(generateQuickMenu(jumpToCopy, layout, 0));
-
-		// put uncategorized passages in others menu
-		if (jumpToCopy.length > 0) {
-			const div = document.createElement("div");
-			div.append("Other");
-			fragment.append("0 ", div);
-			for (let i = 0; i < jumpToCopy.length; i++) {
-				if (State.passage !== jumpToCopy[i]) {
-					fragment.append(generatePassageLink(jumpToCopy[i], 1));
-				} else {
-					const div = document.createElement("div");
-					div.append("1 ", jumpToCopy[i]);
-					fragment.append(div);
-				}
+		const div = document.createElement("div");
+		div.classList.add("quick-links");
+		div.append(generateLinkList(layout));
+		fragment.append(div);
+
+		// traverse from current passage up to uncollapse.
+		if (currentPassageDOM !== null) {
+			while (!currentPassageDOM.classList.contains("quick-links")) {
+				currentPassageDOM.classList.remove("collapsed");
+				currentPassageDOM = currentPassageDOM.parentElement;
 			}
 		}
 
@@ -123,49 +130,91 @@ App.UI.quickMenu = (function() {
 		return fragment;
 	}
 
-	function generateQuickMenu(passages, group, level) {
+	function generateLinkList(group) {
 		const fragment = document.createDocumentFragment();
 		for (const passage in group) {
-			const included = jumpTo.includes(passage);
-			// remove used passage from list
-			if (included) {
-				const index = passages.indexOf(passage);
-				if (index > -1) {
-					passages.splice(index, 1);
-				}
-			}
-
-			// make link
-			if (included && State.passage !== passage) {
-				fragment.append(generatePassageLink(passage, level));
+			if (group[passage] === true) {
+				fragment.append(menuLink(passage));
 			} else {
-				const div = document.createElement("div");
-				div.append(level, " ", passage);
-				fragment.append(div);
-			}
-
-			// lower levels
-			if (group[passage] !== true) {
-				fragment.append(generateQuickMenu(passages, group[passage], level + 1));
+				fragment.append(generateCategory(passage, group[passage]));
 			}
 		}
 		return fragment;
 	}
 
-	function generatePassageLink(passage, level) {
+	function generateCategory(name, group) {
+		const toggle = document.createElement("div");
+		toggle.classList.add("toggle");
+		toggle.append(menuLink(name, true));
+		const content = document.createElement("div");
+		content.classList.add("content");
+		content.append(generateLinkList(group));
+
+		// wrap everything in one div, so we can control it by changing only one class
+		const wrapper = document.createElement("div");
+		wrapper.classList.add("collapsed");
+		toggle.onclick = () => {
+			wrapper.classList.toggle("collapsed");
+		};
+		wrapper.append(toggle, content);
+		return wrapper;
+	}
+
+	function menuLink(passage, toggle = false) {
 		const div = document.createElement("div");
+		if (!toggle) {
+			div.classList.add("menu-link");
+		}
+		// we are already on this passage
+		if (State.passage === passage) {
+			div.append(passage);
+			currentPassageDOM = div;
+			return div;
+		}
+		// That is not a passage we can safely jump to, it's a deco element
+		if (!jumpTo.includes(passage)) {
+			div.append(passage);
+			return div;
+		}
+		// Create link
 		const a = document.createElement("a");
 		a.append(passage);
 		a.onclick = () => {
 			Engine.play(passage);
 		};
-		div.append(level, " ", a);
+		div.append(a);
 		if (hotkeys[passage]) {
 			div.append(" ", App.UI.DOM.makeElement("span", `[${hotkeys[passage]}]`, "hotkey"));
 		}
 		return div;
 	}
 
+	function addOtherCategory(layout) {
+		const passages = jumpTo.slice();
+		filterPassages(passages, layout);
+
+		if (passages.length > 0) {
+			const other = {};
+			for (let i = 0; i < passages.length; i++) {
+				other[passages[i]] = true;
+			}
+			layout.Other = other;
+		}
+		return layout;
+	}
+
+	function filterPassages(passages, layout) {
+		for (const category in layout) {
+			const index = passages.indexOf(category);
+			if (index > -1) {
+				passages.splice(index, 1);
+			}
+			if (layout[category] !== true) {
+				filterPassages(passages, layout[category]);
+			}
+		}
+	}
+
 	/**
 	 * Cleans out all keys that are not contained in jumpTo and therefore not considered safe.
 	 *
-- 
GitLab