Home Reference Source

scripts/widgets/theme_editor.js

import {Area} from '../screen/ScreenArea.js';
import * as nstructjs from '../path-controller/util/struct.js';
import {UIBase, theme, flagThemeUpdate} from '../core/ui_base.js';
import {Container} from '../core/ui.js';
import {validateCSSColor, color2css, css2color, CSSFont} from '../core/ui_theme.js';

export class ThemeEditor extends Container {
  constructor() {
    super();

    this.categoryMap = {};
  }

  static define() {
    return {
      tagname: "theme-editor-x",
      style  : "theme-editor"
    }
  }

  init() {
    super.init();

    this.build();
  }

  doFolder(catkey, obj, container = this) {
    let key = catkey.key;

    let panel = container.panel(key, undefined, undefined, catkey.help);
    panel.style["margin-left"] = "15px";

    let row = panel.row();
    let col1 = row.col();
    let col2 = row.col();


    let do_onchange = (key, k, obj) => {
      flagThemeUpdate();

      if (this.onchange) {
        this.onchange(key, k, obj);
      }

      this.ctx.screen.completeSetCSS();
      this.ctx.screen.completeUpdate();
    };

    let getpath = (path) => {
      let obj = theme;

      for (let i = 0; i < path.length; i++) {
        obj = obj[path[i]];
      }

      return obj;
    }

    let ok = false;
    let _i = 0;

    let dokey = (k, v, path) => {
      let col = _i%2 === 0 ? col1 : col2;

      if (k.toLowerCase().search("flag") >= 0) {
        return; //don't do flags
      }

      if (typeof v === "string") {
        let v2 = v.toLowerCase().trim();

        let iscolor = validateCSSColor(v2);

        if (iscolor) {
          let cw = col.colorbutton();
          ok = true;
          _i++;

          let color = css2color(v2);
          if (color.length < 3) {
            color = [color[0], color[1], color[2], 1.0];
          }

          try {
            cw.setRGBA(color);
          } catch (error) {
            console.warn("Failed to set color " + k, v2);
          }

          cw.onchange = () => {
            console.log("setting '" + k + "' to " + color2css(cw.rgba), key);
            getpath(path)[k] = color2css(cw.rgba);

            do_onchange(key, k);
          }
          cw.label = k;
        } else {
          col.label(k);

          let box = col.textbox();
          box.onchange = () => {
            getpath(path)[k] = box.text;
            do_onchange(key, k);
          }
          box.text = v;
        }
      } else if (typeof v === "number") {
        let slider = col.slider(undefined, k, v, 0, 256, 0.01, false);

        slider.baseUnit = slider.displayUnit = "none";

        ok = true;
        _i++;

        slider.onchange = () => {
          getpath(path)[k] = slider.value;

          do_onchange(key, k);
        }
      } else if (typeof v === "boolean") {
        let check = col.check(undefined, k);

        check.value = getpath(path)[k];

        check.onchange = () => {
          getpath(path)[k] = !!check.value;
          do_onchange(key, k);
        }
      } else if (typeof v === "object" && v instanceof CSSFont) {
        let panel2 = col.panel(k);
        ok = true;
        _i++;

        let textbox = (key) => {
          panel2.label(key);
          let tbox = panel2.textbox(undefined, v[key]);

          tbox.width = tbox.getDefault("width");

          tbox.onchange = function () {
            v[key] = this.text;
            do_onchange(key, k);
          }
        }

        textbox("font");
        textbox("variant");
        textbox("weight");
        textbox("style");

        let cw = panel2.colorbutton();
        cw.label = "color";
        cw.setRGBA(css2color(v.color));
        cw.onchange = () => {
          v.color = color2css(cw.rgba);
          do_onchange(key, k);
        }

        let slider = panel2.slider(undefined, "size", v.size);
        slider.onchange = () => {
          v.size = slider.value;
          do_onchange(key, k);
        }
        slider.setAttribute("min", 1);
        slider.setAttribute("max", 100);

        slider.baseUnit = slider.displayUnit = "none";

        panel2.closed = true;
      } else if (typeof v === "object") {
        let old = {
          panel, row, col1, col2
        };

        let path2 = path.slice(0, path.length);
        path2.push(k);

        panel = panel.panel(k);
        row = panel.row();
        col1 = row.col();
        col2 = row.col();
        for (let k2 in v) {
          let v2 = v[k2];

          dokey(k2, v2, path2);
        }

        panel = old.panel;
        row = old.row;
        col1 = old.col1;
        col2 = old.col2;
      }
    };

    for (let k in obj) {
      let v = obj[k];

      dokey(k, v, [key]);
    }

    if (!ok) {
      panel.remove();
    } else {
      panel.closed = true;
    }

  }

  build() {
    let categories = {};

    for (let k of Object.keys(theme)) {
      let catkey;

      if (k in this.categoryMap) {
        let cat = this.categoryMap[k];

        if (typeof cat === "string") {
          cat = {
            category: cat,
            help    : "",
            key     : k
          }
        }

        catkey = cat;
      } else {
        catkey = {category: k, help: '', key: k};
      }

      if (!catkey.key) {
        catkey.key = k;
      }

      if (!(catkey.category in categories)) {
        categories[catkey.category] = [];
      }

      categories[catkey.category].push(catkey);
    }

    function strcmp(a, b) {
      a = a.trim().toLowerCase();
      b = b.trim().toLowerCase();
      return a < b ? -1 : (a === b ? 0 : 1);
    }

    let keys = Object.keys(categories);
    keys.sort(strcmp);

    for (let k of keys) {
      let list = categories[k];
      list.sort((a, b) => strcmp(a.key, b.key));

      let panel = this;

      if (list.length > 1) {
        panel = this.panel(k);
      }

      for (let cat of list) {
        let k2 = cat.key;
        let v = theme[k2];

        if (typeof v === "object") {
          this.doFolder(cat, v, panel);
        }
      }

      if (list.length > 1) {
        panel.closed = true;
      }
    }
  }
}

UIBase.internalRegister(ThemeEditor);