Home Reference Source

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);