refactor: ice editor dialog

This commit is contained in:
Azgaar 2022-07-09 21:08:49 +03:00
parent 0dd7468184
commit 3125366944
12 changed files with 75 additions and 46 deletions

View file

@ -1,199 +0,0 @@
import {openDialog} from "dialogs";
import {toggleLayer} from "layers";
// @ts-expect-error js module
import {showAboutDialog} from "scripts/options/about";
import {byId} from "utils/shorthands";
import {closeDialogs} from "dialogs/utils";
import {minmax} from "utils/numberUtils";
// @ts-expect-error js module
import {hideOptions} from "modules/ui/options";
// @ts-expect-error js module
import {regeneratePrompt, toggle3dOptions, toggleOptions} from "modules/ui/options";
// @ts-expect-error js module
import {quickSave, saveToDropbox, dowloadMap, toggleSaveReminder} from "modules/io/save";
// @ts-expect-error js module
import {quickLoad} from "modules/io/load";
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
export function addHotkeyListeners() {
document.on("keydown", handleKeydown as EventListener);
document.on("keyup", handleKeyup as EventListener);
}
function handleKeydown(event: KeyboardEvent) {
if (!allowHotkeys()) return; // in some cases (e.g. in a textarea) hotkeys are not allowed
const {code, ctrlKey, altKey} = event;
if (altKey && !ctrlKey) event.preventDefault(); // disallow alt key combinations
if (ctrlKey && ["KeyS", "KeyC"].includes(code)) event.preventDefault(); // disallow CTRL + S and CTRL + C
if (["F1", "F2", "F6", "F9", "Tab"].includes(code)) event.preventDefault(); // disallow default Fn and Tab
}
function handleKeyup(event: KeyboardEvent) {
if (!allowHotkeys()) return; // in some cases (e.g. in a textarea) hotkeys are not allowed
event.stopPropagation();
const {code, key, ctrlKey, metaKey, shiftKey, altKey} = event;
const ctrl = ctrlKey || metaKey || key === "Control";
const shift = shiftKey || key === "Shift";
const alt = altKey || key === "Alt";
const Zoom = window.Zoom;
const $undo = byId("undo");
const $redo = byId("redo");
if (code === "F1") showAboutDialog();
else if (code === "F2") regeneratePrompt();
else if (code === "F6") quickSave();
else if (code === "F9") quickLoad();
else if (code === "Tab") toggleOptions(event);
else if (code === "Escape") closeAllDialogs();
else if (code === "Delete") removeElementOnKey();
else if (code === "KeyO" && byId("canvas3d")) toggle3dOptions();
else if (ctrl && code === "KeyQ") toggleSaveReminder();
else if (ctrl && code === "KeyS") dowloadMap();
else if (ctrl && code === "KeyC") saveToDropbox();
else if (ctrl && code === "KeyZ" && $undo?.offsetParent) $undo.click();
else if (ctrl && code === "KeyY" && $redo?.offsetParent) $redo.click();
else if (shift && code === "KeyH") openDialog("heightmapEditor");
else if (shift && code === "KeyB") editBiomes();
else if (shift && code === "KeyS") openDialog("statesEditor");
else if (shift && code === "KeyP") editProvinces();
else if (shift && code === "KeyD") openDialog("diplomacyEditor");
else if (shift && code === "KeyC") openDialog("culturesEditor");
else if (shift && code === "KeyN") editNamesbase();
else if (shift && code === "KeyZ") editZones();
else if (shift && code === "KeyR") openDialog("religionsEditor");
else if (shift && code === "KeyY") openEmblemEditor();
else if (shift && code === "KeyQ") openDialog("unitsEditor");
else if (shift && code === "KeyO") editNotes();
else if (shift && code === "KeyA") openDialog("chartsOverview");
else if (shift && code === "KeyT") openDialog("burgsOverview");
else if (shift && code === "KeyV") overviewRivers();
else if (shift && code === "KeyM") overviewMilitary();
else if (shift && code === "KeyK") overviewMarkers();
else if (shift && code === "KeyE") viewCellDetails();
else if (key === "!") toggleAddBurg();
else if (key === "@") toggleAddLabel();
else if (key === "#") toggleAddRiver();
else if (key === "$") toggleAddRoute();
else if (key === "%") toggleAddMarker();
else if (alt && code === "KeyB") console.table(pack.burgs);
else if (alt && code === "KeyS") console.table(pack.states);
else if (alt && code === "KeyC") console.table(pack.cultures);
else if (alt && code === "KeyR") console.table(pack.religions);
else if (alt && code === "KeyF") console.table(pack.features);
else if (code === "KeyX") toggleLayer("toggleTexture");
else if (code === "KeyH") toggleLayer("toggleHeight");
else if (code === "KeyB") toggleLayer("toggleBiomes");
else if (code === "KeyE") toggleLayer("toggleCells");
else if (code === "KeyG") toggleLayer("toggleGrid");
else if (code === "KeyO") toggleLayer("toggleCoordinates");
else if (code === "KeyW") toggleLayer("toggleCompass");
else if (code === "KeyV") toggleLayer("toggleRivers");
else if (code === "KeyF") toggleLayer("toggleRelief");
else if (code === "KeyC") toggleLayer("toggleCultures");
else if (code === "KeyS") toggleLayer("toggleStates");
else if (code === "KeyP") toggleLayer("toggleProvinces");
else if (code === "KeyZ") toggleLayer("toggleZones");
else if (code === "KeyD") toggleLayer("toggleBorders");
else if (code === "KeyR") toggleLayer("toggleReligions");
else if (code === "KeyU") toggleLayer("toggleRoutes");
else if (code === "KeyT") toggleLayer("toggleTemp");
else if (code === "KeyN") toggleLayer("togglePopulation");
else if (code === "KeyJ") toggleLayer("toggleIce");
else if (code === "KeyA") toggleLayer("togglePrec");
else if (code === "KeyY") toggleLayer("toggleEmblems");
else if (code === "KeyL") toggleLayer("toggleLabels");
else if (code === "KeyI") toggleLayer("toggleIcons");
else if (code === "KeyM") toggleLayer("toggleMilitary");
else if (code === "KeyK") toggleLayer("toggleMarkers");
else if (code === "Equal") toggleLayer("toggleRulers");
else if (code === "Slash") toggleLayer("toggleScaleBar");
else if (code === "ArrowLeft") Zoom.translateBy(svg, 10, 0);
else if (code === "ArrowRight") Zoom.translateBy(svg, -10, 0);
else if (code === "ArrowUp") Zoom.translateBy(svg, 0, 10);
else if (code === "ArrowDown") Zoom.translateBy(svg, 0, -10);
else if (key === "+" || key === "-") pressNumpadSign(key);
else if (key === "0") Zoom.reset(1000);
else if (key === "1") Zoom.scaleTo(svg, 1);
else if (key === "2") Zoom.scaleTo(svg, 2);
else if (key === "3") Zoom.scaleTo(svg, 3);
else if (key === "4") Zoom.scaleTo(svg, 4);
else if (key === "5") Zoom.scaleTo(svg, 5);
else if (key === "6") Zoom.scaleTo(svg, 6);
else if (key === "7") Zoom.scaleTo(svg, 7);
else if (key === "8") Zoom.scaleTo(svg, 8);
else if (key === "9") Zoom.scaleTo(svg, 9);
else if (ctrl) toggleMode();
}
function allowHotkeys() {
if (document.activeElement) {
const {tagName, contentEditable} = document.activeElement as HTMLElement;
if (["INPUT", "SELECT", "TEXTAREA"].includes(tagName)) return false;
if (tagName === "DIV" && contentEditable === "true") return false;
}
if (document.getSelection()?.toString()) return false;
return true;
}
function getActionBrushInput() {
if (byId("brushRadius")?.offsetParent) return byId("brushRadius");
if (byId("biomesManuallyBrush")?.offsetParent) return byId("biomesManuallyBrush");
if (byId("statesManuallyBrush")?.offsetParent) return byId("statesManuallyBrush");
if (byId("provincesManuallyBrush")?.offsetParent) return byId("provincesManuallyBrush");
if (byId("culturesManuallyBrush")?.offsetParent) return byId("culturesManuallyBrush");
if (byId("zonesBrush")?.offsetParent) return byId("zonesBrush");
if (byId("religionsManuallyBrush")?.offsetParent) return byId("religionsManuallyBrush");
return null;
}
function pressNumpadSign(key: "+" | "-") {
const brush = getActionBrushInput() as HTMLInputElement | null;
if (brush) {
const change = key === "+" ? 1 : -1;
const value = String(minmax(+brush.value + change, +brush.min, +brush.max));
brush.value = value;
const numberInput = byId(brush.id + "Number") as HTMLInputElement | null;
if (numberInput) numberInput.value = value;
} else {
// if no brush inputs visible, Zoom map
const scaleBy = key === "+" ? 1.2 : 0.8;
window.Zoom.scaleBy(svg, scaleBy);
}
}
function toggleMode() {
const $zonesRemove = byId("zonesRemove");
if ($zonesRemove?.offsetParent) {
$zonesRemove.classList.contains("pressed")
? $zonesRemove.classList.remove("pressed")
: $zonesRemove.classList.add("pressed");
}
}
function removeElementOnKey() {
const dialogsWithFastDelete = document.querySelectorAll("[role='dialog'] .fastDelete");
const $fastDelete = Array.from(dialogsWithFastDelete).find(
dialog => (dialog as HTMLElement).style.display !== "none"
) as HTMLElement | undefined;
if ($fastDelete) $fastDelete.click();
const visibleDialogs = Array.from(document.querySelectorAll("[role='dialog']")).filter(
dialog => (dialog as HTMLElement).style.display !== "none"
);
if (!visibleDialogs.length) return;
visibleDialogs.forEach(dialog =>
dialog.querySelectorAll("button").forEach(button => button.textContent === "Remove" && button.click())
);
}
function closeAllDialogs() {
closeDialogs();
hideOptions();
}

View file

@ -1,126 +0,0 @@
import * as d3 from "d3";
import {findGridCell, getGridPolygon} from "utils/graphUtils";
import {tip, clearMainTip} from "scripts/tooltips";
import {rn} from "utils/numberUtils";
import {ra} from "utils/probabilityUtils";
import {parseTransform} from "utils/stringUtils";
import {closeDialogs} from "dialogs/utils";
let isLoaded = false;
export function editIce() {
if (customization) return;
closeDialogs(".stable");
if (!layerIsOn("toggleIce")) toggleIce();
elSelected = d3.select(d3.event.target);
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
document.getElementById("iceRandomize").style.display = type === "Glacier" ? "none" : "inline-block";
document.getElementById("iceSize").style.display = type === "Glacier" ? "none" : "inline-block";
if (type === "Iceberg") document.getElementById("iceSize").value = +elSelected.attr("size");
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
$("#iceEditor").dialog({
title: "Edit " + type,
resizable: false,
position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"},
close: closeEditor
});
if (isLoaded) return;
isLoaded = true;
// add listeners
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
document.getElementById("iceSize").addEventListener("input", changeSize);
document.getElementById("iceNew").addEventListener("click", toggleAdd);
document.getElementById("iceRemove").addEventListener("click", removeIce);
function randomizeShape() {
const c = grid.points[+elSelected.attr("cell")];
const s = +elSelected.attr("size");
const i = ra(grid.cells.i),
cn = grid.points[i];
const poly = getGridPolygon(i).map(p => [p[0] - cn[0], p[1] - cn[1]]);
const points = poly.map(p => [rn(c[0] + p[0] * s, 2), rn(c[1] + p[1] * s, 2)]);
elSelected.attr("points", points);
}
function changeSize() {
const c = grid.points[+elSelected.attr("cell")];
const s = +elSelected.attr("size");
const flat = elSelected
.attr("points")
.split(",")
.map(el => +el);
const pairs = [];
while (flat.length) pairs.push(flat.splice(0, 2));
const poly = pairs.map(p => [(p[0] - c[0]) / s, (p[1] - c[1]) / s]);
const size = +this.value;
const points = poly.map(p => [rn(c[0] + p[0] * size, 2), rn(c[1] + p[1] * size, 2)]);
elSelected.attr("points", points).attr("size", size);
}
function toggleAdd() {
document.getElementById("iceNew").classList.toggle("pressed");
if (document.getElementById("iceNew").classList.contains("pressed")) {
viewbox.style("cursor", "crosshair").on("click", addIcebergOnClick);
tip("Click on map to create an iceberg. Hold Shift to add multiple", true);
} else {
clearMainTip();
viewbox.on("click", clicked).style("cursor", "default");
}
}
function addIcebergOnClick() {
const [x, y] = d3.mouse(this);
const i = findGridCell(x, y, grid);
const c = grid.points[i];
const s = +document.getElementById("iceSize").value;
const points = getGridPolygon(i).map(p => [(p[0] + (c[0] - p[0]) / s) | 0, (p[1] + (c[1] - p[1]) / s) | 0]);
const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", s);
iceberg.call(d3.drag().on("drag", dragElement));
if (d3.event.shiftKey === false) toggleAdd();
}
function removeIce() {
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
$("#alert").dialog({
resizable: false,
title: "Remove " + type,
buttons: {
Remove: function () {
$(this).dialog("close");
elSelected.remove();
$("#iceEditor").dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
}
});
}
function dragElement() {
const tr = parseTransform(this.getAttribute("transform"));
const dx = +tr[0] - d3.event.x,
dy = +tr[1] - d3.event.y;
d3.event.on("drag", function () {
const x = d3.event.x,
y = d3.event.y;
this.setAttribute("transform", `translate(${dx + x},${dy + y})`);
});
}
function closeEditor() {
ice.selectAll("*").classed("draggable", false).call(d3.drag().on("drag", null));
clearMainTip();
iceNew.classList.remove("pressed");
unselect();
}
}

View file

@ -210,7 +210,7 @@ export function editRegiment(selector) {
tip("Click on map to create new regiment or fleet", true);
} else {
clearMainTip();
viewbox.on("click", clicked).style("cursor", "default");
restoreDefaultEvents();
}
}
@ -240,8 +240,8 @@ export function editRegiment(selector) {
armies.selectAll(":scope > g").classed("draggable", false);
} else {
clearMainTip();
restoreDefaultEvents();
armies.selectAll(":scope > g").classed("draggable", true);
viewbox.on("click", clicked).style("cursor", "default");
}
}
@ -313,7 +313,7 @@ export function editRegiment(selector) {
} else {
clearMainTip();
armies.selectAll(":scope > g").classed("draggable", true);
viewbox.on("click", clicked).style("cursor", "default");
restoreDefaultEvents();
}
}

View file

@ -7,6 +7,7 @@ import {rn} from "utils/numberUtils";
import {capitalize} from "utils/stringUtils";
import {si} from "utils/unitUtils";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "scripts/events";
let isLoaded = false;
@ -170,7 +171,7 @@ export function overviewRegiments(state) {
if (regimentAdd.offsetParent) regimentAdd.classList.add("pressed");
} else {
clearMainTip();
viewbox.on("click", clicked).style("cursor", "default");
restoreDefaultEvents();
addLines();
if (regimentAdd.offsetParent) regimentAdd.classList.remove("pressed");
}

View file

@ -6,6 +6,7 @@ import {rn} from "utils/numberUtils";
import {getNextId} from "utils/nodeUtils";
import {round} from "utils/stringUtils";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "../../scripts/events";
let isLoaded = false;
@ -278,7 +279,7 @@ export function editRoute(onClick) {
elSelected.on("click", null);
} else {
clearMainTip();
viewbox.on("click", clicked).style("cursor", "default");
restoreDefaultEvents();
elSelected.on("click", addInterimControlPoint).attr("data-new", null);
}
}