From 823f3d7730ee5eed720caad8539c0ded4a55933a Mon Sep 17 00:00:00 2001 From: Peaceultime Date: Sun, 12 Jan 2025 23:27:34 +0100 Subject: [PATCH] Improve history handling, add color picking and node creation. --- components/CanvasEditor.vue | 296 +++++++++++++++++-------- components/canvas/CanvasNodeEditor.vue | 63 ++++-- components/canvas/CanvasRenderer.vue | 2 +- db.sqlite | Bin 585728 -> 585728 bytes db.sqlite-shm | Bin 32768 -> 32768 bytes db.sqlite-wal | Bin 1227792 -> 1079472 bytes package.json | 2 +- 7 files changed, 244 insertions(+), 119 deletions(-) diff --git a/components/CanvasEditor.vue b/components/CanvasEditor.vue index 9f3bbb7..0d80bc4 100644 --- a/components/CanvasEditor.vue +++ b/components/CanvasEditor.vue @@ -12,18 +12,31 @@ interface ActionMap { move: Position; edit: string; resize: string; + remove: CanvasNode | undefined; + create: CanvasNode | undefined; + property: CanvasNode; } type Action = keyof ActionMap; -interface HistoryAction +interface HistoryEvent { event: T; - element: number | number[]; + actions: HistoryAction[]; +} +interface HistoryAction +{ + element: number; from: ActionMap[T]; to: ActionMap[T]; } const cancelEvent = (e: Event) => e.preventDefault(); const stopPropagation = (e: Event) => e.stopImmediatePropagation(); +function getID(length: number) +{ + for (var id = [], i = 0; i < length; i++) + id.push((16 * Math.random() | 0).toString(16)); + return id.join(""); +} function center(touches: TouchList): Position { const pos = { x: 0, y: 0 }; @@ -70,9 +83,11 @@ const edges = computed(() => { }); }); -const history = ref([]); + + +const history = ref([]); const historyPos = ref(-1); -const lastActiveAction = computed(() => history.value.length > 0 && historyPos.value > -1 ? history.value[historyPos.value] : undefined); +const historyCursor = computed(() => history.value.length > 0 && historyPos.value > -1 ? history.value[historyPos.value] : undefined); const reset = (_: MouseEvent) => { zoom.value = minZoom.value; @@ -81,11 +96,11 @@ const reset = (_: MouseEvent) => { dispY.value = 0; } -function addAction(event: Action, element: number | number[], from: ActionMap[T], to: ActionMap[T]) +function addAction(event: T, actions: HistoryAction[]) { historyPos.value++; history.value.splice(historyPos.value, history.value.length - historyPos.value); - history.value[historyPos.value] = { event, element, from, to }; + history.value[historyPos.value] = { event, actions }; } onMounted(() => { let lastX = 0, lastY = 0, lastDistance = 0; @@ -184,49 +199,73 @@ onMounted(() => { }; }); -function move(index: number, x: number, y: number) +function moveNode(index: number[], deltax: number, deltay: number) { - const node = canvas.value.nodes[index]; - const oldx = node.x, oldy = node.y; - - forElements(index, (e) => { - e.x -= x / zoom.value; - e.y -= y / zoom.value; - }); - - if(lastActiveAction.value && lastActiveAction.value.event === 'move' && lastActiveAction.value.element === index) + const actions: HistoryAction<'move'>[] = []; + for(const i of index) { - const action = lastActiveAction.value as HistoryAction<'move'>; + const node = canvas.value.nodes[i]; - action.to.x -= x / zoom.value; - action.to.y -= y / zoom.value; - } - else - { - addAction('move', index, { x: oldx, y: oldy }, { x: canvas.value.nodes[index].x, y: canvas.value.nodes[index].y }); + actions.push({ element: i, from: { x: node.x - deltax, y: node.y - deltay }, to: { x: node.x, y: node.y } }); } + + addAction('move', actions); } -function select(node: CanvasNode, index: number, event: Event) +function selectNode(index: number) { if(focusing.value !== index) { - unselect(); + unselectNode(); } - nodes.value![index]?.dom?.addEventListener('click', stopPropagation); - canvasRef.value?.addEventListener('click', unselect, { once: true }); + nodes.value![index]?.dom?.addEventListener('click', stopPropagation, { passive: true }); + canvasRef.value?.addEventListener('click', unselectNode, { once: true }); focusing.value = index; } -function edit(node: CanvasNode, index: number, event: Event) +function editNode(index: number) { - nodes.value![index]?.dom?.addEventListener('wheel', stopPropagation); - canvasRef.value?.addEventListener('click', unselect, { once: true }); + nodes.value![index]?.dom?.addEventListener('wheel', stopPropagation, { passive: true }); + nodes.value![index]?.dom?.addEventListener('dblclick', stopPropagation, { passive: true }); + canvasRef.value?.addEventListener('click', unselectNode, { once: true }); editing.value = index; } +function addNode(e: MouseEvent) +{ + let box = canvasRef.value?.getBoundingClientRect()!; + canvas.value.nodes.push({ id: getID(16), x: (e.layerX / zoom.value) - box.width / 2 - 50, y: (e.layerY / zoom.value) - box.height / 2 - 25, width: 100, height: 50, type: 'text' }); +} +function removeNode(index: number[]) +{ + /*const actions: HistoryAction<'remove'>[] = []; + unselectNode(); -const unselect = () => { + for(const i of index) + { + const [node] = canvas.value.nodes.splice(i, 1); + actions.push({ element: i, from: node, to: undefined }); + + console.log("Removing %s", i); + } + + addAction('remove', actions);*/ +} +function editNodeProperty(index: number[], property: T, value: CanvasNode[T]) +{ + const actions: HistoryAction<'remove'>[] = []; + + for(const i of index) + { + const copy = JSON.parse(JSON.stringify(canvas.value.nodes[i])) as CanvasNode; + canvas.value.nodes[i][property] = value; + actions.push({ element: i, from: copy, to: canvas.value.nodes[i] }); + } + + addAction('property', actions); +} + +const unselectNode = () => { if(focusing.value !== undefined) { nodes.value![focusing.value]?.dom?.removeEventListener('click', stopPropagation); @@ -236,44 +275,52 @@ const unselect = () => { if(editing.value !== undefined) { - debugger; nodes.value![editing.value]?.dom?.removeEventListener('wheel', stopPropagation); + nodes.value![editing.value]?.dom?.removeEventListener('dblclick', stopPropagation); nodes.value![editing.value]?.dom?.removeEventListener('click', stopPropagation); nodes.value![editing.value]?.unselect(); } editing.value = undefined; }; const undo = () => { - if(!lastActiveAction.value) + if(!historyCursor.value) return; - switch(lastActiveAction.value.event) + for(const action of historyCursor.value.actions) { - case 'move': + switch(historyCursor.value.event) { - const action = lastActiveAction.value as HistoryAction<'move'>; - - const x = action.to.x - action.from.x, y = action.to.y - action.from.y; - - forElements(action.element, (e) => { - e.x -= x; - e.y -= y; - }); - - break; - } - case 'edit': - { - const action = lastActiveAction.value as HistoryAction<'edit'>; - - forElements(action.element, (e) => { - e.text = action.from; - }); - break; + case 'move': + { + const a = action as HistoryAction<'move'>; + canvas.value.nodes[action.element].x = a.from.x; + canvas.value.nodes[action.element].y = a.from.y; + break; + } + case 'edit': + { + const a = action as HistoryAction<'edit'>; + canvas.value.nodes[action.element].label = a.from; + break; + } + case 'remove': + { + const a = action as HistoryAction<'remove'>; + canvas.value.nodes.splice(action.element, 0, a.from!); + break; + } + case 'property': + { + const a = action as HistoryAction<'property'>; + canvas.value.nodes[action.element] = a.from; + break; + } } } historyPos.value--; + + console.log(historyPos.value, history.value.length); }; const redo = () => { if(!history.value || history.value.length - 1 <= historyPos.value) @@ -281,61 +328,56 @@ const redo = () => { historyPos.value++; - if(!lastActiveAction.value) + if(!historyCursor.value) { historyPos.value--; return; } - switch(lastActiveAction.value.event) + for(const action of historyCursor.value.actions) { - case 'move': + switch(historyCursor.value.event) { - const action = lastActiveAction.value as HistoryAction<'move'>; - - const x = action.from.x - action.to.x, y = action.from.y - action.to.y; - - forElements(action.element, (e) => { - e.x -= x; - e.y -= y; - }); - break; - } - case 'edit': - { - const action = lastActiveAction.value as HistoryAction<'edit'>; - - forElements(action.element, (e) => { - e.text = action.to; - }); - break; + case 'move': + { + const a = action as HistoryAction<'move'>; + canvas.value.nodes[action.element].x = a.to.x; + canvas.value.nodes[action.element].y = a.to.y; + break; + } + case 'edit': + { + const a = action as HistoryAction<'edit'>; + canvas.value.nodes[action.element].label = a.to; + break; + } + case 'remove': + { + const a = action as HistoryAction<'remove'>; + canvas.value.nodes.splice(action.element, 1); + break; + } + case 'property': + { + const a = action as HistoryAction<'property'>; + canvas.value.nodes[action.element] = a.to; + break; + } } } + + console.log(historyPos.value, history.value.length); }; useShortcuts({ meta_z: undo, meta_y: redo, -}) - -function forElements(element: number | number[], fn: (e: CanvasNode) => void) -{ - if(Array.isArray(element)) - { - for(const e of element) - { - fn(canvas.value.nodes[e]); - } - } - else - { - fn(canvas.value.nodes[element]); - } -} + Delete: () => { if(focusing.value !== undefined) { removeNode([focusing.value]) } } +});