Home Reference Source

scripts/widgets/ui_widgets2.js

"use strict";
import './ui_richedit.js';

import * as util from '../util/util.js';
import * as ui_base from '../core/ui_base.js';
import * as events from '../path-controller/util/events.js';
import {Vector2, Vector3, Vector4, Quat, Matrix4} from '../path-controller/util/vectormath.js';
import {RowFrame, ColumnFrame} from "../core/ui.js";
import {isNumber, PropFlags} from "../path-controller/toolsys/toolprop.js";

import './ui_widgets.js';

let keymap = events.keymap;

import {EnumProperty, PropTypes} from '../path-controller/toolsys/toolprop.js';
import {UIBase, PackFlags, IconSheets, parsepx} from '../core/ui_base.js';

import * as units from '../core/units.js';
import {ToolProperty} from '../path-controller/toolsys/toolprop.js';
import {Button} from "./ui_button.js";

export class VectorPopupButton extends Button {
  constructor() {
    super();

    this.value = new Vector4();
  }

  static define() {return {
    tagname : "vector-popup-button-x",
    style : "vecPopupButton"
  }}

  _onpress(e) {
    if (e.button && e.button !== 0) {
      return;
    }

    let panel = UIBase.createElement("vector-panel-x");
    let screen = this.ctx.screen;

    let popup = screen.popup(this, this);

    popup.add(panel);
    popup.button("ok", () => {
      popup.end();
    })

    if (this.hasAttribute("datapath")) {
      panel.setAttribute("datapath", this.getAttribute("datapath"));
    }
    if (this.hasAttribute("mass_set_path")) {
      panel.setAttribute("mass_set_path", this.getAttribute("mass_set_path"));
    }

    popup.flushUpdate();
  }

  updateDataPath() {
    if (!this.hasAttribute("datapath")) {
      return;
    }

    let value = this.getPathValue(this.ctx, this.getAttribute("datapath"));

    if (!value) {
      this.internalDisabled = true;
      return;
    }

    if (this.internalDisabled) {
      this.internalDisabled = false;
    }

    if (this.value.length !== value.length) {
      switch (value.length) {
        case 2:
          this.value = new Vector2();
          break;
        case 3:
          this.value = new Vector3();
          break;
        case 4:
          this.value = new Vector4();
          break;
      }
    }

    if (this.value.vectorDistance(value) > 0.0001) {
      this.value.load(value);
      console.log("updated vector popup button value");
    }
  }

  update() {
    super.update();
    this.updateDataPath();
  }

}
UIBase.internalRegister(VectorPopupButton);

export class VectorPanel extends ColumnFrame {
  constructor() {
    super();

    this.range = [-1e17, 1e17];

    this.name = "";

    this.axes = "XYZW";
    this.value = new Vector3();
    this.sliders = [];
    this.hasUniformSlider = false;

    this.packflag |= PackFlags.FORCE_ROLLER_SLIDER|PackFlags.NO_NUMSLIDER_TEXTBOX;

    let makeParam = (key) => {
      Object.defineProperty(this, key, {
        get : function() {
          return this._getNumParam(key);
        },

        set : function(val) {
          this._setNumParam(key, val);
        }
      });
    };

    this.__range = [-1e17, 1e17];
    this._range = new Array(2);

    Object.defineProperty(this._range, 0, {
      get : () => this.__range[0],
      set : (val) => this.__range[0] = val
    });
    Object.defineProperty(this._range, 1, {
      get : () => this.__range[1],
      set : (val) => this.__range[1] = val
    });

    makeParam("isInt");
    makeParam("radix");
    makeParam("decimalPlaces");
    makeParam("baseUnit");
    makeParam("displayUnit");
    makeParam("step");
    makeParam("expRate");
    makeParam("stepIsRelative");

    window.vp = this;
  }

  init() {
    super.init();
    this.rebuild();
    this.setCSS();

    this.background = this.getDefault("InnerPanelBG");
  }

  _getNumParam(key) {
    return this["_"+key];
  }

  _setNumParam(key, val) {
    if (key === "range") {
      this.__range[0] = val[0];
      this.__range[1] = val[1];

      return;
    }

    this["_"+key] = val;

    for (let slider of this.sliders) {
      slider[key] = val;
    }
  }

  rebuild() {
    this.clear();

    if (this.name) {
      this.label(this.name);
    }

    let frame, row;

    if (this.hasUniformSlider) {
      row = this.row();
      frame = row.col();
    } else {
      frame = this;
    }

    this.sliders = [];

    for (let i=0; i<this.value.length; i++) {
      //inpath, name, defaultval, min, max, step, is_int, do_redraw, callback, packflag = 0) {

      let slider = frame.slider(undefined, {
        name       : this.axes[i],
        defaultval : this.value[i],
        min        : this.range[0],
        max        : this.range[1],
        step       : this.step || 0.001,
        is_int     : this.isInt,
        packflag   : this.packflag
      });

      //let slider = frame.slider(undefined, this.axes[i], this.value[i], this.range[0], this.range[1], 0.001, this.isInt);
      slider.axis = i;
      let this2 = this;

      slider.baseUnit = this.baseUnit;
      slider.displayUnit = this.displayUnit;
      slider.isInt = this.isInt;
      slider.range = this.__range;
      slider.radix = this.radix;
      slider.step = this.step;
      slider.expRate = this.expRate;
      slider.stepIsRelative= this.stepIsRelative;

      if (this.stepIsRelative) {
        slider.step = ToolProperty.calcRelativeStep(this.step, this.value[i]);
      }

      slider.onchange = function(e) {
        this2.value[this.axis] = this.value;

        if (this2.hasAttribute("datapath")) {
          this2.setPathValue(this2.ctx, this2.getAttribute("datapath"), this2.value);
        }

        if (this2.uslider) {
          this2.uslider.setValue(this2.uniformValue, false);
        }

        if (this2.onchange) {
          this2.onchange(this2.value);
        }
      }

      this.sliders.push(slider);
    }

    if (this.hasUniformSlider) {
      let uslider = this.uslider = UIBase.createElement("numslider-x");
      row._prepend(uslider);

      uslider.range = this.range;
      uslider.baseUnit = this.baseUnit;
      uslider.displayUnit = this.displayUnit;
      uslider.expRate = this.expRate;
      uslider.step = this.step;
      uslider.expRate = this.expRate;
      uslider.isInt = this.isInt;
      uslider.radix = this.radix;
      uslider.decimalPlaces = this.decimalPlaces;
      uslider.stepIsRelative= this.stepIsRelative;

      uslider.vertical = true;
      uslider.setValue(this.uniformValue, false);

      this.sliders.push(uslider);

      uslider.onchange = () => {
        this.uniformValue = uslider.value;
      }
    } else {
      this.uslider = undefined;
    }

    this.setCSS();
  }

  get uniformValue() {
    let sum = 0.0;

    for (let i=0; i<this.value.length; i++) {
      sum += isNaN(this.value[i]) ? 0.0 : this.value[i];
    }

    return sum / this.value.length;
  }

  set uniformValue(val) {
    let old = this.uniformValue;
    let doupdate = false;

    if (old === 0.0 || val === 0.0) {
      doupdate = this.value.dot(this.value) !== 0.0

      this.value.zero();
    } else {
      let ratio = val / old;
      for (let i = 0; i < this.value.length; i++) {
        this.value[i] *= ratio;
      }

      doupdate = true;
    }

    if (doupdate) {
      if (this.hasAttribute("datapath")) {
        this.setPathValue(this.ctx, this.getAttribute("datapath"), this.value);
      }

      if (this.onchange) {
        this.onchange(this.value);
      }

      for (let i=0; i<this.value.length; i++) {
        this.sliders[i].setValue(this.value[i], false);
        this.sliders[i]._redraw();
      }

      if (this.uslider) {
        this.uslider.setValue(val, false);
        this.uslider._redraw();
      }
    }
  }

  setValue(value) {
    if (!value) {
      return;
    }

    if (value.length !== this.value.length) {
      switch (value.length) {
        case 2:
          this.value = new Vector2(value);
          break;
        case 3:
          this.value = new Vector3(value);
          break;
        case 4:
          this.value = new Vector4(value);
          break;
        default:
          throw new Error("invalid vector size " + value.length);
      }

      this.rebuild();
    } else {
      this.value.load(value);
    }

    if (this.onchange) {
      this.onchange(this.value);
    }

    return this;
  }

  updateDataPath() {
    if (!this.hasAttribute("datapath")) {
      return;
    }

    let path = this.getAttribute("datapath");

    let val = this.getPathValue(this.ctx, path);
    if (val === undefined) {
      this.internalDisabled = true;
      return;
    }


    let meta = this.getPathMeta(this.ctx, path);
    let name = meta.uiname !== undefined ? meta.uiname : meta.name;
    if (this.hasAttribute("name")) {
      name = this.getAttribute("name");
    }

    if (name && name !== this.name) {
      this.name = name;
      this.rebuild();
      return;
    }

    let loadNumParam = (k, do_rebuild=false) => {
      if (meta && meta[k] !== undefined && this[k] === undefined) {
        this[k] = meta[k];

        if (this[k] !== meta[k] && do_rebuild) {
          this.doOnce(this.rebuild);
        }
      }
    }


    loadNumParam("baseUnit");
    loadNumParam("displayUnit");
    loadNumParam("decimalPlaces");
    loadNumParam("isInt");
    loadNumParam("radix");
    loadNumParam("step");
    loadNumParam("expRate");
    loadNumParam("stepIsRelative");

    if (meta && meta.hasUniformSlider !== undefined && meta.hasUniformSlider !== this.hasUniformSlider) {
      this.hasUniformSlider = meta.hasUniformSlider;
      this.doOnce(this.rebuild);
    }

    if (meta && meta.range) {
      let update = this.range[0] !== meta.range[0];
      update= update || this.range[1] !== meta.range[1];

      this.range[0] = meta.range[0];
      this.range[1] = meta.range[1];

      if (update) {
        this.doOnce(this.rebuild);
      }
    }

    this.internalDisabled = false;

    let length = val.length;

    if (meta && (meta.flag & PropFlags.USE_CUSTOM_GETSET)) {
      let rdef = this.ctx.api.resolvePath(this.ctx, path);

      meta.ctx = this.ctx;
      meta.dataref = rdef.obj;
      meta.datapath = path;

      length = meta.getValue().length;

      meta.dataref = undefined;
    }

    if (this.value.length !== length) {
      switch (length) {
        case 2:
          val = new Vector2(val);
          break;
        case 3:
          val = new Vector3(val);
          break;
        case 4:
          val = new Vector4(val);
          break;
        default:
          val = meta.getValue().copy().load(val);
          break;
      }

      this.value = val;
      this.rebuild();

      for (let i=0; i<this.value.length; i++) {
        this.sliders[i].setValue(val[i], false);
        this.sliders[i]._redraw();
      }
    } else {
      if (this.value.vectorDistance(val) > 0) {
        this.value.load(val);

        if (this.uslider) {
          this.uslider.setValue(this.uniformValue, false);
        }

        for (let i=0; i<this.value.length; i++) {
          this.sliders[i].setValue(val[i], false);
          this.sliders[i]._redraw();
        }
      }
    }
  }

  update() {
    super.update();

    this.updateDataPath();

    if (this.stepIsRelative) {
      for (let slider of this.sliders) {
        slider.step = ToolProperty.calcRelativeStep(this.step, slider.value);
      }
    }

    if (this.uslider) {
      this.uslider.step = this.step;
      if (this.stepIsRelative) {
        this.uslider.step = ToolProperty.calcRelativeStep(this.step, this.uniformValue);
      }

    }
  }

  static define() {return {
    tagname : "vector-panel-x"
  }}
}
UIBase.internalRegister(VectorPanel);


export class ToolTip extends UIBase {
  constructor() {
    super();

    this.visibleToPick = false;
    this.div = document.createElement("div");

    this.shadow.appendChild(this.div);
    this._start_time = undefined;
    this.timeout = undefined;
  }

  static show(message, screen, x, y) {
    let ret = UIBase.createElement(this.define().tagname);

    ret._start_time = util.time_ms();
    ret.timeout = ret.getDefault("timeout");

    ret.text = message;
    let size = ret._estimateSize();

    let pad = 5;
    size = [size[0] + pad, size[1] + pad];

    console.log(size);
    x = Math.min(Math.max(x, 0), screen.size[0] - size[0]);
    y = Math.min(Math.max(y, 0), screen.size[1] - size[1]);

    let dpi = UIBase.getDPI();

    x += 10/dpi;
    y += 15/dpi;

    ret._popup = screen.popup(ret, x, y);
    ret._popup.background = "rgba(0,0,0,0)";
    ret._popup.style["border"] = "none";
    ret.div.style["padding"] = "15px";

    ret._popup.add(ret);

    return ret;
  }

  end() {
    this._popup.end();
  }

  init() {
    super.init();
    this.setCSS();
  }

  set text(val) {
    this.div.innerHTML = val.replace(/[\n]/g, "<br>\n");
  }

  get text() {
    return this.div.innerHTML;
  }

  _estimateSize() {
    let text = this.div.textContent;
    let block = ui_base.measureTextBlock(this, text, undefined, undefined, undefined, this.getDefault("ToolTipText"));

    return [block.width+50, block.height+30];
  }

  update() {
    super.update();

    if (util.time_ms() - this._start_time > this.timeout) {
      this.end();
    }
  }

  setCSS() {
    super.setCSS();

    let color = this.getDefault("background-color");
    let bcolor = this.getDefault("border-color");

    this.background = color;

    let radius = this.getDefault("border-radius", undefined, 5);
    let bstyle = this.getDefault("border-style", undefined, "solid");
    let bwidth = this.getDefault("border-width", undefined, 1);
    let padding = this.getDefault("padding", undefined, 15);

    this.noMarginsOrPadding();

    this.div.style["padding"] = padding + "px";

    this.div.style["background-color"] = "rgba(0,0,0,0)";
    this.div.style["border"] = `${bwidth}px ${bstyle} ${bcolor}`;
    this.div.style["border-radius"] = radius + "px";
    this.style["border-radius"] = radius + "px";

    let font = this.getDefault("ToolTipText");
    this.div.style["color"] = font.color;
    this.div.style["font"] = font.genCSS();
  }

  static define() {return {
    tagname : "tool-tip-x",
    style   : "tooltip"
  }}
};
UIBase.internalRegister(ToolTip);

window._ToolTip = ToolTip;