UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

229 lines (174 loc) • 5.86 kB
import dat from 'dat.gui' import View from "../../view/View.js"; import { OptionGroup } from "./OptionGroup.js"; function isOption(c) { return typeof c.read === "function"; } /** * DAT.GUI sets styles directly on the element internally, to prevent this - we use this hack that clears styles every frame while view is visible * @param {View} view */ function clearGUIStyles(view) { let clearStyleFlag = false; function clearStyle() { view.el.removeAttribute('style'); } function runClearStyle() { clearStyle(); if (clearStyleFlag) { requestAnimationFrame(runClearStyle); } } view.on.linked.add(function () { clearStyleFlag = true; runClearStyle(); }); view.on.unlinked.add(function () { clearStyleFlag = false; }); } class OptionsView extends View { /** * * @constructor * @param {OptionGroup} options * @param {Localization} localization * @param {string[][]} [inclusions] */ constructor({ options, localization, inclusions }) { super(); if (inclusions === undefined) { //no inclusions specified, include everything inclusions = []; options.traverseOptions(op => { inclusions.push(op.computePath()); }); } const gui = new dat.GUI({ autoPlace: false, resizable: false, closed: false }); /** * * @param {Option|OptionGroup} option * @returns {String[]} */ function getPathFor(option) { return option.computePath(); } /** * * @param {Option|OptionGroup} option */ function getNameFor(option) { const pathString = getPathFor(option).join('.'); const localizationKey = 'system_option.' + pathString; return localization.getString(localizationKey); } const controls = new Map(); function updateLocalization() { controls.forEach((control, option) => { const optionName = getNameFor(option); if (option.isOptionGroup) { control.name = optionName; } else { control.name(optionName); } }); } this.on.linked.add(updateLocalization); this.bindSignal(localization.locale.onChanged, updateLocalization); const self = this; /** * * @param {Option} option * @param rootFolder */ function makeOption(option, rootFolder) { const op = { v: null }; let control; Object.defineProperty(op, "v", { get: function () { return option.read(); }, set: function (v) { option.write(v); } }); try { control = rootFolder.add(op, "v", option.settings.values); } catch (e) { console.error("Failed to add option controller", option, e); return; } if (typeof option.settings.min === "number") { control = control.min(option.settings.min); } if (typeof option.settings.max === "number") { control = control.max(option.settings.max); } control.name(getNameFor(option)); controls.set(option, control); self.bindSignal(option.on.written, (v) => { control.updateDisplay(); }); } /** * * @param {Option|OptionGroup} op */ function isOptionIncluded(op) { const n = inclusions.length; const path = op.computePath(); const pathLength = path.length; main: for (let i = 0; i < n; i++) { const inclusion = inclusions[i]; const inclusionLength = inclusion.length; if (pathLength > inclusionLength) { continue; } for (let j = 0; j < pathLength; j++) { const pathPart = path[j]; const inclusionPart = inclusion[j]; if (pathPart !== inclusionPart) { continue main; } } //path is inside the inclusion return true; } return false; } /** * * @param {OptionGroup} group * @param {GUI} rootFolder */ function makeGroup(group, rootFolder) { group.children.forEach((c) => { if (!isOptionIncluded(c)) { return; } if (isOption(c)) { makeOption(c, rootFolder); } else { const folder = rootFolder.addFolder(c.id); folder.name = getNameFor(c); controls.set(c, folder); makeGroup(c, folder); } }); } makeGroup(options, gui); this.el = gui.domElement; clearGUIStyles(this); //update all controls on linkage this.on.linked.add(() => { controls.forEach(control => control.updateDisplay()); }, this); } } export default OptionsView;