Improve the layers UI in the node graph

This commit is contained in:
Keavon Chambers
2023-08-18 14:26:59 -07:00
parent a871ea6d69
commit 5a7d230156
2 changed files with 84 additions and 37 deletions
@@ -113,7 +113,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
let layer_node = resolve_document_node_type("Layer").expect("Node").to_document_node_default_inputs([], Default::default()); let layer_node = resolve_document_node_type("Layer").expect("Node").to_document_node_default_inputs([], Default::default());
let layer_node = self.insert_node_before(new_id, current_node, input_index, layer_node, IVec2::new(0, 3))?; let layer_node = self.insert_node_before(new_id, current_node, input_index, layer_node, IVec2::new(-4, 3))?;
Some(layer_node) Some(layer_node)
} }
+83 -36
View File
@@ -180,12 +180,12 @@
const containerBounds = nodesContainer.getBoundingClientRect(); const containerBounds = nodesContainer.getBoundingClientRect();
const outX = verticalOut ? outputBounds.x + outputBounds.width / 2 : outputBounds.x + outputBounds.width - 1; const outX = verticalOut ? outputBounds.x + outputBounds.width / 2 : outputBounds.x + outputBounds.width - 1;
const outY = verticalOut ? outputBounds.y + 1 : outputBounds.y + outputBounds.height / 2; const outY = verticalOut ? outputBounds.y - 1 : outputBounds.y + outputBounds.height / 2;
const outConnectorX = (outX - containerBounds.x) / transform.scale; const outConnectorX = (outX - containerBounds.x) / transform.scale;
const outConnectorY = (outY - containerBounds.y) / transform.scale; const outConnectorY = (outY - containerBounds.y) / transform.scale;
const inX = verticalIn ? inputBounds.x + inputBounds.width / 2 : inputBounds.x + 1; const inX = verticalIn ? inputBounds.x + inputBounds.width / 2 : inputBounds.x + 1;
const inY = verticalIn ? inputBounds.y + inputBounds.height - 1 : inputBounds.y + inputBounds.height / 2; const inY = verticalIn ? inputBounds.y + inputBounds.height + 2 : inputBounds.y + inputBounds.height / 2;
const inConnectorX = (inX - containerBounds.x) / transform.scale; const inConnectorX = (inX - containerBounds.x) / transform.scale;
const inConnectorY = (inY - containerBounds.y) / transform.scale; const inConnectorY = (inY - containerBounds.y) / transform.scale;
const horizontalGap = Math.abs(outConnectorX - inConnectorX); const horizontalGap = Math.abs(outConnectorX - inConnectorX);
@@ -364,7 +364,7 @@
draggingNodes = { startX: e.x, startY: e.y, roundX: 0, roundY: 0 }; draggingNodes = { startX: e.x, startY: e.y, roundX: 0, roundY: 0 };
} }
if (modifiedSelected) editor.instance.selectNodes(selected.length > 0 ? new BigUint64Array(selected) : null); if (modifiedSelected) editor.instance.selectNodes(selected.length > 0 ? new BigUint64Array(selected) : undefined);
return; return;
} }
@@ -372,7 +372,7 @@
// Clicked on the graph background // Clicked on the graph background
if (lmb && selected.length !== 0) { if (lmb && selected.length !== 0) {
selected = []; selected = [];
editor.instance.selectNodes(null); editor.instance.selectNodes(undefined);
} }
// LMB clicked on the graph background or MMB clicked anywhere // LMB clicked on the graph background or MMB clicked anywhere
@@ -524,15 +524,40 @@
nodeListLocation = undefined; nodeListLocation = undefined;
} }
function buildBorderMask(nodeWidth: number, primaryInputExists: boolean, parameters: number, primaryOutputExists: boolean, exposedOutputs: number): string { function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, parameters: number, primaryOutputExists: boolean, exposedOutputs: number): string {
const nodeHeight = Math.max(1 + parameters, 1 + exposedOutputs) * 24; const nodeHeight = Math.max(1 + parameters, 1 + exposedOutputs) * 24;
const boxes: { x: number; y: number; width: number; height: number }[] = []; const boxes: { x: number; y: number; width: number; height: number }[] = [];
// Primary input
if (primaryInputExists) boxes.push({ x: -8, y: 4, width: 16, height: 16 }); if (primaryInputExists) boxes.push({ x: -8, y: 4, width: 16, height: 16 });
// Parameter inputs
for (let i = 0; i < parameters; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); for (let i = 0; i < parameters; i++) boxes.push({ x: -8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
// Primary output
if (primaryOutputExists) boxes.push({ x: nodeWidth - 8, y: 4, width: 16, height: 16 }); if (primaryOutputExists) boxes.push({ x: nodeWidth - 8, y: 4, width: 16, height: 16 });
// Exposed outputs
for (let i = 0; i < exposedOutputs; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 }); for (let i = 0; i < exposedOutputs; i++) boxes.push({ x: nodeWidth - 8, y: 4 + (i + 1) * 24, width: 16, height: 16 });
return borderMask(boxes, nodeWidth, nodeHeight);
}
function layerBorderMask(nodeWidth: number): string {
const NODE_HEIGHT = 2 * 24;
const THUMBNAIL_WIDTH = 96;
const FUDGE = 2;
const boxes: { x: number; y: number; width: number; height: number }[] = [];
// Left input
boxes.push({ x: -8, y: 16, width: 16, height: 16 });
// Thumbnail
boxes.push({ x: 24, y: -FUDGE, width: THUMBNAIL_WIDTH, height: NODE_HEIGHT + FUDGE * 2 });
return borderMask(boxes, nodeWidth, NODE_HEIGHT);
}
function borderMask(boxes: { x: number; y: number; width: number; height: number }[], nodeWidth: number, nodeHeight: number): string {
const rectangles = boxes.map((box) => `M${box.x},${box.y} L${box.x + box.width},${box.y} L${box.x + box.width},${box.y + box.height} L${box.x},${box.y + box.height}z`); const rectangles = boxes.map((box) => `M${box.x},${box.y} L${box.x + box.width},${box.y} L${box.x + box.width},${box.y + box.height} L${box.x},${box.y + box.height}z`);
return `M-2,-2 L${nodeWidth + 2},-2 L${nodeWidth + 2},${nodeHeight + 2} L-2,${nodeHeight + 2}z ${rectangles.join(" ")}`; return `M-2,-2 L${nodeWidth + 2},-2 L${nodeWidth + 2},${nodeHeight + 2} L-2,${nodeHeight + 2}z ${rectangles.join(" ")}`;
} }
@@ -599,7 +624,7 @@
{#each linkPaths as { pathString, dataType, thick }} {#each linkPaths as { pathString, dataType, thick }}
<path <path
d={pathString} d={pathString}
style:--data-line-width={`${thick ? 5 : 2}px`} style:--data-line-width={`${thick ? 8 : 2}px`}
style:--data-color={`var(--color-data-${dataType})`} style:--data-color={`var(--color-data-${dataType})`}
style:--data-color-dim={`var(--color-data-${dataType}-dim)`} style:--data-color-dim={`var(--color-data-${dataType}-dim)`}
/> />
@@ -650,7 +675,7 @@
style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`} style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`}
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`} style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType}-dim)`}
> >
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" /> <path d="M0,2.953,2.521,1.259a2.649,2.649,0,0,1,2.959,0L8,2.953V8H0Z" />
</svg> </svg>
{/if} {/if}
<svg <svg
@@ -662,17 +687,17 @@
style:--data-color={`var(--color-data-${stackDatainput.dataType})`} style:--data-color={`var(--color-data-${stackDatainput.dataType})`}
style:--data-color-dim={`var(--color-data-${stackDatainput.dataType}-dim)`} style:--data-color-dim={`var(--color-data-${stackDatainput.dataType}-dim)`}
> >
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" /> <path d="M0,0H8V8L5.479,6.319a2.666,2.666,0,0,0-2.959,0L0,8Z" />
</svg> </svg>
</div> </div>
<div class="details"> <div class="details">
<TextLabel>{node.displayName}</TextLabel> <TextLabel tooltip={node.displayName}>{node.displayName}</TextLabel>
</div> </div>
<svg class="border-mask" width="0" height="0"> <svg class="border-mask" width="0" height="0">
<defs> <defs>
<clipPath id={clipPathId}> <clipPath id={clipPathId}>
<path clip-rule="evenodd" d={buildBorderMask(120, node.primaryInput !== undefined, node.exposedInputs.length, node.primaryOutput !== undefined, node.exposedOutputs)} /> <path clip-rule="evenodd" d={layerBorderMask(216)} />
</clipPath> </clipPath>
</defs> </defs>
</svg> </svg>
@@ -696,15 +721,15 @@
<!-- Primary row --> <!-- Primary row -->
<div class="primary" class:no-parameter-section={exposedInputsOutputs.length === 0}> <div class="primary" class:no-parameter-section={exposedInputsOutputs.length === 0}>
<IconLabel icon={nodeIcon(node.displayName)} /> <IconLabel icon={nodeIcon(node.displayName)} />
<TextLabel>{node.displayName}</TextLabel> <TextLabel tooltip={node.displayName}>{node.displayName}</TextLabel>
</div> </div>
<!-- Parameter rows --> <!-- Parameter rows -->
{#if exposedInputsOutputs.length > 0} {#if exposedInputsOutputs.length > 0}
<div class="parameters"> <div class="parameters">
{#each exposedInputsOutputs as parameter, index} {#each exposedInputsOutputs as parameter, index}
<div class="parameter expanded"> <div class={`parameter expanded ${index < node.exposedInputs.length ? "input" : "output"}`}>
<div class="expand-arrow" /> <div class="expand-arrow" />
<TextLabel class={index < node.exposedInputs.length ? "name" : "output"}>{parameter.name}</TextLabel> <TextLabel tooltip={parameter.name}>{parameter.name}</TextLabel>
</div> </div>
{/each} {/each}
</div> </div>
@@ -715,7 +740,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="port primary-port"
data-port="input" data-port="input"
data-datatype={node.primaryInput} data-datatype={node.primaryInput}
style:--data-color={`var(--color-data-${node.primaryInput})`} style:--data-color={`var(--color-data-${node.primaryInput})`}
@@ -746,7 +771,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 8 8" viewBox="0 0 8 8"
class="port" class="port primary-port"
data-port="output" data-port="output"
data-datatype={node.primaryOutput.dataType} data-datatype={node.primaryOutput.dataType}
style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`} style:--data-color={`var(--color-data-${node.primaryOutput.dataType})`}
@@ -774,7 +799,7 @@
<clipPath id={clipPathId}> <clipPath id={clipPathId}>
<path <path
clip-rule="evenodd" clip-rule="evenodd"
d={buildBorderMask(120, node.primaryInput !== undefined, node.exposedInputs.length, node.primaryOutput !== undefined, node.exposedOutputs.length)} d={nodeBorderMask(120, node.primaryInput !== undefined, node.exposedInputs.length, node.primaryOutput !== undefined, node.exposedOutputs.length)}
/> />
</clipPath> </clipPath>
</defs> </defs>
@@ -940,14 +965,17 @@
top: 0; top: 0;
} }
&.selected { &.node.selected .primary {
.primary { background: rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.15); }
}
.parameters { &.node.selected .parameters {
background: rgba(255, 255, 255, 0.1); background: rgba(255, 255, 255, 0.1);
} }
&.layer.selected {
// This is the result of blending `rgba(255, 255, 255, 0.1)` over `rgba(0, 0, 0, 0.33)`
background: rgba(66, 66, 66, 0.4);
} }
&.disabled { &.disabled {
@@ -988,6 +1016,10 @@
&:first-of-type { &:first-of-type {
margin-top: calc((24px - 8px) / 2); margin-top: calc((24px - 8px) / 2);
&:not(.primary-port) {
margin-top: calc((24px - 8px) / 2 + 24px);
}
} }
&:last-of-type { &:last-of-type {
@@ -1022,11 +1054,16 @@
.expanded .expand-arrow::after { .expanded .expand-arrow::after {
transform: rotate(90deg); transform: rotate(90deg);
} }
.text-label {
overflow: hidden;
text-overflow: ellipsis;
}
} }
.layer { .layer {
border-radius: 8px; border-radius: 8px;
min-width: 216px; width: 216px;
&::after { &::after {
border: 1px solid var(--color-5-dullgray); border: 1px solid var(--color-5-dullgray);
@@ -1058,9 +1095,10 @@
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
margin: auto; margin: auto;
inset: 1px; top: 1px;
width: 100%; left: 1px;
height: 100%; width: calc(100% - 2px);
height: calc(100% - 2px);
} }
.port { .port {
@@ -1070,11 +1108,11 @@
right: 0; right: 0;
&.top { &.top {
top: -12px; top: -9px;
} }
&.bottom { &.bottom {
bottom: -12px; bottom: -9px;
} }
} }
} }
@@ -1099,7 +1137,7 @@
.node { .node {
flex-direction: column; flex-direction: column;
border-radius: 2px; border-radius: 2px;
min-width: 120px; width: 120px;
top: calc((var(--offset-top) + 0.5) * 24px); top: calc((var(--offset-top) + 0.5) * 24px);
&::after { &::after {
@@ -1122,10 +1160,12 @@
} }
.icon-label { .icon-label {
display: none; // Remove after we have unique icons for the nodes
margin: 0 8px; margin: 0 8px;
} }
.text-label { .text-label {
margin-left: 8px; // Remove after reenabling icon-label
margin-right: 4px; margin-right: 4px;
} }
} }
@@ -1147,15 +1187,22 @@
border-radius: 0 0 2px 2px; border-radius: 0 0 2px 2px;
} }
.expand-arrow {
margin-left: 4px;
}
.text-label { .text-label {
width: 100%; width: 100%;
}
&.output { &.input {
text-align: right; .expand-arrow {
margin-left: 4px;
}
}
&.output {
flex-direction: row-reverse;
text-align: right;
.expand-arrow {
margin-right: 4px;
} }
} }
} }