Skip to content

Commit 0ea70ff

Browse files
Merge remote-tracking branch 'origin/layer-rearranging' into nixpkgs-testing
2 parents 3cfaccc + 7c08705 commit 0ea70ff

File tree

2 files changed

+89
-67
lines changed

2 files changed

+89
-67
lines changed

frontend/src/components/layout/LayoutCol.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
on:pointerdown
4848
on:pointerenter
4949
on:pointerleave
50+
on:pointermove
51+
on:pointerup
5052
on:scroll
5153
{...$$restProps}
5254
>

frontend/src/components/panels/Layers.svelte

Lines changed: 87 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { getContext, onMount, onDestroy, tick } from "svelte";
33
44
import type { Editor } from "@graphite/editor";
5-
import { beginDraggingElement } from "@graphite/io-managers/drag";
65
import {
76
defaultWidgetLayout,
87
patchWidgetLayout,
@@ -50,10 +49,12 @@
5049
let layers: LayerListingInfo[] = [];
5150
5251
// Interactive dragging
53-
let draggable = true;
5452
let draggingData: undefined | DraggingData = undefined;
5553
let fakeHighlightOfNotYetSelectedLayerBeingDragged: undefined | bigint = undefined;
5654
let dragInPanel = false;
55+
let isDragging = false;
56+
let dragStartPosition = { x: 0, y: 0 };
57+
const dragThreshold = 5;
5758
5859
// Interactive clipping
5960
let layerToClipUponClick: LayerListingInfo | undefined = undefined;
@@ -185,8 +186,6 @@
185186
186187
async function onEditLayerName(listing: LayerListingInfo) {
187188
if (listing.editingName) return;
188-
189-
draggable = false;
190189
listing.editingName = true;
191190
layers = layers;
192191
@@ -200,8 +199,6 @@
200199
function onEditLayerNameChange(listing: LayerListingInfo, e: Event) {
201200
// Eliminate duplicate events
202201
if (!listing.editingName) return;
203-
204-
draggable = true;
205202
listing.editingName = false;
206203
layers = layers;
207204
@@ -211,7 +208,6 @@
211208
}
212209
213210
async function onEditLayerNameDeselect(listing: LayerListingInfo) {
214-
draggable = true;
215211
listing.editingName = false;
216212
layers = layers;
217213
@@ -371,87 +367,110 @@
371367
};
372368
}
373369
374-
async function dragStart(event: DragEvent, listing: LayerListingInfo) {
370+
function handlePointerDown(event: PointerEvent, listing: LayerListingInfo) {
371+
// Only handle primary button (left mouse button)
372+
if (event.button !== 0) return;
373+
375374
const layer = listing.entry;
376-
dragInPanel = true;
377375
if (!$nodeGraph.selected.includes(layer.id)) {
378376
fakeHighlightOfNotYetSelectedLayerBeingDragged = layer.id;
379377
}
380378
const select = () => {
381379
if (!$nodeGraph.selected.includes(layer.id)) selectLayer(listing, false, false);
382380
};
383381
384-
const target = (event.target instanceof HTMLElement && event.target) || undefined;
385-
const closest = target?.closest("[data-layer]") || undefined;
386-
const draggingELement = (closest instanceof HTMLElement && closest) || undefined;
387-
if (draggingELement) beginDraggingElement(draggingELement);
388-
389-
// Set style of cursor for drag
390-
if (event.dataTransfer) {
391-
event.dataTransfer.dropEffect = "move";
392-
event.dataTransfer.effectAllowed = "move";
393-
}
382+
// Store initial drag position and select function for later use
383+
dragStartPosition = { x: event.clientX, y: event.clientY };
384+
draggingData = { select, insertParentId: undefined, insertDepth: 0, insertIndex: undefined, highlightFolder: false, markerHeight: 0 };
394385
395-
if (list) draggingData = calculateDragIndex(list, event.clientY, select);
386+
// Capture pointer to receive move events
387+
(event.target as HTMLElement)?.setPointerCapture(event.pointerId);
396388
}
397389
398-
function updateInsertLine(event: DragEvent) {
399-
if (!draggable) return;
390+
function updateInsertLine(event: PointerEvent) {
391+
// If not dragging yet, check if we should start dragging
392+
if (!isDragging && event.buttons === 1 && draggingData) {
393+
const deltaX = event.clientX - dragStartPosition.x;
394+
const deltaY = event.clientY - dragStartPosition.y;
395+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
396+
397+
if (distance > dragThreshold) {
398+
isDragging = true;
399+
dragInPanel = true;
400+
event.preventDefault();
401+
}
402+
}
403+
404+
if (!isDragging) return;
400405
401-
// Stop the drag from being shown as cancelled
402406
event.preventDefault();
403407
dragInPanel = true;
404408
405409
if (list) draggingData = calculateDragIndex(list, event.clientY, draggingData?.select);
406410
}
407411
408-
function drop(e: DragEvent) {
412+
function handlePointerUp(event: PointerEvent) {
413+
const wasDragging = isDragging;
414+
415+
// If we were dragging, complete the move operation
416+
if (isDragging && draggingData) {
417+
const { select, insertParentId, insertIndex } = draggingData;
418+
419+
// Complete the layer move
420+
select?.();
421+
editor.handle.moveLayerInTree(insertParentId, insertIndex);
422+
}
423+
424+
// Cleanup state
425+
draggingData = undefined;
426+
fakeHighlightOfNotYetSelectedLayerBeingDragged = undefined;
427+
isDragging = false;
428+
dragInPanel = false;
429+
430+
// Release pointer capture
431+
if (wasDragging) {
432+
try {
433+
(event.target as HTMLElement)?.releasePointerCapture(event.pointerId);
434+
} catch {
435+
// Ignore errors - pointer might not be captured
436+
}
437+
}
438+
}
439+
440+
function handleDrop(e: DragEvent) {
409441
if (!draggingData) return;
410-
const { select, insertParentId, insertIndex } = draggingData;
442+
const { insertParentId, insertIndex } = draggingData;
411443
412444
e.preventDefault();
413445
414-
if (e.dataTransfer) {
415-
// Moving layers
416-
if (e.dataTransfer.items.length === 0) {
417-
if (draggable && dragInPanel) {
418-
select?.();
419-
editor.handle.moveLayerInTree(insertParentId, insertIndex);
446+
if (e.dataTransfer && e.dataTransfer.items.length > 0) {
447+
// Handle file imports
448+
Array.from(e.dataTransfer.items).forEach(async (item) => {
449+
const file = item.getAsFile();
450+
if (!file) return;
451+
452+
if (file.type.includes("svg")) {
453+
const svgData = await file.text();
454+
editor.handle.pasteSvg(file.name, svgData, undefined, undefined, insertParentId, insertIndex);
455+
return;
420456
}
421-
}
422-
// Importing files
423-
else {
424-
Array.from(e.dataTransfer.items).forEach(async (item) => {
425-
const file = item.getAsFile();
426-
if (!file) return;
427-
428-
if (file.type.includes("svg")) {
429-
const svgData = await file.text();
430-
editor.handle.pasteSvg(file.name, svgData, undefined, undefined, insertParentId, insertIndex);
431-
return;
432-
}
433457
434-
if (file.type.startsWith("image")) {
435-
const imageData = await extractPixelData(file);
436-
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height, undefined, undefined, insertParentId, insertIndex);
437-
return;
438-
}
458+
if (file.type.startsWith("image")) {
459+
const imageData = await extractPixelData(file);
460+
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height, undefined, undefined, insertParentId, insertIndex);
461+
return;
462+
}
439463
440-
// When we eventually have sub-documents, this should be changed to import the document instead of opening it in a separate tab
441-
const graphiteFileSuffix = "." + editor.handle.fileExtension();
442-
if (file.name.endsWith(graphiteFileSuffix)) {
443-
const content = await file.text();
444-
const documentName = file.name.slice(0, -graphiteFileSuffix.length);
445-
editor.handle.openDocumentFile(documentName, content);
446-
return;
447-
}
448-
});
449-
}
464+
// When we eventually have sub-documents, this should be changed to import the document instead of opening it in a separate tab
465+
const graphiteFileSuffix = "." + editor.handle.fileExtension();
466+
if (file.name.endsWith(graphiteFileSuffix)) {
467+
const content = await file.text();
468+
const documentName = file.name.slice(0, -graphiteFileSuffix.length);
469+
editor.handle.openDocumentFile(documentName, content);
470+
return;
471+
}
472+
});
450473
}
451-
452-
draggingData = undefined;
453-
fakeHighlightOfNotYetSelectedLayerBeingDragged = undefined;
454-
dragInPanel = false;
455474
}
456475
457476
function rebuildLayerHierarchy(updateDocumentLayerStructure: DocumentLayerStructure) {
@@ -509,9 +528,11 @@
509528
data-layer-panel
510529
bind:this={list}
511530
on:click={() => deselectAllLayers()}
512-
on:dragover={updateInsertLine}
513-
on:dragend={drop}
514-
on:drop={drop}
531+
on:pointermove={updateInsertLine}
532+
on:pointerup={handlePointerUp}
533+
on:pointercancel={handlePointerUp}
534+
on:pointerleave={() => (dragInPanel = false)}
535+
on:drop={handleDrop}
515536
>
516537
{#each layers as listing, index}
517538
{@const selected = fakeHighlightOfNotYetSelectedLayerBeingDragged !== undefined ? fakeHighlightOfNotYetSelectedLayerBeingDragged === listing.entry.id : listing.entry.selected}
@@ -528,8 +549,7 @@
528549
data-layer
529550
data-index={index}
530551
tooltip={listing.entry.tooltip}
531-
{draggable}
532-
on:dragstart={(e) => draggable && dragStart(e, listing)}
552+
on:pointerdown={(e) => handlePointerDown(e, listing)}
533553
on:click={(e) => selectLayerWithModifiers(e, listing)}
534554
>
535555
{#if listing.entry.childrenAllowed}

0 commit comments

Comments
 (0)