mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-23 04:58:13 +08:00
WIP: Thumbnails
This commit is contained in:
@@ -92,6 +92,12 @@
|
||||
updateLayerInTree(targetId, targetLayer);
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => {
|
||||
const targetLayer = updateDocumentLayerDetails.data;
|
||||
const targetId = targetLayer.id;
|
||||
|
||||
updateLayerInTree(targetId, targetLayer);
|
||||
});
|
||||
addEventListener("pointermove", clippingHover);
|
||||
addEventListener("keydown", clippingKeyPress);
|
||||
addEventListener("keyup", clippingKeyPress);
|
||||
@@ -540,8 +546,8 @@
|
||||
<IconLabel icon="Clipped" class="clipped-arrow" tooltip={"Clipping mask is active (Alt-click border to release)"} />
|
||||
{/if}
|
||||
<div class="thumbnail">
|
||||
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
|
||||
{@html $nodeGraph.thumbnails.get(listing.entry.id)}
|
||||
{#if $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni) !== undefined}
|
||||
{@html $nodeGraph.thumbnails.get($nodeGraph.wires.get(listing.entry.id)?.get(BigInt(1))?.sni)}
|
||||
{/if}
|
||||
</div>
|
||||
{#if listing.entry.name === "Artboard"}
|
||||
|
||||
@@ -230,6 +230,22 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function generateCheckerboardString() {
|
||||
let rects = "";
|
||||
|
||||
for (let y = 0; y < 6; y++) {
|
||||
for (let x = 0; x < 8; x++) {
|
||||
const isDark = (x + y) % 2 === 0;
|
||||
const fill = isDark ? "#cccccc" : "#ffffff";
|
||||
rects += `<rect x="${x * 4 - 16}" y="${y * 4 - 12}" width="${4.1}" height="${4.1}" fill="${fill}" />`;
|
||||
}
|
||||
}
|
||||
|
||||
rects += ``;
|
||||
|
||||
return rects;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -317,10 +333,11 @@
|
||||
<div class="wires" style:transform-origin={`0 0`} style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||
<svg>
|
||||
{#each $nodeGraph.wires.values() as map}
|
||||
{#each map.values() as { pathString, dataType, thick, dashed }}
|
||||
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
|
||||
{#if thick}
|
||||
<path
|
||||
d={pathString}
|
||||
class="wire-path"
|
||||
style:--data-line-width={"8px"}
|
||||
style:--data-color={`var(--color-data-${dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||
@@ -513,8 +530,8 @@
|
||||
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
||||
{/if}
|
||||
<div class="thumbnail">
|
||||
{#if $nodeGraph.thumbnails.has(node.id)}
|
||||
{@html $nodeGraph.thumbnails.get(node.id)}
|
||||
{#if node.layerThumbnailSNI && $nodeGraph.thumbnails.has(node.layerThumbnailSNI)}
|
||||
{@html $nodeGraph.thumbnails.get(node.layerThumbnailSNI)}
|
||||
{/if}
|
||||
<!-- Layer stacking top output -->
|
||||
{#if node.primaryOutput}
|
||||
@@ -613,8 +630,8 @@
|
||||
<div class="wires">
|
||||
<svg>
|
||||
{#each $nodeGraph.wires.values() as map}
|
||||
{#each map.values() as { pathString, dataType, thick, dashed, center, monitorSni }}
|
||||
{#if !thick}
|
||||
{#each map.values() as { pathString, dataType, thick, dashed, center, sni }}
|
||||
{#if !thick && pathString}
|
||||
<path
|
||||
d={pathString}
|
||||
class="wire-path"
|
||||
@@ -623,20 +640,16 @@
|
||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||
style:--data-dasharray={`3,${dashed ? 2 : 0}`}
|
||||
/>
|
||||
{/if}
|
||||
<!-- {console.log("center: ", center)} -->
|
||||
<!-- {console.log("monitorSni: ", monitorSni)} -->
|
||||
{#if center && monitorSni}
|
||||
<!-- {console.log("thumbnails: ", $nodeGraph.thumbnails.get(monitorSni))} -->
|
||||
<g transform={`translate(${center[0]}, ${center[1]})`}>
|
||||
{@html $nodeGraph.thumbnails.get(monitorSni)}
|
||||
</g>
|
||||
<!-- <g transform={`translate(${center[0] - 30}, ${center[1] - 10})`}>
|
||||
<text fill="white">{sni}</text>
|
||||
</g> -->
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
{#if $nodeGraph.wirePathInProgress}
|
||||
<path
|
||||
d={$nodeGraph.wirePathInProgress?.pathString}
|
||||
class="wire-path"
|
||||
style:--data-line-width={`${$nodeGraph.wirePathInProgress.thick ? 8 : 2}px`}
|
||||
style:--data-color={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${$nodeGraph.wirePathInProgress.dataType.toLowerCase()}-dim)`}
|
||||
@@ -644,6 +657,22 @@
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
{#each $nodeGraph.wires.values() as map}
|
||||
{#each map.values() as { pathString, center, sni, dataType, thick }}
|
||||
{#if !thick && pathString && sni && center !== undefined}
|
||||
<div
|
||||
class="wire-thumbnail"
|
||||
style:--offset-left-px={`${center[0] - 12}px`}
|
||||
style:--offset-top-px={`${center[1] - 8}px`}
|
||||
style:--data-color-dim={`var(--color-data-${dataType.toLowerCase()}-dim)`}
|
||||
>
|
||||
{#if sni && $nodeGraph.thumbnails.has(sni)}
|
||||
{@html $nodeGraph.thumbnails.get(sni)}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Nodes -->
|
||||
@@ -891,7 +920,6 @@
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -904,6 +932,23 @@
|
||||
stroke-dasharray: var(--data-dasharray);
|
||||
}
|
||||
}
|
||||
|
||||
.wire-thumbnail {
|
||||
// background: var(--color-2-mildblack);
|
||||
border: 1px solid var(--data-color-dim);
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
left: var(--offset-left-px);
|
||||
top: var(--offset-top-px);
|
||||
box-sizing: border-box;
|
||||
width: 24px;
|
||||
height: 16px;
|
||||
// background-color: white;
|
||||
background-image: var(--color-transparent-checkered-background);
|
||||
background-size: var(--color-transparent-checkered-background-size-mini);
|
||||
background-position: var(--color-transparent-checkered-background-position-mini);
|
||||
background-repeat: var(--color-transparent-checkered-background-repeat);
|
||||
}
|
||||
}
|
||||
|
||||
.imports-and-exports {
|
||||
|
||||
@@ -12,6 +12,7 @@ export class JsMessage {
|
||||
}
|
||||
|
||||
const TupleToVec2 = Transform(({ value }: { value: [number, number] | undefined }) => (value === undefined ? undefined : { x: value[0], y: value[1] }));
|
||||
|
||||
const ImportsToVec2Array = Transform(({ obj: { imports } }: { obj: { imports: [FrontendGraphOutput, number, number][] } }) =>
|
||||
imports.map(([outputMetadata, x, y]) => ({ outputMetadata, position: { x, y } })),
|
||||
);
|
||||
@@ -36,6 +37,10 @@ export class UpdateBox extends JsMessage {
|
||||
readonly box!: Box | undefined;
|
||||
}
|
||||
|
||||
export class UpdateGraphBreadcrumbPath extends JsMessage {
|
||||
readonly breadcrumbPath!: bigint[];
|
||||
}
|
||||
|
||||
export class UpdateClickTargets extends JsMessage {
|
||||
readonly clickTargets!: FrontendClickTargets | undefined;
|
||||
}
|
||||
@@ -57,6 +62,10 @@ export class UpdateContextMenuInformation extends JsMessage {
|
||||
readonly contextMenuInformation!: ContextMenuInformation | undefined;
|
||||
}
|
||||
|
||||
export class UpdateContextDuringEvaluation extends JsMessage {
|
||||
readonly contextDuringEvaluation!: [bigint, number, string][];
|
||||
}
|
||||
|
||||
export class UpdateImportsExports extends JsMessage {
|
||||
@ImportsToVec2Array
|
||||
readonly imports!: { outputMetadata: FrontendGraphOutput; position: XY }[];
|
||||
@@ -109,7 +118,7 @@ export class UpdateNodeGraphWires extends JsMessage {
|
||||
readonly wires!: WireUpdate[];
|
||||
}
|
||||
|
||||
export class ClearAllNodeGraphWires extends JsMessage {}
|
||||
export class ClearAllNodeGraphWirePaths extends JsMessage {}
|
||||
|
||||
export class UpdateNodeGraphTransform extends JsMessage {
|
||||
readonly transform!: NodeGraphTransform;
|
||||
@@ -128,6 +137,10 @@ export class UpdateThumbnails extends JsMessage {
|
||||
readonly add!: [bigint, string][];
|
||||
|
||||
readonly clear!: bigint[];
|
||||
|
||||
readonly wireSNIUpdates!: WireSNIUpdate[];
|
||||
|
||||
readonly layerSNIUpdates!: FrontendLayerSNIUpdate[];
|
||||
}
|
||||
|
||||
export class UpdateNodeGraphSelection extends JsMessage {
|
||||
@@ -266,6 +279,8 @@ export class FrontendGraphOutput {
|
||||
export class FrontendNode {
|
||||
readonly isLayer!: boolean;
|
||||
|
||||
layerThumbnailSNI!: bigint | undefined;
|
||||
|
||||
readonly canBeLayer!: boolean;
|
||||
|
||||
readonly id!: bigint;
|
||||
@@ -302,6 +317,12 @@ export class FrontendNode {
|
||||
readonly uiOnly!: boolean;
|
||||
}
|
||||
|
||||
export class FrontendLayerSNIUpdate {
|
||||
readonly id!: bigint;
|
||||
|
||||
readonly sni!: bigint | undefined;
|
||||
}
|
||||
|
||||
export class FrontendNodeType {
|
||||
readonly name!: string;
|
||||
|
||||
@@ -317,13 +338,12 @@ export class NodeGraphTransform {
|
||||
}
|
||||
|
||||
export class WirePath {
|
||||
readonly pathString!: string;
|
||||
pathString!: string;
|
||||
readonly dataType!: FrontendGraphDataType;
|
||||
readonly thick!: boolean;
|
||||
readonly dashed!: boolean;
|
||||
@TupleToVec2
|
||||
readonly center!: XY | undefined;
|
||||
readonly inputSni!: bigint;
|
||||
readonly center!: [number, number] | undefined;
|
||||
sni!: bigint | undefined;
|
||||
}
|
||||
|
||||
export class WireUpdate {
|
||||
@@ -332,6 +352,12 @@ export class WireUpdate {
|
||||
readonly wirePathUpdate!: WirePath | undefined;
|
||||
}
|
||||
|
||||
export class WireSNIUpdate {
|
||||
readonly id!: bigint;
|
||||
readonly inputIndex!: number;
|
||||
readonly sni!: bigint | undefined;
|
||||
}
|
||||
|
||||
export class IndexedDbDocumentDetails extends DocumentDetails {
|
||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||
id!: string;
|
||||
@@ -1624,7 +1650,7 @@ type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHand
|
||||
type MessageMaker = typeof JsMessage | JSMessageFactory;
|
||||
|
||||
export const messageMakers: Record<string, MessageMaker> = {
|
||||
ClearAllNodeGraphWires,
|
||||
ClearAllNodeGraphWirePaths,
|
||||
DisplayDialog,
|
||||
DisplayDialogDismiss,
|
||||
DisplayDialogPanic,
|
||||
@@ -1653,8 +1679,10 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||
TriggerVisitLink,
|
||||
UpdateActiveDocument,
|
||||
UpdateBox,
|
||||
UpdateGraphBreadcrumbPath,
|
||||
UpdateClickTargets,
|
||||
UpdateContextMenuInformation,
|
||||
UpdateContextDuringEvaluation,
|
||||
UpdateDialogButtons,
|
||||
UpdateDialogColumn1,
|
||||
UpdateDialogColumn2,
|
||||
|
||||
@@ -9,11 +9,13 @@ import {
|
||||
type FrontendNode,
|
||||
type FrontendNodeType,
|
||||
type WirePath,
|
||||
ClearAllNodeGraphWires,
|
||||
ClearAllNodeGraphWirePaths,
|
||||
SendUIMetadata,
|
||||
UpdateBox,
|
||||
UpdateGraphBreadcrumbPath,
|
||||
UpdateClickTargets,
|
||||
UpdateContextMenuInformation,
|
||||
UpdateContextDuringEvaluation,
|
||||
UpdateInSelectedNetwork,
|
||||
UpdateImportReorderIndex,
|
||||
UpdateExportReorderIndex,
|
||||
@@ -32,6 +34,7 @@ import {
|
||||
export function createNodeGraphState(editor: Editor) {
|
||||
const { subscribe, update } = writable({
|
||||
box: undefined as Box | undefined,
|
||||
breadcrumbPath: [] as bigint[],
|
||||
clickTargets: undefined as FrontendClickTargets | undefined,
|
||||
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
||||
layerWidths: new Map<bigint, number>(),
|
||||
@@ -43,8 +46,9 @@ export function createNodeGraphState(editor: Editor) {
|
||||
addExport: undefined as { x: number; y: number } | undefined,
|
||||
nodes: new Map<bigint, FrontendNode>(),
|
||||
visibleNodes: new Set<bigint>(),
|
||||
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
|
||||
/// The first key is the document node id. The index is the actual input index. The exports have a first key value of u32::MAX.
|
||||
wires: new Map<bigint, Map<number, WirePath>>(),
|
||||
/// The first key is the caller stable node id
|
||||
wirePathInProgress: undefined as WirePath | undefined,
|
||||
nodeDescriptions: new Map<string, string>(),
|
||||
nodeTypes: [] as FrontendNodeType[],
|
||||
@@ -70,6 +74,13 @@ export function createNodeGraphState(editor: Editor) {
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateGraphBreadcrumbPath, (updateGraphBreadcrumbPath) => {
|
||||
update((state) => {
|
||||
state.breadcrumbPath = updateGraphBreadcrumbPath.breadcrumbPath;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateClickTargets, (UpdateClickTargets) => {
|
||||
update((state) => {
|
||||
state.clickTargets = UpdateClickTargets.clickTargets;
|
||||
@@ -82,6 +93,11 @@ export function createNodeGraphState(editor: Editor) {
|
||||
return state;
|
||||
});
|
||||
});
|
||||
// editor.subscriptions.subscribeJsMessage(UpdateContextDuringEvaluation, (updateContextDuringEvaluation) => {
|
||||
// update((state) => {
|
||||
// return state;
|
||||
// });
|
||||
// });
|
||||
editor.subscriptions.subscribeJsMessage(UpdateImportReorderIndex, (updateImportReorderIndex) => {
|
||||
update((state) => {
|
||||
state.reorderImportIndex = updateImportReorderIndex.importIndex;
|
||||
@@ -118,7 +134,6 @@ export function createNodeGraphState(editor: Editor) {
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
||||
// console.log(updateNodeGraphNodes);
|
||||
update((state) => {
|
||||
state.nodes.clear();
|
||||
updateNodeGraphNodes.nodes.forEach((node) => {
|
||||
@@ -127,6 +142,7 @@ export function createNodeGraphState(editor: Editor) {
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
||||
editor.subscriptions.subscribeJsMessage(UpdateVisibleNodes, (updateVisibleNodes) => {
|
||||
update((state) => {
|
||||
state.visibleNodes = new Set<bigint>(updateVisibleNodes.nodes);
|
||||
@@ -143,17 +159,33 @@ export function createNodeGraphState(editor: Editor) {
|
||||
state.wires.set(wireUpdate.id, inputMap);
|
||||
}
|
||||
if (wireUpdate.wirePathUpdate !== undefined) {
|
||||
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
|
||||
const existing = inputMap.get(wireUpdate.inputIndex);
|
||||
if (existing) {
|
||||
inputMap.set(wireUpdate.inputIndex, {
|
||||
...wireUpdate.wirePathUpdate,
|
||||
sni: existing.sni,
|
||||
});
|
||||
} else {
|
||||
inputMap.set(wireUpdate.inputIndex, wireUpdate.wirePathUpdate);
|
||||
}
|
||||
} else {
|
||||
inputMap.delete(wireUpdate.inputIndex);
|
||||
const existing = inputMap.get(wireUpdate.inputIndex);
|
||||
if (existing) {
|
||||
existing.pathString = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWires, (_) => {
|
||||
editor.subscriptions.subscribeJsMessage(ClearAllNodeGraphWirePaths, (_) => {
|
||||
update((state) => {
|
||||
state.wires.clear();
|
||||
for (const [, innerMap] of state.wires) {
|
||||
for (const [, wirePath] of innerMap) {
|
||||
wirePath.pathString = "";
|
||||
}
|
||||
}
|
||||
return state;
|
||||
});
|
||||
});
|
||||
@@ -178,7 +210,22 @@ export function createNodeGraphState(editor: Editor) {
|
||||
for (const id of updateThumbnails.clear) {
|
||||
state.thumbnails.set(id, "");
|
||||
}
|
||||
// console.log("thumbnails: ", state.thumbnails);
|
||||
updateThumbnails.wireSNIUpdates.forEach((wireUpdate) => {
|
||||
const inputMap = state.wires.get(wireUpdate.id);
|
||||
if (inputMap) {
|
||||
const wire = inputMap.get(wireUpdate.inputIndex);
|
||||
if (wire) {
|
||||
wire.sni = wireUpdate.sni;
|
||||
}
|
||||
}
|
||||
});
|
||||
updateThumbnails.layerSNIUpdates.forEach((wireUpdate) => {
|
||||
const node = state.nodes.get(wireUpdate.id);
|
||||
if (node) {
|
||||
node.layerThumbnailSNI = wireUpdate.sni;
|
||||
}
|
||||
});
|
||||
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user