Home Reference Source

scripts/widgets/ui_container.js

import {Container} from '../core/ui.js';
import {UIBase} from '../core/ui_base.js';
import {DataAPI} from "../path-controller/controller/controller.js";

export let api = new DataAPI();

export function setRootStruct(val) {
  return api.setRoot(val);
}

/*container interface refactor TODO:

* Have a system to build datapath definition and UI at the same time
* Make greater use of arguments object literals, instead of giant
  function parameter lists

Building the data api along with the UI will probably require
patching widget objects in real-time.  The idea is to do something like this:

layout.beginPath("object", SomeObjectClass)
layout.slider("value").range(0, 1)

*/

export class ContainerIF {
  beginPath(path, cls) {}
  endPath() {}

  prop(path, args) {}
  tool(path, args) {}
  menu(title, definition, args) {}

  slider(path, args) {}
  simpleslider(path, args) {}
  textbox(path, args) {}
  vector(path, args) {}
  colorpicker(path, args) {}
  colorbutton(path, args) {}
  iconenum(path, args) {}
  iconcheck(path, args) {}
  button(name, tooltip, args) {}
  iconbutton(icon, tooltip, args) {}

  //also known as a dropbox
  listenum(path, args) {}

  table() {}
  row() {}
  col() {}
  strip() {}

  useIcons() {}
  useSimpleSliders() {}
}

export class BuilderContainer extends Container {
  constructor() {
    super();
    this.pathPrefix = "";
    this.pathstack = [];
    this._class = undefined;
    this._struct = undefined;
  }

  init() {
    super.init();
  }

  _buildPath() {
    let path = "";

    for (let p of this.pathstack) {
      if (p[0].trim() === "") {
        continue;
      }

      if (path.length > 0)
        path += "."
      path += p[0];
    }

    if (this.pathstack.length > 0) {
      this._class = this.pathstack[this.pathstack.length - 1][1];
      this._struct = this.pathstack[this.pathstack.length - 1][2];
    } else {
      this._class = undefined;
      this._struct = undefined;
    }

    this.pathPrefix = path;
    return path;
  }

  beginPath(path, cls) {
    this.pathstack.push([path, cls, api.mapStruct(cls, true)]);
    this._buildPath();
  }

  popPath(path, cls) {
    this.pathstack.pop();
    this._buildPath();
  }

  joinPath(path) {
    if (this.pathPrefix.trim().length > 0) {
      return this.pathPrefix + "." + path;
    } else {
      return path.trim();
    }
  }

  _makeAPI(path) {
    if (!path) {
      return false;
    }

    if (!this._struct) {
      console.warn("No struct");
      return false;
    }

    return !(path in this._struct.pathmap);
  }


  static define() {return {
    tagname : "container-builder-x"
  }}

  _args(args={}) {
    if (args.packflag === undefined)
      args.packflag = 0;

    args.packflag |= this.inherit_packflag;

    return args;
  }

  prop(path, args) {
    args = this._args(args);
    return super.prop(path, args.packflag, args.mass_set_path);
  }
  tool(path, args) {
    args = this._args(args);
    return super.tool(path, args.packflag, args.create_cb);
  }

  menu(title, definition, args) {
    args = this._args(args);
    return super.menu(title, definition, args.packflag);
  }

  _wrapElem(e, dpath) {
    return {
      widget : e,
      range : (min, max) => dpath.range(min, max),
      description : (d) => dpath.description(d),
      on : () => dpath.on(...arguments),
      off : () => dpath.off(...arguments),
      simpleSlider : () => dpath.simpleSlider(),
      rollerSlider : () => dpath.rollerSlider(),
      uiRange : (min, max) => dpath.uiRange(),
      decimalPlaces : (p) => dpath.decimalPlaces(),
      expRate : (p) => dpath.expRate(p),
      radix : (p) => dpath.radix(p),
      step : (p) => dpath.step(p),
      icon : (icon) => dpath.icon(icon),
      icons : (iconmap) => dpath.icons(iconmap),
      descriptions : (ds) => dpath.descriptions(ds),
      customGetSet : () => dpath.customGetSet.apply(...arguments)
    }
  }

  slider(path, args) {
    args = this._args(args);
    let dopatch = false, dpath;

    if (this._makeAPI(path)) {
      let path2 = args.apiname ? args.apiname : path;
      let uiname = args.uiName ? args.uiName : path2;

      if (args.is_int || args.isInt) {
        dpath = this._struct.int(path, path2, uiname, args.description);
      } else {
        dpath = this._struct.float(path, path2, uiname, args.description);
      }

      if (args.min && args.max) {
        dpath.range(args.min, args.max);
      }

    }


    let ret = super.slider(this.joinPath(path), args.name, args.defaultval, args.min, args.max,
                        args.step, args.is_int, args.do_redraw, args.callback, args.packflag);

    if (dopatch) {
      this._wrapElem(ret, dpath);
    }

    return ret;
  }

  simpleslider(path, args) {
    args = this._args(args);
    args.packflag |= PackFlags.SIMPLE_NUMSLIDERS;
    return this.slider(path, args);
  }

  textbox(path, args) {
    args = this._args(args);
    let dopatch = false, dpath;

    if (this._makeAPI(path)) {
      let path2 = args.apiname ? args.apiname : path;
      let uiname = args.uiName ? args.uiName : path2;


      if (args.type === "int") {
        dpath = this._struct.int(path, path2, uiname, args.description);
      } else if (args.type === "float") {
        dpath = this._struct.float(path, path2, uiname, args.description);
      } else {
        dpath = this._struct.string(path, path2, uiname, args.description);
      }

      if ((args.type === "int" || args.type === "float") && args.min && args.max) {
        dpath.range(args.min, args.max);
      }
    }


    let ret = super.textbox(this.joinPath(path), args.text, args.callback, args.packflag);

    if (dopatch) {
      this._wrapElem(ret, dpath);
    }

    return ret;
  }

  vector(path, args) {}
  colorpicker(path, args) {}
  colorbutton(path, args) {}
  iconenum(path, args) {}
  iconcheck(path, args) {}
  button(name, tooltip, args) {}
  iconbutton(icon, tooltip, args) {}

  //also known as a dropbox
  listenum(path, args) {}

  table() {}
  row() {}
  col() {}
  strip() {}

  useIcons() {}
  useSimpleSliderS() {}

}

export class BuilderRow extends BuilderContainer {
  init() {
    super.init();

    this.style["flex-direction"] = "row";
  }

  static define() {return {
    tagname : "row-builder-x"
  }}
}
UIBase.internalRegister(BuilderRow);