From 3af06eb52a7c7496f7b2d630bca1b170b71707b6 Mon Sep 17 00:00:00 2001
From: Maximilian Giller <m.giller@tu-bs.de>
Date: Sun, 26 Jun 2022 18:58:45 +0200
Subject: [PATCH] Moved some variables to state and tried to implemented custom
 node and link rendering, but doesn't get called for some reason

---
 src/editor/js/components/editor.tsx | 171 +++++++++--
 src/editor/js/state.ts              | 435 ++++++----------------------
 2 files changed, 235 insertions(+), 371 deletions(-)

diff --git a/src/editor/js/components/editor.tsx b/src/editor/js/components/editor.tsx
index 30a4587..e6fc0ff 100644
--- a/src/editor/js/components/editor.tsx
+++ b/src/editor/js/components/editor.tsx
@@ -1,5 +1,4 @@
 import React from "react";
-import { State } from "../state";
 import * as Interactions from "../interactions";
 import { Graph } from "../structures/graph/graph";
 import { loadGraphJson } from "../../../datasets";
@@ -9,19 +8,20 @@ import "./editor.css";
 import ReactForceGraph2d from "react-force-graph-2d";
 import { Node } from "../structures/graph/node";
 import { HistoryNavigator } from "./historynavigator";
+import { GraphElement } from "../structures/graph/graphelement";
 
 type propTypes = any;
 type stateTypes = {
     graph: Graph;
+    visibleLabels: boolean;
+    selectedNode: Node;
+    keys: { [name: string]: boolean };
 };
 
 export class Editor extends React.PureComponent<propTypes, stateTypes> {
     private defaultWarmupTicks = 100;
     private warmupTicks = 100;
 
-    private keyStates: { [name: string]: boolean } = {};
-    private selectedNode: Node;
-
     constructor(props: propTypes) {
         super(props);
         this.loadGraph = this.loadGraph.bind(this);
@@ -32,10 +32,16 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
         this.handleKeyDown = this.handleKeyDown.bind(this);
         this.handleKeyUp = this.handleKeyUp.bind(this);
         this.forceUpdate = this.forceUpdate.bind(this);
+        this.isHighlighted = this.isHighlighted.bind(this);
+        this.handleNodeCanvasObject = this.handleNodeCanvasObject.bind(this);
+        this.handleLinkCanvasObject = this.handleLinkCanvasObject.bind(this);
 
         // Set as new state
         this.state = {
             graph: undefined,
+            visibleLabels: true,
+            selectedNode: undefined,
+            keys: {},
         };
 
         Interactions.initInteractions();
@@ -72,9 +78,6 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
             return false;
         }
 
-        //     .linkColor((link: any) => Editor.globalState.linkColor(link))
-        //     .nodeColor((node: any) => Editor.globalState.nodeColor(node))
-        //     .onNodeClick((node: any) => Editor.globalState.onNodeClick(node))
         //     .onNodeDragEnd((node: any, translate: any) =>
         //         Editor.globalState.onNodeDragEnd(node, translate)
         //     )
@@ -91,18 +94,6 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
         //             this.extractPositions(event)
         //         )
         //     )
-        //     .nodeCanvasObjectMode((node: any) =>
-        //         Editor.globalState.nodeCanvasObjectMode(node)
-        //     )
-        //     .nodeCanvasObject((node: any, ctx: any, globalScale: any) =>
-        //         Editor.globalState.nodeCanvasObject(node, ctx, globalScale)
-        //     )
-        //     .linkCanvasObjectMode((link: any) =>
-        //         Editor.globalState.linkCanvasObjectMode(link)
-        //     )
-        //     .linkCanvasObject((link: any, ctx: any, globalScale: any) =>
-        //         Editor.globalState.linkCanvasObject(link, ctx, globalScale)
-        //     )
         //     .onLinkClick((link: any) => Editor.globalState.onLinkClick(link));
 
         // Set as new state
@@ -122,22 +113,62 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
 
     private handleKeyDown(event: KeyboardEvent) {
         const key: string = event.key;
-        this.keyStates[key] = true;
+
+        const keys = this.state.keys;
+        keys[key] = true;
+
+        this.setState({
+            keys: keys,
+        });
     }
 
     private handleKeyUp(event: KeyboardEvent) {
         const key: string = event.key;
-        this.keyStates[key] = false;
+
+        const keys = this.state.keys;
+        keys[key] = false;
+
+        this.setState({
+            keys: keys,
+        });
     }
 
     /**
      * Propagates the changed state of the graph.
      */
     private onHistoryChange() {
-        this.selectedNode = undefined;
+        this.setState({
+            selectedNode: undefined,
+        });
         this.forceUpdate();
     }
 
+    /**
+     * Should a given element be highlighted in rendering or not.
+     * @param element Element that should, or should not be highlighted.
+     * @returns True, if element should be highlighted.
+     */
+    private isHighlighted(element: GraphElement): boolean {
+        if (this.state.selectedNode == undefined) {
+            // Default to false if nothing selected.
+            return false;
+        }
+
+        if (element.node) {
+            // Is node
+            return element.equals(this.state.selectedNode);
+        } else if (element.link) {
+            // Is link
+            // Is it one of the adjacent links?
+            const found = this.state.selectedNode.links.find(
+                this.state.selectedNode.equals
+            );
+            return found !== undefined;
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Calculates the corresponding coordinates for a click event for easier further processing.
      * @param event The corresponding click event.
@@ -157,14 +188,100 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
     }
 
     private handleNodeClick(node: Node) {
-        if (this.keyStates["Control"]) {
+        if (this.state.keys["Control"]) {
             node.delete();
         } else {
-            this.selectedNode = node;
+            this.setState({
+                selectedNode: node,
+            });
         }
         this.forceUpdate();
     }
 
+    private handleNodeCanvasObject(node: any, ctx: any, globalScale: any) {
+        // add ring just for highlighted nodes
+        if (this.isHighlighted(node)) {
+            ctx.beginPath();
+            ctx.arc(node.x, node.y, 4 * 0.6, 0, 2 * Math.PI, false);
+            ctx.fillStyle = "red";
+            ctx.fill();
+        }
+
+        // Draw image
+        const imageSize = 12;
+        if (node.icon !== undefined) {
+            const img = new Image();
+            img.src = node.icon.link;
+
+            ctx.drawImage(
+                img,
+                node.x - imageSize / 2,
+                node.y - imageSize / 2,
+                imageSize,
+                imageSize
+            );
+        }
+
+        // Draw label
+        if (this.state.visibleLabels) {
+            const label = node.label;
+            const fontSize = 11 / globalScale;
+            ctx.font = `${fontSize}px Sans-Serif`;
+            const textWidth = ctx.measureText(label).width;
+            const bckgDimensions = [textWidth, fontSize].map(
+                (n) => n + fontSize * 0.2
+            ); // some padding
+
+            const nodeHeightOffset = imageSize / 3 + bckgDimensions[1];
+            ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
+            ctx.fillRect(
+                node.x - bckgDimensions[0] / 2,
+                node.y - bckgDimensions[1] / 2 + nodeHeightOffset,
+                ...bckgDimensions
+            );
+
+            ctx.textAlign = "center";
+            ctx.textBaseline = "middle";
+            ctx.fillStyle = "white";
+            ctx.fillText(label, node.x, node.y + nodeHeightOffset);
+        }
+
+        // TODO: Render label as always visible
+    }
+
+    private handleLinkCanvasObject(link: any, ctx: any): any {
+        // Links already initialized?
+        if (link.source.x === undefined) {
+            return undefined;
+        }
+
+        // Draw gradient link
+        const gradient = ctx.createLinearGradient(
+            link.source.x,
+            link.source.y,
+            link.target.x,
+            link.target.y
+        );
+        // Have reversed colors
+        // Color at source node referencing the target node and vice versa
+        gradient.addColorStop("0", link.target.type.color);
+        gradient.addColorStop("1", link.source.type.color);
+
+        ctx.beginPath();
+        ctx.moveTo(link.source.x, link.source.y);
+        ctx.lineTo(link.target.x, link.target.y);
+        ctx.strokeStyle = gradient;
+        ctx.stroke();
+
+        // Only render strokes on last link
+        // var lastLink = graph.data[Graph.GRAPH_LINKS][graph.data[Graph.GRAPH_LINKS].length - 1];
+        // if (link === lastLink) {
+        //     ctx.stroke();
+        // }
+
+        return undefined;
+    }
+
     private handleEngineStop() {
         // Only do something on first stop for each graph
         if (this.warmupTicks <= 0) {
@@ -192,7 +309,7 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
                         />
                         <hr />
                         <NodeDetails
-                            selectedNode={this.selectedNode}
+                            selectedNode={this.state.selectedNode}
                             allTypes={
                                 this.state.graph ? this.state.graph.types : []
                             }
@@ -207,6 +324,10 @@ export class Editor extends React.PureComponent<propTypes, stateTypes> {
                             cooldownTicks={0}
                             warmupTicks={this.warmupTicks}
                             onEngineStop={this.handleEngineStop}
+                            nodeCanvasObject={this.handleNodeCanvasObject}
+                            nodeCanvasObjectMode={"after"}
+                            linkCanvasObject={this.handleLinkCanvasObject}
+                            linkCanvasObjectMode={"replace"}
                         />
                     ) : undefined}
                 </div>
diff --git a/src/editor/js/state.ts b/src/editor/js/state.ts
index 04710bd..335c8ee 100644
--- a/src/editor/js/state.ts
+++ b/src/editor/js/state.ts
@@ -1,346 +1,89 @@
-import Tool from "./tools/tool";
-import UndoTool from "./tools/undotool";
-import RedoTool from "./tools/redotool";
-import SelectTool from "./tools/selecttool";
-import DeleteTool from "./tools/deletetool";
-import AddNodeTool from "./tools/addnodetool";
-import ConnectTool from "./tools/connecttool";
-import SettingsTool from "./tools/settingstool";
-import SaveTool from "./tools/savetool";
-import Toolbar from "./toolbar";
-import { DRAG_THRESHOLD_2D } from "../../config";
-import { GraphElement } from "./structures/graph/graphelement";
-import { Node } from "./structures/graph/node";
-
-export const TOOLS = {
-    undo: new UndoTool("undo"),
-    redo: new RedoTool("redo"),
-    select: new SelectTool("select"),
-    delete: new DeleteTool("delete"),
-    addnode: new AddNodeTool("addnode"),
-    connect: new ConnectTool("connect"),
-    settings: new SettingsTool("settings"),
-    save: new SaveTool("save"),
-};
-
-export const CONTEXT = {
-    node: "node",
-    link: "link",
-    mixed: "mixed",
-    nothing: "nothing",
-};
-
-export class State extends Tool {
-    display: Toolbar;
-    tool: Tool;
-    previousTool: Tool;
-    selectedItem: GraphElement;
-    selectedItems: Set<GraphElement>;
-    itemsContext: any;
-    labelVisible: boolean;
-    keyStates: any;
-
-    constructor() {
-        super("State");
-
-        this.display = new Toolbar(TOOLS);
-
-        this.tool = undefined;
-        this.setTool(TOOLS.select);
-
-        // Shared variables
-        this.selectedItem = undefined;
-        this.selectedItems = new Set();
-        this.itemsContext = CONTEXT.nothing;
-        this.labelVisible = true;
-
-        this.keyStates = {};
-    }
-
-    setTool(tool: Tool) {
-        if (this.tool === tool) {
-            return;
-        }
-
-        if (this.tool !== undefined) {
-            this.tool.deactivateTool(tool);
-        }
-
-        this.previousTool = this.tool;
-        this.tool = tool;
-        this.display.setSelectedTool(tool);
-
-        if (this.tool !== undefined) {
-            this.tool.activateTool();
-        }
-    }
-
-    setSelectedItem(item: GraphElement) {
-        this.selectedItem = item;
-    }
-
-    addSelectedItem(item: GraphElement) {
-        this.selectedItems.add(item);
-    }
-
-    addSelectedItems(items: GraphElement) {
-        Object.values(items).forEach((item) => {
-            this.selectedItems.add(item);
-        });
-    }
-
-    removeSelectedItem(item: GraphElement) {
-        this.selectedItems.delete(item);
-    }
-
-    clearSelectedItems() {
-        this.selectedItems.clear();
-        this.itemsContext = CONTEXT.nothing;
-    }
-
-    onNodeClick(node: GraphElement) {
-        this.tool.onNodeClick(node);
-    }
-
-    onNodeDragEnd(node: any, translate: any) {
-        // Handle as click event, if drag distance under a certain threshold
-        const distanceDragged = Math.sqrt(
-            Math.pow(translate.x, 2) + Math.pow(translate.y, 2)
-        );
-        if (distanceDragged < DRAG_THRESHOLD_2D) {
-            this.onNodeClick(node);
-            return;
-        } else {
-            this.tool.onNodeDragEnd(node, translate);
-        }
-    }
-
-    onLinkClick(link: any) {
-        this.tool.onLinkClick(link);
-    }
-
-    linkColor(link: any) {
-        return "red";
-    }
-
-    nodeColor(node: any) {
-        return "black";
-    }
-
-    onKeyDown(key: any) {
-        const id = this.getKeyId(key);
-        const previous = this.keyStates[id];
-
-        this.keyStates[id] = true;
-
-        if (previous !== true) {
-            this.tool.onKeyDown(key);
-        }
-    }
-
-    onKeyUp(key: any) {
-        const id = this.getKeyId(key);
-        const previous = this.keyStates[id];
-
-        this.keyStates[id] = false;
-
-        if (previous !== false) {
-            this.tool.onKeyUp(key);
-        }
-    }
-
-    getKeyId(key: any) {
-        return key.keyCode;
-    }
-
-    nodeCanvasObject(node: any, ctx: any, globalScale: any) {
-        const toolValue = this.tool.nodeCanvasObject(node, ctx);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        // TODO: Clean up function
-
-        // add ring just for highlighted nodes
-        if (this.selectedItem === node || this.selectedItems.has(node)) {
-            ctx.beginPath();
-            ctx.arc(node.x, node.y, 4 * 0.6, 0, 2 * Math.PI, false);
-            ctx.fillStyle = this.selectedItem === node ? "red" : "white";
-            ctx.fill();
-        }
-
-        // Draw image
-        const imageSize = 12;
-        if (node.icon !== undefined) {
-            const img = new Image();
-            img.src = node.icon.link;
-
-            ctx.drawImage(
-                img,
-                node.x - imageSize / 2,
-                node.y - imageSize / 2,
-                imageSize,
-                imageSize
-            );
-        }
-
-        // Draw label
-        if (this.labelVisible) {
-            const label = node.label;
-            const fontSize = 11 / globalScale;
-            ctx.font = `${fontSize}px Sans-Serif`;
-            const textWidth = ctx.measureText(label).width;
-            const bckgDimensions = [textWidth, fontSize].map(
-                (n) => n + fontSize * 0.2
-            ); // some padding
-
-            const nodeHeightOffset = imageSize / 3 + bckgDimensions[1];
-            ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
-            ctx.fillRect(
-                node.x - bckgDimensions[0] / 2,
-                node.y - bckgDimensions[1] / 2 + nodeHeightOffset,
-                ...bckgDimensions
-            );
-
-            ctx.textAlign = "center";
-            ctx.textBaseline = "middle";
-            ctx.fillStyle = "white";
-            ctx.fillText(label, node.x, node.y + nodeHeightOffset);
-        }
-
-        // TODO: Render label as always visible
-    }
-
-    nodePointerAreaPaint(node: any, color: any, ctx: any) {
-        const toolValue = this.tool.nodePointerAreaPaint(node, color, ctx);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        const imageSize = 12;
-        ctx.fillStyle = color;
-        ctx.fillRect(
-            node.x - imageSize / 2,
-            node.y - imageSize / 2,
-            imageSize,
-            imageSize
-        ); // draw square as pointer trap
-    }
-
-    nodeCanvasObjectMode(node: any) {
-        const toolValue = this.tool.nodeCanvasObjectMode(node);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        return "after";
-    }
-
-    linkCanvasObjectMode(link: any) {
-        const toolValue = this.tool.linkCanvasObjectMode(link);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        return "replace";
-    }
-
-    linkCanvasObject(link: any, ctx: any, globalScale: any) {
-        const toolValue = this.tool.linkCanvasObject(link, ctx);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        // Links already initialized?
-        if (link.source.x === undefined) {
-            return undefined;
-        }
-
-        // Draw gradient link
-        const gradient = ctx.createLinearGradient(
-            link.source.x,
-            link.source.y,
-            link.target.x,
-            link.target.y
-        );
-        // Have reversed colors
-        // Color at source node referencing the target node and vice versa
-        gradient.addColorStop("0", link.target.type.color);
-        gradient.addColorStop("1", link.source.type.color);
-
-        ctx.beginPath();
-        ctx.moveTo(link.source.x, link.source.y);
-        ctx.lineTo(link.target.x, link.target.y);
-        ctx.strokeStyle = gradient;
-        ctx.stroke();
-
-        // Only render strokes on last link
-        // var lastLink = graph.data[Graph.GRAPH_LINKS][graph.data[Graph.GRAPH_LINKS].length - 1];
-        // if (link === lastLink) {
-        //     ctx.stroke();
-        // }
-
-        return undefined;
-    }
-
-    linkWidth(link: any) {
-        const toolValue = this.tool.linkWidth(link);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        return this.isLinkHighlighted(link) ? 2 : 1;
-    }
-
-    linkDirectionalParticles() {
-        const toolValue = this.tool.linkDirectionalParticles();
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        return 4;
-    }
-
-    linkDirectionalParticleWidth(link: any) {
-        const toolValue = this.tool.linkDirectionalParticleWidth(link);
-
-        if (toolValue !== undefined) {
-            return toolValue;
-        }
-
-        return this.isLinkHighlighted(link) ? 4 : 0;
-    }
-
-    onBackgroundClick(event: any, positions: any) {
-        this.tool.onBackgroundClick(event, positions);
-    }
-
-    redraw() {
-        this.display.setSelectedTool(this.tool);
-    }
-
-    isLinkHighlighted(link: any) {
-        return false;   // Code after that is causing issues
-
-        if (this.selectedItem === link) {
-            return true;
-        }
-
-        if (this.selectedItem.node) {
-            return (this.selectedItem as Node).links.includes(link);
-        }
-
-        return false;
-    }
-
-    setLabelVisibility(visibility: boolean) {
-        this.labelVisible = visibility;
-    }
-}
+// export const TOOLS = {
+//     undo: new UndoTool("undo"),
+//     redo: new RedoTool("redo"),
+//     select: new SelectTool("select"),
+//     delete: new DeleteTool("delete"),
+//     addnode: new AddNodeTool("addnode"),
+//     connect: new ConnectTool("connect"),
+//     settings: new SettingsTool("settings"),
+//     save: new SaveTool("save"),
+// };
+// export class State extends Tool {
+//     onNodeDragEnd(node: any, translate: any) {
+//         // Handle as click event, if drag distance under a certain threshold
+//         const distanceDragged = Math.sqrt(
+//             Math.pow(translate.x, 2) + Math.pow(translate.y, 2)
+//         );
+//         if (distanceDragged < DRAG_THRESHOLD_2D) {
+//             this.onNodeClick(node);
+//             return;
+//         } else {
+//             this.tool.onNodeDragEnd(node, translate);
+//         }
+//     }
+
+//     onKeyDown(key: any) {
+//         const previous = this.keyStates[id];
+//         if (previous !== true) {
+//             this.tool.onKeyDown(key);
+//         }
+//     }
+
+//     onKeyUp(key: any) {
+//         const previous = this.keyStates[id];
+//         if (previous !== false) {
+//             this.tool.onKeyUp(key);
+//         }
+//     }
+
+//     nodePointerAreaPaint(node: any, color: any, ctx: any) {
+//         const toolValue = this.tool.nodePointerAreaPaint(node, color, ctx);
+
+//         if (toolValue !== undefined) {
+//             return toolValue;
+//         }
+
+//         const imageSize = 12;
+//         ctx.fillStyle = color;
+//         ctx.fillRect(
+//             node.x - imageSize / 2,
+//             node.y - imageSize / 2,
+//             imageSize,
+//             imageSize
+//         ); // draw square as pointer trap
+//     }
+
+//     linkWidth(link: any) {
+//         const toolValue = this.tool.linkWidth(link);
+
+//         if (toolValue !== undefined) {
+//             return toolValue;
+//         }
+
+//         return this.isLinkHighlighted(link) ? 2 : 1;
+//     }
+
+//     linkDirectionalParticles() {
+//         const toolValue = this.tool.linkDirectionalParticles();
+
+//         if (toolValue !== undefined) {
+//             return toolValue;
+//         }
+
+//         return 4;
+//     }
+
+//     linkDirectionalParticleWidth(link: any) {
+//         const toolValue = this.tool.linkDirectionalParticleWidth(link);
+
+//         if (toolValue !== undefined) {
+//             return toolValue;
+//         }
+
+//         return this.isLinkHighlighted(link) ? 4 : 0;
+//     }
+
+//     onBackgroundClick(event: any, positions: any) {
+//         this.tool.onBackgroundClick(event, positions);
+//     }
+// }
-- 
GitLab