mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 04:28:12 +08:00
Improve the layers UI in the node graph
This commit is contained in:
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user