2022-11-22 16:24:55 -06:00
|
|
|
/**
|
|
|
|
* Toolbar
|
|
|
|
*/
|
|
|
|
|
|
|
|
const toolbar = {
|
2022-11-24 21:34:34 -06:00
|
|
|
_locked: false,
|
2022-11-22 16:24:55 -06:00
|
|
|
_toolbar: document.getElementById("ui-toolbar"),
|
2022-11-24 21:34:34 -06:00
|
|
|
_toolbar_lock_indicator: document.getElementById("toolbar-lock-indicator"),
|
2022-11-22 16:24:55 -06:00
|
|
|
|
|
|
|
tools: [],
|
|
|
|
|
2022-11-24 21:34:34 -06:00
|
|
|
lock() {
|
|
|
|
toolbar._locked = true;
|
|
|
|
toolbar._toolbar_lock_indicator.style.display = "block";
|
|
|
|
},
|
|
|
|
unlock() {
|
|
|
|
toolbar._locked = false;
|
|
|
|
toolbar._toolbar_lock_indicator.style.display = "none";
|
|
|
|
},
|
|
|
|
|
2022-11-22 16:24:55 -06:00
|
|
|
_makeToolbarEntry: (tool) => {
|
|
|
|
const toolTitle = document.createElement("img");
|
|
|
|
toolTitle.classList.add("tool-icon");
|
|
|
|
toolTitle.src = tool.icon;
|
|
|
|
|
|
|
|
const toolEl = document.createElement("div");
|
|
|
|
toolEl.id = `tool-${tool.id}`;
|
|
|
|
toolEl.classList.add("tool");
|
|
|
|
toolEl.title = tool.name;
|
|
|
|
if (tool.options.shortcut) toolEl.title += ` (${tool.options.shortcut})`;
|
|
|
|
toolEl.onclick = () => tool.enable();
|
|
|
|
|
|
|
|
toolEl.appendChild(toolTitle);
|
|
|
|
|
|
|
|
return toolEl;
|
|
|
|
},
|
|
|
|
|
|
|
|
registerTool(
|
|
|
|
icon,
|
|
|
|
toolname,
|
|
|
|
enable,
|
|
|
|
disable,
|
|
|
|
options = {
|
|
|
|
/**
|
|
|
|
* Runs on tool creation. It receives the tool state.
|
|
|
|
*
|
|
|
|
* Can be used to setup callback functions, for example.
|
|
|
|
*/
|
|
|
|
init: null,
|
|
|
|
/**
|
|
|
|
* Function to populate the state menu.
|
|
|
|
*
|
|
|
|
* It receives a div element (that is the menu) and the current tool state.
|
|
|
|
*/
|
|
|
|
populateContextMenu: null,
|
|
|
|
/**
|
|
|
|
* Help description of the tool; for now used for nothing
|
|
|
|
*/
|
|
|
|
description: "",
|
|
|
|
/**
|
|
|
|
* Shortcut; Text describing this tool's shortcut access
|
|
|
|
*/
|
|
|
|
shortcut: "",
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
// Set some defaults
|
|
|
|
if (!options.init)
|
|
|
|
options.init = (state) => console.debug(`Initialized tool '${toolname}'`);
|
|
|
|
|
|
|
|
if (!options.populateContextMenu)
|
|
|
|
options.populateContextMenu = (menu, state) => {
|
|
|
|
const span = document.createElement("span");
|
|
|
|
span.textContent = "Tool has no context menu";
|
|
|
|
menu.appendChild(span);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Create tool
|
|
|
|
const id = guid();
|
|
|
|
|
|
|
|
const contextMenuEl = document.getElementById("tool-context");
|
|
|
|
|
|
|
|
const tool = {
|
|
|
|
id,
|
|
|
|
icon,
|
|
|
|
name: toolname,
|
|
|
|
enabled: false,
|
|
|
|
_element: null,
|
|
|
|
state: {},
|
|
|
|
options,
|
|
|
|
enable: (opt = null) => {
|
2022-11-24 21:34:34 -06:00
|
|
|
if (toolbar._locked) return;
|
|
|
|
|
2022-11-22 16:24:55 -06:00
|
|
|
this.tools.filter((t) => t.enabled).forEach((t) => t.disable());
|
|
|
|
|
|
|
|
while (contextMenuEl.lastChild) {
|
|
|
|
contextMenuEl.removeChild(contextMenuEl.lastChild);
|
|
|
|
}
|
|
|
|
options.populateContextMenu(contextMenuEl, tool.state);
|
|
|
|
|
|
|
|
tool._element && tool._element.classList.add("using");
|
|
|
|
tool.enabled = true;
|
|
|
|
enable(tool.state, opt);
|
|
|
|
},
|
|
|
|
disable: (opt = null) => {
|
|
|
|
tool._element && tool._element.classList.remove("using");
|
|
|
|
disable(tool.state, opt);
|
|
|
|
tool.enabled = false;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Initalize
|
|
|
|
options.init && options.init(tool.state);
|
|
|
|
|
|
|
|
this.tools.push(tool);
|
|
|
|
|
|
|
|
// Add tool to toolbar
|
|
|
|
tool._element = this._makeToolbarEntry(tool);
|
|
|
|
this._toolbar.appendChild(tool._element);
|
|
|
|
|
|
|
|
return tool;
|
|
|
|
},
|
|
|
|
|
|
|
|
addSeparator() {
|
|
|
|
const separator = document.createElement("div");
|
|
|
|
separator.classList.add("separator");
|
|
|
|
this._toolbar.appendChild(separator);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-11-22 19:24:04 -06:00
|
|
|
/**
|
|
|
|
* Premade inputs for populating the context menus
|
|
|
|
*/
|
|
|
|
const _toolbar_input = {
|
2022-11-26 19:35:16 -06:00
|
|
|
checkbox: (state, dataKey, text, cb = null) => {
|
2022-11-22 19:24:04 -06:00
|
|
|
if (state[dataKey] === undefined) state[dataKey] = false;
|
|
|
|
|
|
|
|
const checkbox = document.createElement("input");
|
|
|
|
checkbox.type = "checkbox";
|
|
|
|
checkbox.checked = state[dataKey];
|
2022-11-26 19:35:16 -06:00
|
|
|
checkbox.onchange = () => {
|
|
|
|
state[dataKey] = checkbox.checked;
|
|
|
|
cb && cb();
|
|
|
|
};
|
2022-11-22 19:24:04 -06:00
|
|
|
|
|
|
|
const label = document.createElement("label");
|
|
|
|
label.appendChild(checkbox);
|
|
|
|
label.appendChild(new Text(text));
|
|
|
|
|
|
|
|
return {checkbox, label};
|
|
|
|
},
|
|
|
|
|
2022-12-03 04:50:23 -06:00
|
|
|
slider: (state, dataKey, text, options = {}) => {
|
|
|
|
defaultOpt(options, {min: 0, max: 1, step: 0.1, textStep: null, cb: null});
|
2022-11-23 22:53:14 -06:00
|
|
|
const slider = document.createElement("div");
|
|
|
|
|
|
|
|
const value = createSlider(text, slider, {
|
2022-12-03 04:50:23 -06:00
|
|
|
min: options.min,
|
|
|
|
max: options.max,
|
|
|
|
step: options.step,
|
2022-11-23 22:53:14 -06:00
|
|
|
valuecb: (v) => {
|
|
|
|
state[dataKey] = v;
|
2022-12-03 04:50:23 -06:00
|
|
|
options.cb && options.cb(v);
|
2022-11-23 22:53:14 -06:00
|
|
|
},
|
2022-11-24 09:30:13 -06:00
|
|
|
defaultValue: state[dataKey],
|
2022-12-03 04:50:23 -06:00
|
|
|
textStep: options.textStep,
|
2022-11-23 22:53:14 -06:00
|
|
|
});
|
2022-11-22 19:24:04 -06:00
|
|
|
|
|
|
|
return {
|
|
|
|
slider,
|
|
|
|
setValue(v) {
|
2022-11-23 22:53:14 -06:00
|
|
|
value.value = v;
|
|
|
|
return value.value;
|
2022-11-22 19:24:04 -06:00
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
};
|