mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 04:48:11 +08:00
Add drag-and-drop and copy-paste file importing/opening throughout the UI (#2012)
* Add file importing by dragging and dropping throughout the UI * Disable comment-profiling-changes.yaml * Fix CI
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
on:dragleave
|
||||
on:dragover
|
||||
on:dragstart
|
||||
on:drop
|
||||
on:mouseup
|
||||
on:pointerdown
|
||||
on:pointerenter
|
||||
@@ -58,7 +59,6 @@ on:copy
|
||||
on:cut
|
||||
on:drag
|
||||
on:dragenter
|
||||
on:drop
|
||||
on:focus
|
||||
on:fullscreenchange
|
||||
on:fullscreenerror
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
on:dragleave
|
||||
on:dragover
|
||||
on:dragstart
|
||||
on:drop
|
||||
on:mouseup
|
||||
on:pointerdown
|
||||
on:pointerenter
|
||||
@@ -58,7 +59,6 @@ on:copy
|
||||
on:cut
|
||||
on:drag
|
||||
on:dragenter
|
||||
on:drop
|
||||
on:focus
|
||||
on:fullscreenchange
|
||||
on:fullscreenerror
|
||||
|
||||
@@ -118,23 +118,33 @@
|
||||
};
|
||||
})($document.toolShelfLayout.layout[0]);
|
||||
|
||||
function pasteFile(e: DragEvent) {
|
||||
function dropFile(e: DragEvent) {
|
||||
const { dataTransfer } = e;
|
||||
const [x, y] = e.target instanceof Element && e.target.closest("[data-viewport]") ? [e.clientX, e.clientY] : [undefined, undefined];
|
||||
if (!dataTransfer) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
Array.from(dataTransfer.items).forEach(async (item) => {
|
||||
const file = item.getAsFile();
|
||||
if (file?.type.includes("svg")) {
|
||||
const svgData = await file.text();
|
||||
editor.handle.pasteSvg(svgData, e.clientX, e.clientY);
|
||||
if (!file) return;
|
||||
|
||||
if (file.type.includes("svg")) {
|
||||
const svgData = await file.text();
|
||||
editor.handle.pasteSvg(file.name, svgData, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file?.type.startsWith("image")) {
|
||||
if (file.type.startsWith("image")) {
|
||||
const imageData = await extractPixelData(file);
|
||||
editor.handle.pasteImage(new Uint8Array(imageData.data), imageData.width, imageData.height, e.clientX, e.clientY);
|
||||
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.name.endsWith(".graphite")) {
|
||||
const content = await file.text();
|
||||
editor.handle.openDocumentFile(file.name, content);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -426,7 +436,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<LayoutCol class="document">
|
||||
<LayoutCol class="document" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}>
|
||||
<LayoutRow class="options-bar" classes={{ "for-graph": $document.graphViewOverlayOpen }} scrollableX={true}>
|
||||
{#if !$document.graphViewOverlayOpen}
|
||||
<WidgetLayout layout={$document.documentModeLayout} />
|
||||
@@ -482,7 +492,7 @@
|
||||
y={cursorTop}
|
||||
/>
|
||||
{/if}
|
||||
<div class="viewport" on:pointerdown={(e) => canvasPointerDown(e)} on:dragover={(e) => e.preventDefault()} on:drop={(e) => pasteFile(e)} bind:this={viewport} data-viewport>
|
||||
<div class="viewport" on:pointerdown={(e) => canvasPointerDown(e)} bind:this={viewport} data-viewport>
|
||||
<svg class="artboards" style:width={canvasWidthCSS} style:height={canvasHeightCSS}>
|
||||
{@html artworkSvg}
|
||||
</svg>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { beginDraggingElement } from "@graphite/io-managers/drag";
|
||||
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
|
||||
import { platformIsMac } from "@graphite/utility-functions/platform";
|
||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||
import type { Editor } from "@graphite/wasm-communication/editor";
|
||||
import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages";
|
||||
import type { DataBuffer, LayerPanelEntry } from "@graphite/wasm-communication/messages";
|
||||
@@ -305,6 +306,8 @@
|
||||
}
|
||||
|
||||
function updateInsertLine(event: DragEvent) {
|
||||
if (!draggable) return;
|
||||
|
||||
// Stop the drag from being shown as cancelled
|
||||
event.preventDefault();
|
||||
dragInPanel = true;
|
||||
@@ -312,13 +315,48 @@
|
||||
if (list) draggingData = calculateDragIndex(list, event.clientY, draggingData?.select);
|
||||
}
|
||||
|
||||
async function drop() {
|
||||
if (draggingData && dragInPanel) {
|
||||
const { select, insertParentId, insertIndex } = draggingData;
|
||||
function drop(e: DragEvent) {
|
||||
if (!draggingData) return;
|
||||
const { select, insertParentId, insertIndex } = draggingData;
|
||||
|
||||
select?.();
|
||||
editor.handle.moveLayerInTree(insertParentId, insertIndex);
|
||||
e.preventDefault();
|
||||
|
||||
if (e.dataTransfer) {
|
||||
// Moving layers
|
||||
if (e.dataTransfer.items.length === 0) {
|
||||
if (draggable && dragInPanel) {
|
||||
select?.();
|
||||
editor.handle.moveLayerInTree(insertParentId, insertIndex);
|
||||
}
|
||||
}
|
||||
// Importing files
|
||||
else {
|
||||
Array.from(e.dataTransfer.items).forEach(async (item) => {
|
||||
const file = item.getAsFile();
|
||||
if (!file) return;
|
||||
|
||||
if (file.type.includes("svg")) {
|
||||
const svgData = await file.text();
|
||||
editor.handle.pasteSvg(file.name, svgData, undefined, undefined, insertParentId, insertIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.type.startsWith("image")) {
|
||||
const imageData = await extractPixelData(file);
|
||||
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height, undefined, undefined, insertParentId, insertIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// When we eventually have sub-documents, this should be changed to import the document instead of opening it in a separate tab
|
||||
if (file.name.endsWith(".graphite")) {
|
||||
const content = await file.text();
|
||||
editor.handle.openDocumentFile(file.name, content);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
draggingData = undefined;
|
||||
fakeHighlight = undefined;
|
||||
dragInPanel = false;
|
||||
@@ -369,7 +407,7 @@
|
||||
<WidgetLayout layout={layersPanelOptionsLayout} />
|
||||
</LayoutRow>
|
||||
<LayoutRow class="list-area" scrollableY={true}>
|
||||
<LayoutCol class="list" data-layer-panel bind:this={list} on:click={() => deselectAllLayers()} on:dragover={(e) => draggable && updateInsertLine(e)} on:dragend={() => draggable && drop()}>
|
||||
<LayoutCol class="list" data-layer-panel bind:this={list} on:click={() => deselectAllLayers()} on:dragover={updateInsertLine} on:dragend={drop} on:drop={drop}>
|
||||
{#each layers as listing, index}
|
||||
<LayoutRow
|
||||
class="layer"
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
import Document from "@graphite/components/panels/Document.svelte";
|
||||
import Layers from "@graphite/components/panels/Layers.svelte";
|
||||
import Properties from "@graphite/components/panels/Properties.svelte";
|
||||
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
|
||||
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
||||
|
||||
const PANEL_COMPONENTS = {
|
||||
Document,
|
||||
@@ -18,11 +16,14 @@
|
||||
|
||||
import { platformIsMac, isEventSupported } from "@graphite/utility-functions/platform";
|
||||
|
||||
import { extractPixelData } from "@graphite/utility-functions/rasterization";
|
||||
import type { Editor } from "@graphite/wasm-communication/editor";
|
||||
import { type LayoutKeysGroup, type Key } from "@graphite/wasm-communication/messages";
|
||||
|
||||
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
|
||||
import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte";
|
||||
import TextButton from "@graphite/components/widgets/buttons/TextButton.svelte";
|
||||
import IconLabel from "@graphite/components/widgets/labels/IconLabel.svelte";
|
||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||
import UserInputLabel from "@graphite/components/widgets/labels/UserInputLabel.svelte";
|
||||
@@ -50,6 +51,35 @@
|
||||
return reservedKey ? [CONTROL, ALT] : [CONTROL];
|
||||
}
|
||||
|
||||
function dropFile(e: DragEvent) {
|
||||
if (!e.dataTransfer) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
Array.from(e.dataTransfer.items).forEach(async (item) => {
|
||||
const file = item.getAsFile();
|
||||
if (!file) return;
|
||||
|
||||
if (file.type.includes("svg")) {
|
||||
const svgData = await file.text();
|
||||
editor.handle.pasteSvg(file.name, svgData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.type.startsWith("image")) {
|
||||
const imageData = await extractPixelData(file);
|
||||
editor.handle.pasteImage(file.name, new Uint8Array(imageData.data), imageData.width, imageData.height);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.name.endsWith(".graphite")) {
|
||||
const content = await file.text();
|
||||
editor.handle.openDocumentFile(file.name, content);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function scrollTabIntoView(newIndex: number) {
|
||||
await tick();
|
||||
tabElements[newIndex]?.div?.()?.scrollIntoView();
|
||||
@@ -76,7 +106,7 @@
|
||||
}
|
||||
}}
|
||||
on:mouseup={(e) => {
|
||||
// Fallback for Safari:
|
||||
// Middle mouse button click fallback for Safari:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event#browser_compatibility
|
||||
// The downside of using mouseup is that the mousedown didn't have to originate in the same element.
|
||||
// A possible future improvement could save the target element during mousedown and check if it's the same here.
|
||||
@@ -110,7 +140,7 @@
|
||||
{#if panelType}
|
||||
<svelte:component this={PANEL_COMPONENTS[panelType]} />
|
||||
{:else}
|
||||
<LayoutCol class="empty-panel">
|
||||
<LayoutCol class="empty-panel" on:dragover={(e) => e.preventDefault()} on:drop={dropFile}>
|
||||
<LayoutCol class="content">
|
||||
<LayoutRow class="logotype">
|
||||
<IconLabel icon="GraphiteLogotypeSolid" />
|
||||
|
||||
Reference in New Issue
Block a user