scripts/widgets/ui_lasttool.js
import {PackFlags, UIBase} from "../core/ui_base.js";
import {ColumnFrame} from "../core/ui.js";
import {PropTypes, PropFlags} from "../path-controller/toolsys/toolprop.js";
import {UndoFlags, ToolFlags} from "../path-controller/toolsys/toolsys.js";
import {DataPath, DataTypes} from "../path-controller/controller/controller.js";
import {ToolProperty} from '../path-controller/toolsys/toolprop.js';
import * as util from '../path-controller/util/util.js';
import cconst from '../config/const.js';
const LastKey = Symbol("LastToolPanelId");
let tool_idgen = 0;
export function getLastToolStruct(ctx) {
let ret = ctx.state._last_tool;
if (!ret) {
ret = ctx.toolstack.head;
} else {
let msg = "Passing the last tool to last-tool-panel via appstate._last_tool is deprecated;";
msg += "\nctx.toolstack.head is now used instead.";
console.warn(msg);
}
return ret;
}
/*
*
* This panel shows the most recently executed ToolOp's
* settings. It assumes that recent toolops are accessible
* in ctx.last_tool.
* */
export class LastToolPanel extends ColumnFrame {
constructor() {
super();
this._tool_id = undefined;
this.useDataPathUndo = false;
}
init() {
super.init();
this.useDataPathUndo = false;
this.rebuild();
}
/** client code can subclass and override this method */
getToolStackHead(ctx) {
//don't process the root toolop
let bad = ctx.toolstack.length === 0 || ctx.toolstack.cur >= ctx.toolstack.length;
bad = bad || ctx.toolstack.cur < 0;
bad = bad || ctx.toolstack[ctx.toolstack.cur].undoflag & UndoFlags.IS_UNDO_ROOT;
if (bad) {
return undefined;
}
return ctx.toolstack[ctx.toolstack.cur];
}
rebuild() {
let ctx = this.ctx;
if (ctx === undefined) {
this._tool_id = -1; //wait for .ctx
return;
}
this.clear();
this.label("Recent Command Settings");
let tool = this.getToolStackHead(ctx);
if (!tool) {
this.setCSS();
return;
}
let def = tool.constructor.tooldef();
let name = def.uiname !== undefined ? def.uiname : def.name;
let panel = this.panel(def.uiname);
this.buildTool(ctx, tool, panel);
this.flushUpdate();
}
/** client code can subclass and override this method */
buildTool(ctx, tool, panel) {
let fakecls = {};
fakecls.constructor = fakecls;
//in theory it shouldn't matter if multiple last tool panels
//override _last_tool, since they all access the same data
this.ctx.state._last_tool = fakecls;
let lastkey = tool[LastKey];
let getTool = () => {
let tool = this.ctx.toolstack[this.ctx.toolstack.cur];
if (!tool || tool[LastKey] !== lastkey) {
return undefined;
}
return tool;
};
if (tool.flag & ToolFlags.PRIVATE) {
return;
}
let st = this.ctx.api.mapStruct(fakecls, true);
let paths = [];
function defineProp(k, key) {
Object.defineProperty(fakecls, key, {
get : function() {
let tool = getTool();
if (tool) {
if (!tool.inputs[k]) {
console.error("Missing property " + k, tool);
}
return tool.inputs[k].getValue();
}
},
set : function(val) {
let tool = getTool();
if (tool) {
tool.inputs[k].setValue(val);
tool.saveDefaultInputs();
ctx.toolstack.rerun(tool);
}
}
});
}
for (let k in tool.inputs) {
let prop = tool.inputs[k];
if (prop.flag & (PropFlags.PRIVATE|PropFlags.READ_ONLY)) {
continue;
}
let uiname = prop.uiname;
if (!uiname) {
uiname = ToolProperty.makeUIName(k);
}
prop.uiname = uiname;
let apikey = k.replace(/[\t ]/g, "_");
let dpath = new DataPath(apikey, apikey, prop, DataTypes.PROP);
st.add(dpath);
paths.push(dpath);
defineProp(k, apikey);
}
panel.useDataPathUndo = false;
for (let dpath of paths) {
let path = "last_tool." + dpath.path;
panel.label(dpath.data.uiname);
let ret = panel.prop(path, PackFlags.FORCE_ROLLER_SLIDER);
if (ret) {
ret.useDataPathUndo = false;
}
}
this.setCSS();
//console.log("Building last tool settings");
}
update() {
super.update();
let ctx = this.ctx;
if (!ctx) {
return;
}
let tool = this.getToolStackHead(ctx);
if (tool && (!(LastKey in tool) || tool[LastKey] !== this._tool_id)) {
tool[LastKey] = tool_idgen++;
this._tool_id = tool[LastKey];
this.rebuild();
}
}
static define() {return {
tagname : "last-tool-panel-x"
}}
}
UIBase.internalRegister(LastToolPanel);