changeset 2331:b09ff4b0094c

faders in pages now
author drewp@bigasterisk.com
date Fri, 02 Jun 2023 09:42:18 -0700
parents e28a9b41ad87
children 2aeceb6f03aa
files light9/fade/Light9EffectFader.ts light9/fade/Light9FadeUi.ts light9/fade/Light9Fader.ts light9/web/EditChoice.ts
diffstat 4 files changed, 191 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/light9/fade/Light9EffectFader.ts	Thu Jun 01 22:06:34 2023 -0700
+++ b/light9/fade/Light9EffectFader.ts	Fri Jun 02 09:42:18 2023 -0700
@@ -1,11 +1,74 @@
 import { css, html, LitElement } from "lit";
 import { customElement, property, state } from "lit/decorators.js";
-import { NamedNode } from "n3";
+import { NamedNode, Quad } from "n3";
 import { getTopGraph } from "../web/RdfdbSyncedGraph";
 import { showRoot } from "../web/show_specific";
 import { SyncedGraph } from "../web/SyncedGraph";
+import { Patch } from "../web/patch";
+import { Literal } from "n3";
 export { Light9Fader } from "./Light9Fader";
 
+//////////////////////////////////////
+const RETURN_URI = new NamedNode("");
+const RETURN_FLOAT = 1;
+function get2Step<T extends NamedNode | number>(returnWhat: T, graph: SyncedGraph, subj1: NamedNode, pred1: NamedNode, pred2: NamedNode): T | undefined {
+  // ?subj1 ?pred1 ?x . ?x ?pred2 ?returned .
+  let x: NamedNode;
+  try {
+    x = graph.uriValue(subj1, pred1);
+  } catch (e) {
+    return undefined;
+  }
+  try {
+    if (typeof returnWhat === "object" && (returnWhat as NamedNode).termType == "NamedNode") {
+      return graph.uriValue(x, pred2) as T;
+    } else if (typeof returnWhat === "number") {
+      return graph.floatValue(x, pred2) as T;
+    }
+  } catch (e) {
+    return undefined;
+  }
+}
+function set2Step(
+  graph: SyncedGraph, //
+  subj1: NamedNode,
+  pred1: NamedNode,
+  baseName: string,
+  pred2: NamedNode,
+  newObjLiteral: Literal
+) {}
+
+function maybeUriValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): NamedNode | undefined {
+  try {
+    return graph.uriValue(s, p);
+  } catch (e) {
+    return undefined;
+  }
+}
+function maybeStringValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): string | undefined {
+  try {
+    return graph.stringValue(s, p);
+  } catch (e) {
+    return undefined;
+  }
+}
+function maybeFloatValue(graph: SyncedGraph, s: NamedNode, p: NamedNode): number | undefined {
+  try {
+    return graph.floatValue(s, p);
+  } catch (e) {
+    return undefined;
+  }
+}
+
+//////////////////////////////////////
+class EffectFader {
+  constructor(public uri: NamedNode) {}
+  column: string = "unset";
+  effect?: NamedNode;
+  effectAttr?: NamedNode; // :strength
+  setting?: NamedNode; // we assume fader always has exactly one setting
+  value?: number;
+}
 
 @customElement("light9-effect-fader")
 export class Light9EffectFader extends LitElement {
@@ -23,81 +86,85 @@
     `,
   ];
   render() {
+    if (this.conf === undefined || this.conf.value === undefined) {
+      return html`...`;
+    }
     return html`
-      <light9-fader .value=${this.value} @change=${this.onSliderInput}></light9-fader>
-      <div>${this.value.toPrecision(3)}</div>
-      <div>eff: <edit-choice nounlink .uri=${this.effect} @edited=${this.onEffectChange}></edit-choice></div>
-      <div>attr: <edit-choice nounlink .uri=${this.effectAttr} @edited=${this.onEffectAttrChange}></edit-choice></div>
-      <div>Slider ${this.column}</div>
+      <light9-fader .value=${this.conf.value} @change=${this.onSliderInput}></light9-fader>
+      <div>${this.conf.value.toPrecision(3)}</div>
+      <div>effect <edit-choice nounlink .uri=${this.conf.effect} @edited=${this.onEffectChange}></edit-choice></div>
+      <div>attr <edit-choice nounlink .uri=${this.conf.effectAttr} @edited=${this.onEffectAttrChange}></edit-choice></div>
     `;
   }
 
   graph?: SyncedGraph;
   ctx: NamedNode = new NamedNode(showRoot + "/fade");
   @property() uri!: NamedNode;
-  @property() column!: string;
-  @property() effect?: NamedNode;
-  @property() effectAttr?: NamedNode;
-  @state() setting?: NamedNode;
-
-  @property() value: number = 0.0;
+  @state() conf?: EffectFader; // compiled from graph
 
   constructor() {
     super();
     getTopGraph().then((g) => {
       this.graph = g;
-      this.graph.runHandler(this.compile.bind(this, this.graph), `config ${this.uri.value}`);
-      this.graph.runHandler(this.compileValue.bind(this, this.graph), `valueSync ${this.uri.value}`);
+      this.graph.runHandler(this.compile.bind(this, this.graph), `fader config ${this.uri.value}`);
     });
   }
 
   private compile(graph: SyncedGraph) {
     const U = graph.U();
+    this.conf = undefined;
+
+    const conf = new EffectFader(this.uri);
+
     if (!graph.contains(this.uri, U("rdf:type"), U(":Fader"))) {
       // not loaded yet, perhaps
-      this.column = "unset";
-      this.effect = undefined;
-      this.effectAttr = undefined;
       return;
     }
-    this.column = graph.stringValue(this.uri, U(":column"));
-    this.effect = graph.uriValue(this.uri, U(":effect"));
-    this.setting = graph.uriValue(this.uri, U(":setting"));
-    if (this.setting !== undefined) {
-      try {
-        this.effectAttr = graph.uriValue(this.setting, U(":effectAttr"));
-      } catch (e) {
-        this.effectAttr = undefined;
-      }
-    }
+
+    conf.column = maybeStringValue(graph, this.uri, U(":column")) || "unset";
+    conf.effect = maybeUriValue(graph, this.uri, U(":effect"));
+    conf.effectAttr = get2Step(RETURN_URI, graph, this.uri, U(":setting"), U(":effectAttr"));
+
+    this.conf = conf;
+    graph.runHandler(this.compileValue.bind(this, graph, this.conf), `fader config.value ${this.uri.value}`);
   }
 
-  private compileValue(graph: SyncedGraph) {
+  private compileValue(graph: SyncedGraph, conf: EffectFader) {
+    //  external graph change -> conf.value
     const U = graph.U();
-    if (!graph.contains(this.uri, U("rdf:type"), U(":Fader"))) {
-      // not loaded yet
-      // console.timeEnd(`valueSync ${this.uri.value}`)
-      return;
-    }
-    const st = graph.uriValue(this.uri, U(":setting"));
-    this.value = graph.floatValue(st, graph.Uri(":value"));
+    conf.value = get2Step(RETURN_FLOAT, graph, this.uri, U(":setting"), U(":value"));
   }
 
   onSliderInput(ev: CustomEvent) {
+    // slider user input -> graph
     if (this.graph === undefined) {
       return;
     }
     const U = this.graph.U();
-    const prev = this.value;
-    const v: number = ev.detail.value;
-    this.value = parseFloat(v.toPrecision(3)); // rewrite pls
-    if (this.value == prev) {
+    // const prev = this.value;
+    // const v: number = ev.detail.value;
+    // this.value = parseFloat(v.toPrecision(3)); // rewrite pls
+    // if (this.value == prev) {
+    //   return;
+    // }
+    // if (!this.setting) {
+    //   throw new Error("can't make new settings yet");
+    // }
+
+    if (this.conf === undefined) {
       return;
     }
-    if (!this.setting) {
-      throw new Error("can't make new settings yet");
+    let patch = new Patch([], []);
+    let settingNode: NamedNode;
+    const valueTerm = this.graph.LiteralRoundedFloat(ev.detail.value);
+    try {
+      settingNode = this.graph.uriValue(this.uri, U(":setting"));
+    } catch (e) {
+      settingNode = this.graph.nextNumberedResource(U(":fadeset"));
+      patch = patch.update(new Patch([], [new Quad(this.conf.uri, U(":setting"), settingNode, this.ctx)]));
     }
-    this.graph.patchObject(this.setting, this.graph.Uri(":value"), this.graph.LiteralRoundedFloat(this.value), this.ctx);
+    patch = patch.update(this.graph.getObjectPatch(settingNode, this.graph.Uri(":value"), valueTerm, this.ctx));
+    this.graph.applyAndSendPatch(patch);
   }
 
   onEffectChange(ev: CustomEvent) {
@@ -112,11 +179,11 @@
     if (this.graph === undefined) {
       return;
     }
-    const { newValue } = ev.detail;
-    if (this.setting === undefined) {
-      this.setting = this.graph.nextNumberedResource(this.graph.Uri(":fade_set"));
-      this.graph.patchObject(this.uri, this.graph.Uri(":setting"), this.setting, this.ctx);
-    }
-    this.graph.patchObject(this.setting, this.graph.Uri(":effectAttr"), newValue, this.ctx);
+    // const { newValue } = ev.detail;
+    // if (this.setting === undefined) {
+    //   this.setting = this.graph.nextNumberedResource(this.graph.Uri(":fade_set"));
+    //   this.graph.patchObject(this.uri, this.graph.Uri(":setting"), this.setting, this.ctx);
+    // }
+    // this.graph.patchObject(this.setting, this.graph.Uri(":effectAttr"), newValue, this.ctx);
   }
 }
--- a/light9/fade/Light9FadeUi.ts	Thu Jun 01 22:06:34 2023 -0700
+++ b/light9/fade/Light9FadeUi.ts	Fri Jun 02 09:42:18 2023 -0700
@@ -1,16 +1,26 @@
 import debug from "debug";
-import { css, html, LitElement } from "lit";
+import { css, html, LitElement, TemplateResult } from "lit";
 import { customElement, property } from "lit/decorators.js";
-import { NamedNode } from "n3";
+import * as N3 from "n3";
+import { NamedNode, Quad } from "n3";
+import { Patch } from "../web/patch";
 import { getTopGraph } from "../web/RdfdbSyncedGraph";
-import { shortShow } from "../web/show_specific";
+import { showRoot } from "../web/show_specific";
 import { SyncedGraph } from "../web/SyncedGraph";
 export { EditChoice } from "../web/EditChoice";
 export { Light9EffectFader } from "./Light9EffectFader";
 
-debug.enable("*");
+debug.enable("*,autodep");
 const log = debug("fade");
 
+class FadePage {
+  constructor(public uri: NamedNode) {}
+  faders: NamedNode[] = [];
+}
+class FadePages {
+  pages: FadePage[] = [];
+}
+
 @customElement("light9-fade-ui")
 export class Light9FadeUi extends LitElement {
   static styles = [
@@ -27,35 +37,77 @@
 
       <h1>Fade</h1>
 
+      ${(this.fadePages?.pages || []).map(this.renderPage)}
 
-      ${this.faders.map((fd) => html` <light9-effect-fader .uri=${fd}></light9-effect-fader> `)}
+      <div><button @click=${this.addPage}>Add new page</button></div>
     `;
   }
+  private renderPage(page: FadePage): TemplateResult {
+    return html`<div>
+      <fieldset>
+        <legend>Page <resource-display rename .uri=${page.uri}></resource-display></legend>
+        ${page.faders.map((fd) => html` <light9-effect-fader .uri=${fd}></light9-effect-fader> `)}
+      </fieldset>
+    </div>`;
+  }
 
   graph!: SyncedGraph;
+  ctx: NamedNode = new NamedNode(showRoot + "/fade");
 
-  @property() faders: NamedNode[] = [];
+  @property() fadePages?: FadePages;
 
   constructor() {
     super();
     getTopGraph().then((g) => {
       this.graph = g;
-      // todo: start with a page, then find the faders on that page
-      this.faders = [
-        g.Uri(`:show/${shortShow}/fadePage1f0`),
-        g.Uri(`:show/${shortShow}/fadePage1f1`),
-        g.Uri(`:show/${shortShow}/fadePage1f2`),
-        g.Uri(`:show/${shortShow}/fadePage1f3`),
-        g.Uri(`:show/${shortShow}/fadePage1f4`),
-        g.Uri(`:show/${shortShow}/fadePage1f5`),
-        g.Uri(`:show/${shortShow}/fadePage1f6`),
-        g.Uri(`:show/${shortShow}/fadePage1f7`),
-      ];
+      this.graph.runHandler(this.compile.bind(this), `faders layout`);
     });
   }
   connectedCallback(): void {
     super.connectedCallback();
   }
-}
 
+  compile() {
+    const U = this.graph.U();
+    this.fadePages = undefined;
+    const fadePages = new FadePages();
+    for (let page of this.graph.subjects(U("rdf:type"), U(":FadePage"))) {
+      const fp = new FadePage(page as NamedNode);
+      try {
+        for (let fader of this.graph.objects(page, U(":fader"))) {
+          fp.faders.push(fader as NamedNode);
+        }
+        fp.faders.sort((a, b) => {
+          // todo this is supposed to sort by :column so you can reorder
+          return a.value.localeCompare(b.value);
+        });
+        fadePages.pages.push(fp);
+      } catch (e) {}
+    }
+    fadePages.pages.sort((a, b) => {
+      return a.uri.value.localeCompare(b.uri.value);
+    });
+    this.fadePages = fadePages;
+  }
 
+  addPage() {
+    const U = this.graph.U();
+    const uri = this.graph.nextNumberedResource(showRoot + "/fadePage");
+    const adds = [
+      //
+      new Quad(uri, U("rdf:type"), U(":FadePage"), this.ctx),
+      new Quad(uri, U("rdfs:label"), N3.DataFactory.literal("unnamed"), this.ctx),
+    ];
+    for (let n = 1; n <= 8; n++) {
+      const f = this.graph.nextNumberedResource(showRoot + "/fader");
+      const s = this.graph.nextNumberedResource(showRoot + "/faderset");
+      adds.push(new Quad(uri, U(":fader"), f, this.ctx));
+      adds.push(new Quad(f, U("rdf:type"), U(":Fader"), this.ctx));
+      adds.push(new Quad(f, U(":column"), N3.DataFactory.literal("" + n), this.ctx));
+      adds.push(new Quad(f, U(":setting"), s, this.ctx));
+      adds.push(new Quad(s, U(":effectAttr"), U(":strength"), this.ctx));
+      adds.push(new Quad(s, U(":value"), this.graph.LiteralRoundedFloat(0), this.ctx));
+    }
+    this.graph.applyAndSendPatch(new Patch([], adds));
+  }
+}
--- a/light9/fade/Light9Fader.ts	Thu Jun 01 22:06:34 2023 -0700
+++ b/light9/fade/Light9Fader.ts	Fri Jun 02 09:42:18 2023 -0700
@@ -17,7 +17,7 @@
         display: inline-block;
         border: 2px gray inset;
         background: #000;
-        height: 250px;
+        height: 130px;
       }
       #handle {
         background: gray;
@@ -33,7 +33,7 @@
 
   @query("#handle") handleEl!: HTMLElement;
 
-  troughHeight = 250 - 2 - 2 - 5 - 5;
+  troughHeight = 130 - 2 - 2 - 5 - 5;
   handleHeight = 20;
 
   drag?: Drag;
--- a/light9/web/EditChoice.ts	Thu Jun 01 22:06:34 2023 -0700
+++ b/light9/web/EditChoice.ts	Fri Jun 02 09:42:18 2023 -0700
@@ -17,11 +17,11 @@
   const highlight = () => highlightElem.classList.add("dragging");
   const unhighlight = () => highlightElem.classList.remove("dragging");
 
-  senseElem.addEventListener("drag", (event: DragEvent) => {});
+  senseElem.addEventListener("drag", (event: DragEvent) => { });
 
-  senseElem.addEventListener("dragstart", (event: DragEvent) => {});
+  senseElem.addEventListener("dragstart", (event: DragEvent) => { });
 
-  senseElem.addEventListener("dragend", (event: DragEvent) => {});
+  senseElem.addEventListener("dragend", (event: DragEvent) => { });
 
   senseElem.addEventListener("dragover", (event: DragEvent) => {
     event.preventDefault();
@@ -108,7 +108,7 @@
 
   _setUri(u?: NamedNode) {
     this.uri = u;
-    this.dispatchEvent(new CustomEvent("edited", {detail: {newValue: u}}));
+    this.dispatchEvent(new CustomEvent("edited", { detail: { newValue: u } }));
   }
 
   unlink() {