Generalize layers as merge nodes to enable adjustment layers (#1712)

* WIP, backward traversal issues

* Fix some tool issues

* Remove debugging

* Change some indices

* WIP: new artboard node

* WIP: add artboard node

* WIP: Artboard node and create_artboard

* WIP: Artboard node implementation complete

* WIP: Artboards input for output node

* Complete Artboard node

* Generalize LayerNodeIdentifier,  monitor_nodes support for Artboard node, adjust ResizeArtboard/ClearArtboards, move alias validation to Rust

* Fix misaligned artboard click targets

* Generalize/clarify create_layer and insert_between

* non-negative dimensions for resize_artboard

* Show artboards in layer panel

* Generalize create_layer for layer output node

* Generalize delete_layer/delete_artboard to NodeGraphMessage::DeleteNodes. Fixed upstream flow Iter

* remove old primary_input function

* Vertical node visuals, remove is_layer function, rename Layer node to Merge node, toggle display as layer

exposed_value_count type fix

Vertical node visuals, remove is_layer function, rename Layer node to Merge node, toggle display as layer

* Fix demo artwork

* Layer display context menu

* Automatically select artboard, fix warnings

* Improvements to context menu and layer invariant enforcement

* Remove display_as_layer and update load_structure

* Improve load_structure to show more layers, improve FlowIter, improve layer naming, layer rearrangement validation.

* Clean up demo artwork using generalized layers

* Improve design of Layers panel and graph nodes

* MoveSelectedLayersTo rewrite to support generalized layer nodes

* Include artboards in deepest_common_ancestor, fix resize_artboard/delete_artboard, sync artboard tool to layer panel

* MoveSelectedLayersTo adjustments

* Sync non layer node visibility with metadata

* Include non layer nodes when moving/creating layer

* Fix group layers and get_post_node_with_index

* Include non layer nodes in UngroupSelectedLayers

* GroupSelected for all selected nodes, UnGroupSelected position adjustments

* Add grouping for layers in different folders

* Fix hidden layers

* Prevent node from connecting to itself, fix undo automatic node insertion,

* Fix undo CreateEmptyFolder, fix grouping nested layer nodes

* Formatting

* Remove test and check if node is layer from network

* Fix undo group layers

* Check off roadmap

* MoveUpstreamSiblingsToChild adjustments

* Replace tabs with spaces, remove mut from argument

* Final code review pass

---------

Co-authored-by: 0hypercube <0hypercube@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
adamgerhant
2024-04-30 23:03:42 -07:00
committed by GitHub
co-authored by 0hypercube Keavon Chambers
parent beb88d280c
commit 8d83fa7079
41 changed files with 1712 additions and 825 deletions
+30 -34
View File
@@ -6,7 +6,7 @@
import { platformIsMac } from "@graphite/utility-functions/platform";
import type { Editor } from "@graphite/wasm-communication/editor";
import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages";
import type { DataBuffer, LayerClassification, LayerPanelEntry } from "@graphite/wasm-communication/messages";
import type { DataBuffer, LayerPanelEntry } from "@graphite/wasm-communication/messages";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
import LayoutRow from "@graphite/components/layout/LayoutRow.svelte";
@@ -165,7 +165,7 @@
const name = (e.target instanceof HTMLInputElement && e.target.value) || "";
editor.handle.setLayerName(listing.entry.id, name);
listing.entry.name = name;
listing.entry.alias = name;
}
async function onEditLayerNameDeselect(listing: LayerListingInfo) {
@@ -174,7 +174,7 @@
layers = layers;
// Set it back to the original name if the user didn't enter a new name
if (document.activeElement instanceof HTMLInputElement) document.activeElement.value = listing.entry.name;
if (document.activeElement instanceof HTMLInputElement) document.activeElement.value = listing.entry.alias;
// Deselect the text so it doesn't appear selected while the input field becomes disabled and styled to look like regular text
window.getSelection()?.removeAllRanges();
@@ -203,10 +203,6 @@
editor.handle.deselectAllLayers();
}
function isNestingLayer(layerClassification: LayerClassification) {
return layerClassification === "Folder" || layerClassification === "Artboard";
}
function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData {
const treeChildren = tree.div()?.children;
const treeOffset = tree.div()?.getBoundingClientRect().top;
@@ -248,7 +244,7 @@
}
// Inserting below current row
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0) {
if (isNestingLayer(layer.layerClassification)) {
if (layer.childrenAllowed) {
insertParentId = layer.id;
insertDepth = layer.depth;
insertIndex = 0;
@@ -381,7 +377,6 @@
classes={{
selected: fakeHighlight !== undefined ? fakeHighlight === listing.entry.id : $nodeGraph.selected.includes(listing.entry.id),
"insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertParentId === listing.entry.id,
"nesting-layer": isNestingLayer(listing.entry.layerClassification),
}}
styles={{ "--layer-indent-levels": `${listing.entry.depth - 1}` }}
data-layer
@@ -391,32 +386,29 @@
on:dragstart={(e) => draggable && dragStart(e, listing)}
on:click={(e) => selectLayerWithModifiers(e, listing)}
>
{#if isNestingLayer(listing.entry.layerClassification)}
{#if listing.entry.childrenAllowed}
<button
class="expand-arrow"
class:expanded={listing.entry.expanded}
disabled={!listing.entry.hasChildren}
disabled={!listing.entry.childrenPresent}
on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.id)}
tabindex="0"
/>
{#if listing.entry.layerClassification === "Artboard"}
<IconLabel icon="Artboard" class={"layer-type-icon"} />
{:else if listing.entry.layerClassification === "Folder"}
<IconLabel icon="Folder" class={"layer-type-icon"} />
{/if}
<div class="thumbnail">
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
{@html $nodeGraph.thumbnails.get(listing.entry.id)}
{/if}
{:else}
<div class="thumbnail">
{#if $nodeGraph.thumbnails.has(listing.entry.id)}
{@html $nodeGraph.thumbnails.get(listing.entry.id)}
{/if}
</div>
</div>
{#if listing.entry.name === "Artboard"}
<IconLabel icon="Artboard" class={"layer-type-icon"} />
{/if}
<LayoutRow class="layer-name" on:dblclick={() => onEditLayerName(listing)}>
<input
data-text-input
type="text"
value={listing.entry.name}
placeholder={listing.entry.layerClassification}
value={listing.entry.alias}
placeholder={listing.entry.name}
disabled={!listing.editingName}
on:blur={() => onEditLayerNameDeselect(listing)}
on:keydown={(e) => e.key === "Escape" && onEditLayerNameDeselect(listing)}
@@ -503,11 +495,7 @@
border-radius: 2px;
height: 32px;
margin: 0 4px;
padding-left: calc(4px + var(--layer-indent-levels) * 16px);
&.nesting-layer {
padding-left: calc(var(--layer-indent-levels) * 16px);
}
padding-left: calc(var(--layer-indent-levels) * 16px);
&.selected {
background: var(--color-4-dimgray);
@@ -557,17 +545,19 @@
}
}
.layer-type-icon {
flex: 0 0 auto;
margin-left: 4px;
}
.thumbnail {
width: 36px;
height: 24px;
background: white;
margin-left: 4px;
border-radius: 2px;
flex: 0 0 auto;
background: var(--color-transparent-checkered-background);
background-size: var(--color-transparent-checkered-background-size-mini);
background-position: var(--color-transparent-checkered-background-position-mini);
&:first-child {
margin-left: 20px;
}
svg {
width: calc(100% - 4px);
@@ -576,6 +566,12 @@
}
}
.layer-type-icon {
flex: 0 0 auto;
margin-left: 8px;
margin-right: -4px;
}
.layer-name {
flex: 1 1 100%;
margin: 0 8px;