From 4b0f4050cca98c6f757313c6c4c782d22c65334a Mon Sep 17 00:00:00 2001 From: GGodPL <46885632+GGodPL@users.noreply.github.com> Date: Fri, 21 Jul 2023 15:42:32 +0200 Subject: [PATCH] add betterMenuScreens mod adds betterMenuScreens utility mod --- mods/betterMenuScreens.js | 229 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 mods/betterMenuScreens.js diff --git a/mods/betterMenuScreens.js b/mods/betterMenuScreens.js new file mode 100644 index 00000000..0fa8977d --- /dev/null +++ b/mods/betterMenuScreens.js @@ -0,0 +1,229 @@ +/** + * @typedef {object} MenuScreenLoader + * @property {string} name Name of the menu screen. Gets displayed on the button + * @property {string} parentDiv ID of the parent div. Should be the same as the ID provided in MenuScreen class via setParentDivId method + * @property {string} buttonDescription Description that is shown when the button is hovered + * @property {boolean} show Whether menu screen button should get shown on tool controls + * @property {() => void} [close] Closing method. Optional + * @property {() => void} [open] Opening method. Optional + * @property {() => void} [onClose] Method that gets called on close (except when menu is force closed, like when clicking on a different menu button). Optional + * @property {() => void} [loader] Method that injects the menu screen into HTML. Can be set to ModScreen build method. Optional + */ +var menuScreens = { + info: { + name: "Info", + parentDiv: "infoParent", + buttonDescription: "Brings up the element info screen", + show: true, + close: () => { + var infoParent = document.getElementById("infoParent"); + var infoSearch = document.getElementById("infoSearch"); + infoParent.style.display = "none"; + infoSearch.value = ""; + infoHistory = []; + }, + open: showInfo + }, + mods: { + name: "Mods", + parentDiv: "modParent", + buttonDescription: "Brings up the Mod Manager", + show: true, + close: () => { + var modParent = document.getElementById("modParent"); + var modManagerUrl = document.getElementById("modManagerUrl"); + modParent.style.display = "none"; + modManagerUrl.value = ""; + }, + open: showModManager + }, + settings: { + name: "Settings", + parentDiv: "settingsParent", + buttonDescription: "Brings up the settings screen", + show: true, + open: showSettings + } +} + +closeMenu = (force) => { + if (!showingMenu) return; + const menu = menuScreens[showingMenu]; + if (!menu) { + const menuParents = document.getElementsByClassName("menuParent"); + for (const elem of menuParents) elem.style.display = "none"; + showingMenu = false; + } else { + if (menu.close) menu.close(); + else { + const menuParent = document.getElementById(menu.parentDiv); + menuParent.style.display = "none"; + } + if (!force && menu.onClose) menu.onClose(); + else showingMenu = false; + } +} + +// injects into toolControls +const inject = () => { + const toolControls = document.getElementById("toolControls"); + const buttons = []; + for (const key in menuScreens) { + const element = menuScreens[key]; + if (element.show) { + const button = document.createElement("button"); + button.id = `betterMenuScreens_${key}Button`; + button.title = element.buttonDescription ?? ""; + button.onclick = () => { + if (showingMenu != key) { + closeMenu(true); + if (element.open) element.open(); + else { + const menuParent = document.getElementById(element.parentDiv); + menuParent.style.display = "block"; + showingMenu = key; + } + } else { + closeMenu(true); + } + } + button.innerText = element.name; + button.className = "controlButton"; + buttons.push(button); + } + if (element.loader) element.loader(); + } + toolControls.removeChild(document.getElementById("infoButton")); + toolControls.removeChild(document.getElementById("modsButton")); + // replace the old settings button with new buttons + document.getElementById(`settingsButton`).replaceWith(...buttons); + +} + +class MenuScreen { + constructor () { + this.nodes = []; + this.innerHtml = ""; + this.showCloseButton = true; + this.closeButtonText = "-"; + } + + /** + * Sets the screen title + * @param {string} [title] Screen title. "New Menu Screen" by default + */ + setTitle(title = "New Menu Screen") { + this.title = title; + return this; + } + + /** + * Sets close button visibility. When false the close button will not be added to the menu screen + * @param {boolean} show Visibility of the close button + */ + setShowCloseButton(show) { + this.showCloseButton = show; + } + + /** + * Sets the close button text + * @param {string} [text] Close button text. "-" by default + */ + setCloseButtonText(text = "-") { + this.closeButtonText = text; + } + + /** + * Sets the parent div ID. Has to be called at least once before build method is called + * @param {string} id Parent div ID + */ + setParentDivId(id) { + this.parentDivId = id; + return this; + } + + /** + * Sets the parent div class name. Changing the div class name is not recommended + * @param {string} [className] Parent div class name. "menuParent" by default + */ + setParentDivClass(className = "menuParent") { + this.parentDivClass = className; + return this; + } + + /** + * Sets the inner div ID. Has to be called at least once before build method is called + * @param {string} id Inner div ID + */ + setInnerDivId(id) { + this.innerDivId = id; + return this; + } + + /** + * Sets the inner div class name. Changing the div class name is not recommended + * @param {string} [className] Inner div class name. "menuScreen" by default + */ + setInnerDivClass(className = "menuScreen") { + this.innerDivClass = className; + return this; + } + + /** + * Adds a node to the menu screen content + * @param {Node|Node[]} node Any HTML node/element or array of HTML nodes/elements + */ + addNode(node) { + if (node instanceof Array) this.nodes.push(...node); + else this.nodes.push(node); + return this; + } + + /** + * Appends to menu screen contents inner html + * @param {string} html HTML code to append + */ + appendInnerHtml(html) { + this.innerHtml += html; + return this; + } + + /** + * Sets the menu screen contents inner html + * @param {string} html HTML code to set to + */ + setInnerHtml(html) { + this.innerHtml = html; + return this; + } + + /** + * Checks whether the menu screen is ready for build. That method should not be called outside of build method + * @private + */ + _check() { + if (!this.parentDivId) throw "No parent div id specified"; + if (!this.innerDivId) throw "No inner div id specified"; + } + + /** + * Builds the menu screen and appends it to chosen element + * @param {string} [id] Element id to append the menu screen to. Changing the id from default "gameDiv" is not recommended + */ + build(id = "gameDiv") { + this._check(); + const parent = document.createElement("div"); + parent.className = this.parentDivClass ?? "menuParent"; + parent.id = this.parentDivId; + parent.style.display = "none"; + const inner = document.createElement("div"); + inner.className = this.innerDivClass ?? "menuScreen"; + inner.innerHTML = `${this.showCloseButton ? ` + ${this.title ?? "Menu Screen"}

"; + inner.append(this.nodes); + parent.appendChild(inner); + document.getElementById(id).appendChild(parent); + } +} + +runAfterLoadList.push(inject); \ No newline at end of file