diff --git a/macrovision.css b/macrovision.css index 148337de..c9f63787 100644 --- a/macrovision.css +++ b/macrovision.css @@ -230,6 +230,9 @@ body.show-extra-options .options-block.options-block-optional { width: 100%; text-align: center; margin-top: 10px; + display: flex; + flex-direction: row; + flex-wrap: wrap; } .options-banner-button { @@ -246,7 +249,6 @@ body.show-extra-options .options-block.options-block-optional { border-color: #666; border-width: 3pt; border-style: outset; - min-width: 85%; max-width: 100%; } @@ -1038,4 +1040,60 @@ body.screenshot-mode .scroll-button { #ground { --ground-color: #000; background-color: var(--ground-color); +} + +.filter-holder { + user-select: none; + -webkit-user-select: none; +} + +.filter-holder > select { + font-size: 200%; + width: 100%; +} + +.filter-holder > div { + font-size: 200%; + flex-basis: 150pt; +} + +.filter-holder { + display: flex; + align-items: center; + justify-content: left space-between; + padding: 10px 20px 10px 10px; + background: gray; + border-color: darkslategray; + border-width: 5px; + border-style: solid; +} + +.filter-holder.enabled { + background: green; + border-color: darkgreen; +} + +.info-holder > div, +.info-holder > i { + font-size: 200%; + margin: 10px; +} + +.info-holder { + display: flex; + align-items: center; + justify-content: left; + padding: 10px 20px 10px 10px; + background: gray; + border-color: darkslategray; + border-width: 5px; + border-style: solid; + text-decoration: none; + user-select: none; + -webkit-user-select: none; + color: white; +} + +.info-holder:hover { + background-color: lightgray; } \ No newline at end of file diff --git a/macrovision.html b/macrovision.html index 6210f61b..4e58567a 100644 --- a/macrovision.html +++ b/macrovision.html @@ -57,6 +57,23 @@
+
+
+ +
+
+ + +
Help
+
+ + +
Submit Your Character
+
+ + +
Donate
+
-

World Info

diff --git a/macrovision.js b/macrovision.js index 1b8a9a53..1cc7d55b 100644 --- a/macrovision.js +++ b/macrovision.js @@ -1452,7 +1452,7 @@ function drawHorizontalScale(ifDirty = false) { // calling the constructor -- e.g. making a list of authors and // owners. So, this function is used to generate that information. // It is invoked like makeEntity so that it can be dropped in easily, -// but returns an object that lets you construct many copies of an entity, +// but returns an object that lets you construct many copies of an entity, // rather than creating a new entity. function createEntityMaker(info, views, sizes, forms) { const maker = {}; @@ -3679,15 +3679,6 @@ document.addEventListener("DOMContentLoaded", () => { prepareMenu(); prepareEntities(); - document.querySelector("#open-help").addEventListener("click", (e) => { - setHelpDate(); - document.querySelector("#open-help").classList.remove("highlighted"); - window.open( - "https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e", - "_blank" - ); - }); - document .querySelector("#copy-screenshot") .addEventListener("click", (e) => { @@ -3734,10 +3725,18 @@ document.addEventListener("DOMContentLoaded", () => { e.stopPropagation(); }); + document.querySelector("#sidebar-menu").addEventListener("touchstart", (e) => { + e.stopPropagation(); + }); + document.addEventListener("click", (e) => { document.querySelector("#sidebar-menu").classList.remove("visible"); }); + document.addEventListener("touchstart", (e) => { + document.querySelector("#sidebar-menu").classList.remove("visible"); + }); + document .querySelector("#toggle-settings") .addEventListener("click", (e) => { @@ -3767,10 +3766,96 @@ document.addEventListener("DOMContentLoaded", () => { e.stopPropagation(); }); + document.querySelector("#settings-menu").addEventListener("touchstart", (e) => { + e.stopPropagation(); + }); + document.addEventListener("click", (e) => { document.querySelector("#settings-menu").classList.remove("visible"); }); + document.addEventListener("touchstart", (e) => { + document.querySelector("#settings-menu").classList.remove("visible"); + }); + + document.querySelector("#toggle-filters").addEventListener("click", (e) => { + const popoutMenu = document.querySelector("#filters-menu"); + if (popoutMenu.classList.contains("visible")) { + popoutMenu.classList.remove("visible"); + } else { + document + .querySelectorAll(".popout-menu") + .forEach((menu) => menu.classList.remove("visible")); + const rect = e.target.getBoundingClientRect(); + popoutMenu.classList.add("visible"); + popoutMenu.style.left = rect.x + rect.width + 10 + "px"; + popoutMenu.style.top = rect.y + rect.height + 10 + "px"; + + let menuWidth = popoutMenu.getBoundingClientRect().width; + let screenWidth = window.innerWidth; + + if (menuWidth * 1.5 > screenWidth) { + popoutMenu.style.left = 25 + "px"; + } + } + e.stopPropagation(); + }); + + document.querySelector("#filters-menu").addEventListener("click", (e) => { + e.stopPropagation(); + }); + + document.querySelector("#filters-menu").addEventListener("touchstart", (e) => { + e.stopPropagation(); + }); + + document.addEventListener("click", (e) => { + document.querySelector("#filters-menu").classList.remove("visible"); + }); + + document.addEventListener("touchstart", (e) => { + document.querySelector("#filters-menu").classList.remove("visible"); + }); + + document.querySelector("#toggle-info").addEventListener("click", (e) => { + const popoutMenu = document.querySelector("#info-menu"); + if (popoutMenu.classList.contains("visible")) { + popoutMenu.classList.remove("visible"); + } else { + document + .querySelectorAll(".popout-menu") + .forEach((menu) => menu.classList.remove("visible")); + const rect = e.target.getBoundingClientRect(); + popoutMenu.classList.add("visible"); + popoutMenu.style.left = rect.x + rect.width + 10 + "px"; + popoutMenu.style.top = rect.y + rect.height + 10 + "px"; + + let menuWidth = popoutMenu.getBoundingClientRect().width; + let screenWidth = window.innerWidth; + + if (menuWidth * 1.5 > screenWidth) { + popoutMenu.style.left = 25 + "px"; + } + } + e.stopPropagation(); + }); + + document.querySelector("#info-menu").addEventListener("click", (e) => { + e.stopPropagation(); + }); + + document.querySelector("#info-menu").addEventListener("touchstart", (e) => { + e.stopPropagation(); + }); + + document.addEventListener("click", (e) => { + document.querySelector("#info-menu").classList.remove("visible"); + }); + + document.addEventListener("touchstart", (e) => { + document.querySelector("#info-menu").classList.remove("visible"); + }); + window.addEventListener("unload", () => { saveScene("autosave"); setUserSettings(exportUserSettings()); @@ -4845,6 +4930,8 @@ document.addEventListener("DOMContentLoaded", () => { ); } }; + + updateFilter(); }); let searchText = ""; @@ -4898,13 +4985,6 @@ function makeCustomEntity(url, x = 0.5, y = 0.5) { } const filterDefs = { - none: { - id: "none", - name: "No Filter", - extract: (maker) => [], - render: (name) => name, - sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]), - }, author: { id: "author", name: "Authors", @@ -5027,6 +5107,8 @@ const filterDefs = { }, }; +const filterStates = {}; + const sizeCategories = { atomic: math.unit(100, "angstroms"), microscopic: math.unit(100, "micrometers"), @@ -5079,20 +5161,18 @@ function prepareEntities() { return x.name.localeCompare(y.name); }); const holder = document.querySelector("#spawners"); - const filterHolder = document.querySelector("#filters"); + const filterMenu = document.querySelector("#filters-menu"); const categorySelect = document.createElement("select"); categorySelect.id = "category-picker"; - const filterSelect = document.createElement("select"); - filterSelect.id = "filter-picker"; holder.appendChild(categorySelect); - filterHolder.appendChild(filterSelect); const filterSets = {}; Object.values(filterDefs).forEach((filter) => { filterSets[filter.id] = new Set(); + filterStates[filter.id] = false; }); Object.entries(availableEntities).forEach(([category, entityList]) => { @@ -5183,76 +5263,36 @@ function prepareEntities() { }); Object.values(filterDefs).forEach((filter) => { - const option = document.createElement("option"); - option.innerText = filter.name; - option.value = filter.id; - filterSelect.appendChild(option); + const filterHolder = document.createElement("label"); + filterHolder.setAttribute("for", "filter-toggle-" + filter.id); + filterHolder.classList.add("filter-holder"); + + const filterToggle = document.createElement("input"); + filterToggle.type = "checkbox"; + filterToggle.id = "filter-toggle-" + filter.id; + filterHolder.appendChild(filterToggle); + + filterToggle.addEventListener("input", e => { + filterStates[filter.id] = filterToggle.checked + if (filterToggle.checked) { + filterHolder.classList.add("enabled"); + } else { + filterHolder.classList.remove("enabled"); + } + clearFilter(); + updateFilter(); + }); + + const filterLabel = document.createElement("div"); + filterLabel.innerText = filter.name; + filterHolder.appendChild(filterLabel); const filterNameSelect = document.createElement("select"); filterNameSelect.classList.add("filter-select"); filterNameSelect.id = "filter-" + filter.id; filterHolder.appendChild(filterNameSelect); - const button = document.createElement("button"); - button.classList.add("filter-button"); - button.id = "create-filtered-" + filter.id + "-button"; - filterHolder.appendChild(button); - - const counter = document.createElement("div"); - counter.classList.add("button-counter"); - counter.innerText = "10"; - button.appendChild(counter); - const i = document.createElement("i"); - i.classList.add("fas"); - i.classList.add("fa-plus"); - button.appendChild(i); - - button.addEventListener("click", (e) => { - const makers = Array.from( - document.querySelector(".entity-select.category-visible") - ).filter((element) => !element.classList.contains("filtered")); - const count = makers.length + 2; - let index = 1; - - if (makers.length > 50) { - if ( - !confirm( - "Really spawn " + makers.length + " things at once?" - ) - ) { - return; - } - } - - const worldWidth = - (config.height.toNumber("meters") / canvasHeight) * canvasWidth; - - const spawned = makers.map((element) => { - const category = - document.querySelector("#category-picker").value; - const maker = availableEntities[category][element.value]; - const entity = maker.constructor(); - displayEntity( - entity, - entity.view, - -worldWidth * 0.45 + - config.x + - (worldWidth * 0.9 * index) / (count - 1), - config.y - ); - index += 1; - return entityIndex - 1; - }); - updateSizes(true); - - if (config.autoFitAdd) { - let targets = {}; - spawned.forEach((key) => { - targets[key] = entities[key]; - }); - fitEntities(targets); - } - }); + filterMenu.appendChild(filterHolder); Array.from(filterSets[filter.id]) .map((name) => [name, filter.render(name)]) @@ -5269,6 +5309,14 @@ function prepareEntities() { }); }); + const spawnButton = document.createElement("button"); + spawnButton.id = "spawn-all" + spawnButton.addEventListener("click", e => { + spawnAll(); + }); + + filterMenu.appendChild(spawnButton); + console.log( "Loaded " + Object.keys(availableEntitiesByName).length + " entities" ); @@ -5298,20 +5346,49 @@ function prepareEntities() { recomputeFilters(); - filterSelect.addEventListener("input", (e) => { - const oldSelect = document.querySelector( - ".filter-select.category-visible" - ); - if (oldSelect) oldSelect.classList.remove("category-visible"); + ratioInfo = document.body.querySelector(".extra-info"); +} - const newSelect = document.querySelector("#filter-" + e.target.value); - if (newSelect && e.target.value != "none") - newSelect.classList.add("category-visible"); +function spawnAll() { + const makers = Array.from( + document.querySelector(".entity-select.category-visible") + ).filter((element) => !element.classList.contains("filtered")); + const count = makers.length + 2; + let index = 1; - updateFilter(); + if (makers.length > 50) { + if (!confirm("Really spawn " + makers.length + " things at once?")) { + return; + } + } + + const worldWidth = + (config.height.toNumber("meters") / canvasHeight) * canvasWidth; + + const spawned = makers.map((element) => { + const category = document.querySelector("#category-picker").value; + const maker = availableEntities[category][element.value]; + const entity = maker.constructor(); + displayEntity( + entity, + entity.view, + -worldWidth * 0.45 + + config.x + + (worldWidth * 0.9 * index) / (count - 1), + config.y + ); + index += 1; + return entityIndex - 1; }); + updateSizes(true); - ratioInfo = document.body.querySelector(".extra-info"); + if (config.autoFitAdd) { + let targets = {}; + spawned.forEach((key) => { + targets[key] = entities[key]; + }); + fitEntities(targets); + } } // Only display authors and owners if they appear @@ -5325,75 +5402,64 @@ function recomputeFilters() { filterSets[filter.id] = new Set(); }); - document - .querySelectorAll(".entity-select.category-visible > option") - .forEach((element) => { - const entity = availableEntities[category][element.value]; - - Object.values(filterDefs).forEach((filter) => { - filter.extract(entity).forEach((result) => { - filterSets[filter.id].add(result); - }); + availableEntities[category].forEach((entity) => { + Object.values(filterDefs).forEach((filter) => { + filter.extract(entity).forEach((result) => { + filterSets[filter.id].add(result); }); }); + }); Object.values(filterDefs).forEach((filter) => { + filterStates[filter.id] = false; + document.querySelector("#filter-toggle-" + filter.id).checked = false + document.querySelector("#filter-toggle-" + filter.id).dispatchEvent(new Event("click")) // always show the "none" option let found = filter.id == "none"; + const filterSelect = document.querySelector("#filter-" + filter.id); + const filterSelectHolder = filterSelect.parentElement; + filterSelect.querySelectorAll("option").forEach((element) => { + if ( + filterSets[filter.id].has(element.value) || + filter.id == "none" + ) { + element.classList.remove("filtered"); + element.disabled = false; + found = true; + } else { + element.classList.add("filtered"); + element.disabled = true; + } + }); - document - .querySelectorAll("#filter-" + filter.id + " > option") - .forEach((element) => { - if ( - filterSets[filter.id].has(element.value) || - filter.id == "none" - ) { - element.classList.remove("filtered"); - element.disabled = false; - found = true; - } else { - element.classList.add("filtered"); - element.disabled = true; - } - }); - - const filterOption = document.querySelector( - "#filter-picker > option[value='" + filter.id + "']" - ); if (found) { - filterOption.classList.remove("filtered"); - filterOption.disabled = false; + filterSelectHolder.style.display = ""; } else { - filterOption.classList.add("filtered"); - filterOption.disabled = true; + filterSelectHolder.style.display = "none"; } }); - - document.querySelector("#filter-picker").value = "none"; - document.querySelector("#filter-picker").dispatchEvent(new Event("input")); } function updateFilter() { const category = document.querySelector("#category-picker").value; - const type = document.querySelector("#filter-picker").value; - const filterKeySelect = document.querySelector( - ".filter-select.category-visible" - ); - clearFilter(); + const types = Object.values(filterDefs).filter(def => filterStates[def.id]).map(def => def.id) + + const keys = { - const noFilter = !filterKeySelect; + } + + types.forEach(type => { + const filterKeySelect = document.querySelector("#filter-" + type); + keys[type] = filterKeySelect.value; + }) + + clearFilter(); - let key; let current = document.querySelector( ".entity-select.category-visible" ).value; - if (!noFilter) { - key = filterKeySelect.value; - current; - } - let replace = current == ""; let first = null; @@ -5403,16 +5469,18 @@ function updateFilter() { document .querySelectorAll(".entity-select.category-visible > option") .forEach((element) => { - let keep = noFilter; + let keep = true - if ( - !noFilter && - filterDefs[type] - .extract(availableEntities[category][element.value]) - .indexOf(key) >= 0 - ) { - keep = true; - } + types.forEach(type => { + if ( + !(filterDefs[type] + .extract(availableEntities[category][element.value]) + .indexOf(keys[type]) >= 0) + ) { + keep = false; + } + }) + if ( searchText != "" && @@ -5438,13 +5506,9 @@ function updateFilter() { } }); - const button = document.querySelector( - ".filter-select.category-visible + button" - ); - - if (button) { - button.querySelector(".button-counter").innerText = count; - } + const button = document.querySelector("#spawn-all") + + button.innerText = "Spawn " + count + " filtered " + (count == 1 ? "entity" : "entities") + "."; if (replace) { document.querySelector(".entity-select.category-visible").value = first;