this repo has no description

add multi-select

phaz.uk 60049985 d17ed172

verified
Changed files
+88 -37
src
+54 -13
src/App.tsx
··· 13 13 14 14 import * as keybinds from './keybinds'; 15 15 import { listen } from "@tauri-apps/api/event"; 16 + import { ConfirmationPopup } from "./components/ConfirmationPopup"; 16 17 17 18 let App = () => { 18 - let [ selectedNode, setSelectedNode ] = createSignal<Node | null>(null); 19 + let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([]); 19 20 20 21 let canvas!: HTMLCanvasElement; 21 22 let ctx: CanvasRenderingContext2D; ··· 57 58 } 58 59 59 60 onMount(async () => { 60 - NodeManager.Instance.HookTabChange(() => setSelectedNode(null)); 61 + NodeManager.Instance.HookTabChange(() => setSelectedNodes([])); 61 62 62 63 ctx = canvas.getContext('2d')!; 63 64 ··· 100 101 } 101 102 102 103 if(clickedNode){ 103 - contextMenu.items = NodeContextMenu(clickedNode, selectedNode, setSelectedNode); 104 + contextMenu.items = NodeContextMenu(clickedNode, selectedNodes, setSelectedNodes); 104 105 } else{ 105 106 contextMenu.items = CanvasContextMenu; 106 107 } ··· 121 122 return; 122 123 } 123 124 124 - if(e.shiftKey){ 125 - // TODO: Multi-select 126 - return; 127 - } 128 - 129 125 if(contextMenu.visible){ 130 126 let submenus: ContextMenu[] = []; 131 127 contextMenu.items.map(x => x.menu ? submenus.push(x.menu): null); ··· 160 156 161 157 if(nodes){ 162 158 nodes.map(node => { 163 - node.selected = false; 159 + if(!e.shiftKey)node.selected = false; 164 160 165 161 if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 166 162 e.clientX, e.clientY, ··· 220 216 221 217 movingNode = clickedNode; 222 218 223 - if(clickedNode){ 219 + if(!e.shiftKey){ 220 + if(clickedNode){ 221 + clickedNode.selected = true; 222 + setSelectedNodes([ clickedNode ]); 223 + } else{ 224 + setSelectedNodes([]); 225 + } 226 + } else{ 224 227 clickedNode.selected = true; 225 - setSelectedNode(clickedNode); 228 + 229 + let snodes = selectedNodes(); 230 + if(!snodes.find(x => x.id === clickedNode!.id)){ 231 + snodes.push(clickedNode); 232 + clickedNode.selected = true; 233 + 234 + setSelectedNodes(snodes); 235 + } 226 236 } 227 237 228 238 isMouseDown = true; ··· 230 240 } 231 241 232 242 canvas.onmousemove = ( e ) => { 243 + if(e.shiftKey && isMouseDown){ 244 + let nodes = NodeManager.Instance.GetNodes(); 245 + let hoveredNode: Node | null = null; 246 + 247 + if(nodes){ 248 + nodes.map(node => { 249 + if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 250 + e.clientX, e.clientY, 251 + node.x - 20, node.y, node.w + 40, node.h 252 + )){ 253 + hoveredNode = node; 254 + return; 255 + } 256 + }) 257 + } 258 + 259 + if(hoveredNode !== null){ 260 + let snodes = selectedNodes(); 261 + if(!snodes.find(x => x.id === hoveredNode!.id)){ 262 + snodes.push(hoveredNode); 263 + 264 + // @ts-ignore 265 + hoveredNode.selected = true; 266 + setSelectedNodes(snodes); 267 + } 268 + } 269 + 270 + return; 271 + } 272 + 233 273 if(isMouseDown){ 234 274 if(isDrawing){ 235 275 drawingTo = screenToWorldSpace(canvas, { x: offset[0], y: offset[1], scale }, e.clientX - 10 * scale, e.clientY - 10 * scale) as [ number, number ]; ··· 321 361 isMouseDown = false; 322 362 } 323 363 324 - keybinds.load(selectedNode, setSelectedNode); 364 + keybinds.load(selectedNodes, setSelectedNodes); 325 365 requestAnimationFrame(update); 326 366 327 367 let unlisten_0 = await listen('hide-window', () => { ··· 378 418 379 419 return ( 380 420 <> 421 + <ConfirmationPopup /> 381 422 <TabMenu /> 382 - <ControlBar node={selectedNode} lockMovement={( lock ) => lockMovement = lock} /> 423 + <ControlBar node={selectedNodes} lockMovement={( lock ) => lockMovement = lock} /> 383 424 <canvas ref={canvas}/> 384 425 </> 385 426 );
+11 -2
src/ContextMenu/Node.tsx
··· 3 3 import { PositionInfo } from "../renderer"; 4 4 import { Node } from "../structs/node"; 5 5 6 - export let NodeContextMenu = ( clickedNode: Node, selectedNode: Accessor<Node | null>, setSelectedNode: Setter<Node | null> ) => [ 6 + export let NodeContextMenu = ( clickedNode: Node, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => [ 7 7 { 8 8 text: "Delete Node", 9 9 clicked: ( _e: MouseEvent, _canvas: HTMLCanvasElement, _position: PositionInfo ) => { ··· 20 20 }) 21 21 22 22 let selected = selectedNode(); 23 - if(selected && clickedNode.id === selected.id)setSelectedNode(null); 23 + for (let i = 0; i < selected.length; i++) { 24 + let node = selected[i]; 25 + 26 + if(node.id === clickedNode.id){ 27 + selected.splice(i, 1); 28 + setSelectedNode(selected); 29 + 30 + break; 31 + } 32 + } 24 33 25 34 NodeManager.Instance.RemoveNode(clickedNode!) 26 35 },
+1 -1
src/components/ConfirmationPopup.tsx
··· 10 10 <div class="confirmation-popup"> 11 11 <h2>{ConfirmationManager.Instance.Text()}</h2> 12 12 <p>{ConfirmationManager.Instance.Body()}</p> 13 - 13 + 14 14 <div class="confirmation-buttons"> 15 15 <For each={ConfirmationManager.Instance.Buttons()}> 16 16 { item =>
+7 -7
src/components/ControlBar.tsx
··· 9 9 import { NodeManager } from '../Mangers/NodeManager'; 10 10 11 11 export interface ControlBarProps{ 12 - node: Accessor<Node | null>, 12 + node: Accessor<Node[]>, 13 13 lockMovement: ( lock: boolean ) => void 14 14 } 15 15 ··· 20 20 21 21 return ( 22 22 <div class="control-bar"> 23 - <For each={props.node()?.statics}> 23 + <For each={props.node()[0]?.statics}> 24 24 { ( item ) => { 25 25 let [ popupOpen, setPopupOpen ] = createSignal(false); 26 26 ··· 36 36 value={item.value || ''} 37 37 onChange={( el ) => { 38 38 let value = el.target.value; 39 - let node = props.node()!; 39 + let node = props.node()[0]!; 40 40 41 41 item.value = value; 42 42 node.onStaticsUpdate(node); ··· 54 54 value={item.value !== undefined ? item.value : ''} 55 55 onChange={( el ) => { 56 56 let value = el.target.value; 57 - let node = props.node()!; 57 + let node = props.node()[0]!; 58 58 59 59 item.value = parseInt(value); 60 60 node.onStaticsUpdate(node); ··· 72 72 value={item.value !== undefined ? item.value : ''} 73 73 onChange={( el ) => { 74 74 let value = el.target.value; 75 - let node = props.node()!; 75 + let node = props.node()[0]!; 76 76 77 77 item.value = parseFloat(value); 78 78 node.onStaticsUpdate(node); ··· 92 92 return addresses.map(x => x.address).filter(x => x.toLowerCase().includes(text.toLowerCase())); 93 93 }} 94 94 change={( text ) => { 95 - let node = props.node()!; 95 + let node = props.node()[0]!; 96 96 97 97 item.value = text; 98 98 node.onStaticsUpdate(node); ··· 118 118 }} 119 119 value={item.value} 120 120 changed={( value ) => { 121 - let node = props.node()!; 121 + let node = props.node()[0]!; 122 122 123 123 item.value = value; 124 124 node.onStaticsUpdate(node);
+15 -14
src/keybinds.ts
··· 4 4 5 5 let isKeyDown: any = {}; 6 6 7 - export let load = ( selectedNode: Accessor<Node | null>, setSelectedNode: Setter<Node | null> ) => { 7 + export let load = ( selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => { 8 8 // TODO: Copy / paste 9 9 // TODO: Add undo / redo -ing 10 10 ··· 15 15 16 16 switch(e.key){ 17 17 case 'Delete': 18 - let node = selectedNode(); 19 - if(!node)return; 18 + let nodes = selectedNode(); 19 + for(let node of nodes){ 20 + node.inputs.map(input => { 21 + input.connections.map(partner => { 22 + partner.connections = partner.connections.filter(x => x != input); 23 + }) 24 + }) 20 25 21 - node.inputs.map(input => { 22 - input.connections.map(partner => { 23 - partner.connections = partner.connections.filter(x => x != input); 26 + node.outputs.map(output => { 27 + output.connections.map(partner => { 28 + partner.connections = partner.connections.filter(x => x != output); 29 + }) 24 30 }) 25 - }) 26 31 27 - node.outputs.map(output => { 28 - output.connections.map(partner => { 29 - partner.connections = partner.connections.filter(x => x != output); 30 - }) 31 - }) 32 + NodeManager.Instance.RemoveNode(node); 33 + } 32 34 33 - setSelectedNode(null); 34 - NodeManager.Instance.RemoveNode(node); 35 + setSelectedNode([]); 35 36 break; 36 37 case 's': 37 38 if(e.ctrlKey){