Skip to content

Commit d972f3e

Browse files
authored
ui: NodeGraph: Add options to the demo code for testing (#3480)
<img width="881" height="528" alt="image" src="https://github.com/user-attachments/assets/ab264a16-b83e-4e46-80e4-e837bec6e41a" />
1 parent f2e31d8 commit d972f3e

File tree

3 files changed

+124
-92
lines changed

3 files changed

+124
-92
lines changed

ui/src/assets/widgets/nodegraph.scss

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,22 @@
7171
left: 0;
7272
width: 10px;
7373
height: 100%;
74-
background: hsl(var(--pf-node-hue, 210), 60%, 50%);
74+
background-color: var(
75+
--pf-color-border
76+
); /* Fallback color when hue not set */
7577
border-top-left-radius: 6px;
7678
border-bottom-left-radius: 6px;
7779
}
7880

81+
/* Override with hue-based color when --pf-node-hue is set via inline styles */
82+
.pf-node[style*="--pf-node-hue"].pf-node--has-accent-bar::before {
83+
background: color-mix(
84+
in srgb,
85+
hsl(var(--pf-node-hue), 60%, 50%) 50%,
86+
var(--pf-color-background)
87+
);
88+
}
89+
7990
.pf-node-body {
8091
padding-block: 12px;
8192
}
@@ -137,13 +148,9 @@
137148
}
138149

139150
.pf-node-header {
140-
background: color-mix(
141-
in srgb,
142-
hsl(var(--pf-node-hue, 210), 60%, 50%) 50%,
143-
var(--pf-color-background)
144-
);
145-
padding: 8px 12px;
146-
font-weight: bold;
151+
/* Default background when hue is not set */
152+
background: var(--pf-color-background);
153+
padding: 6px 12px;
147154
border-radius: 6px 6px 0 0;
148155
display: flex;
149156
border-bottom: 1px solid var(--pf-color-border);
@@ -152,6 +159,19 @@
152159
gap: 8px;
153160
}
154161

162+
/* Override with hue-based color when --pf-node-hue is set via inline styles */
163+
.pf-node[style*="--pf-node-hue"] .pf-node-header {
164+
background: color-mix(
165+
in srgb,
166+
hsl(var(--pf-node-hue), 60%, 50%) 50%,
167+
var(--pf-color-background)
168+
);
169+
}
170+
171+
.pf-node--has-accent-bar .pf-node-header {
172+
padding-left: 22px;
173+
}
174+
155175
.pf-docked-child .pf-node-header {
156176
border-top-left-radius: 0;
157177
border-top-right-radius: 0;

ui/src/plugins/dev.perfetto.WidgetsPage/nodegraph_demo.ts

Lines changed: 88 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,14 @@ interface NodeTemplate {
4141
inputs: string[];
4242
outputs: string[];
4343
content?: m.Children;
44-
allInputsLeft?: boolean;
4544
}
4645

4746
interface NodeGraphDemoAttrs {
4847
readonly titleBars?: boolean;
4948
readonly accentBars?: boolean;
5049
readonly allInputsLeft?: boolean;
5150
readonly allOutputsRight?: boolean;
52-
readonly multiselect?: boolean;
51+
readonly colors?: boolean;
5352
}
5453

5554
export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
@@ -190,7 +189,6 @@ export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
190189
join: {
191190
inputs: ['Left', 'Right'],
192191
outputs: ['Output'],
193-
allInputsLeft: true,
194192
content: m(
195193
'',
196194
{style: {display: 'flex', flexDirection: 'column', gap: '4px'}},
@@ -231,89 +229,97 @@ export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
231229
return Array.from(modelNodes.keys()).filter((id) => !referenced.has(id));
232230
}
233231

234-
// Render model state into NodeGraph nodes
235-
function renderNodes(): Node[] {
236-
const rootIds = getRootNodeIds();
237-
return rootIds
238-
.map((id) => {
239-
const model = modelNodes.get(id);
240-
if (!model) return null;
241-
return renderNodeChain(model);
242-
})
243-
.filter((n): n is Node => n !== null);
244-
}
245-
246-
// Render a model node and its chain
247-
function renderNodeChain(model: ModelNode): Node {
248-
const template = nodeTemplates[model.type];
249-
const hasNext = model.nextId !== undefined;
250-
const nextModel = hasNext ? modelNodes.get(model.nextId!) : undefined;
251-
const allInputsLeft = template.allInputsLeft ?? false;
252-
253-
return {
254-
id: model.id,
255-
x: model.x,
256-
y: model.y,
257-
inputs: template.inputs,
258-
outputs: template.outputs,
259-
content: template.content,
260-
next: nextModel ? renderChildNode(nextModel) : undefined,
261-
allInputsLeft,
262-
accentBar: true,
263-
hue: nodeHues[model.type],
264-
addMenuItems: [
265-
m(MenuItem, {
266-
label: 'Select',
267-
icon: 'filter_alt',
268-
onclick: () => addNode('select', model.id),
269-
}),
270-
m(MenuItem, {
271-
label: 'Filter',
272-
icon: 'filter_list',
273-
onclick: () => addNode('filter', model.id),
274-
}),
275-
],
276-
};
277-
}
232+
const connections: Connection[] = [];
278233

279-
// Render child node (keep all ports visible)
280-
function renderChildNode(model: ModelNode): Omit<Node, 'x' | 'y'> {
281-
const template = nodeTemplates[model.type];
282-
const hasNext = model.nextId !== undefined;
283-
const nextModel = hasNext ? modelNodes.get(model.nextId!) : undefined;
234+
return {
235+
view: ({attrs}: m.Vnode<NodeGraphDemoAttrs>) => {
236+
// Render a model node and its chain
237+
function renderNodeChain(model: ModelNode): Node {
238+
const template = nodeTemplates[model.type];
239+
const hasNext = model.nextId !== undefined;
240+
const nextModel = hasNext ? modelNodes.get(model.nextId!) : undefined;
241+
242+
return {
243+
id: model.id,
244+
x: model.x,
245+
y: model.y,
246+
inputs: template.inputs,
247+
outputs: template.outputs,
248+
content: template.content,
249+
next: nextModel ? renderChildNode(nextModel) : undefined,
250+
allInputsLeft: attrs.allInputsLeft,
251+
allOutputsRight: attrs.allOutputsRight,
252+
accentBar: attrs.accentBars,
253+
titleBar: attrs.titleBars
254+
? {title: model.type.toUpperCase()}
255+
: undefined,
256+
hue: attrs.colors ? nodeHues[model.type] : undefined,
257+
addMenuItems: [
258+
m(MenuItem, {
259+
label: 'Select',
260+
icon: 'filter_alt',
261+
onclick: () => addNode('select', model.id),
262+
}),
263+
m(MenuItem, {
264+
label: 'Filter',
265+
icon: 'filter_list',
266+
onclick: () => addNode('filter', model.id),
267+
}),
268+
],
269+
};
270+
}
284271

285-
return {
286-
id: model.id,
287-
inputs: template.inputs,
288-
outputs: template.outputs,
289-
content: template.content,
290-
next: nextModel ? renderChildNode(nextModel) : undefined,
291-
accentBar: true,
292-
hue: nodeHues[model.type],
293-
addMenuItems: [
294-
m(MenuItem, {
295-
label: 'Select',
296-
icon: 'filter_alt',
297-
onclick: () => addNode('select', model.id),
298-
}),
299-
m(MenuItem, {
300-
label: 'Filter',
301-
icon: 'filter_list',
302-
onclick: () => addNode('filter', model.id),
303-
}),
304-
m(MenuItem, {
305-
label: 'Join',
306-
icon: 'join',
307-
onclick: () => addNode('join', model.id),
308-
}),
309-
],
310-
};
311-
}
272+
// Render child node (keep all ports visible)
273+
function renderChildNode(model: ModelNode): Omit<Node, 'x' | 'y'> {
274+
const template = nodeTemplates[model.type];
275+
const hasNext = model.nextId !== undefined;
276+
const nextModel = hasNext ? modelNodes.get(model.nextId!) : undefined;
277+
278+
return {
279+
id: model.id,
280+
inputs: template.inputs,
281+
outputs: template.outputs,
282+
content: template.content,
283+
next: nextModel ? renderChildNode(nextModel) : undefined,
284+
allInputsLeft: attrs.allInputsLeft,
285+
allOutputsRight: attrs.allOutputsRight,
286+
accentBar: attrs.accentBars,
287+
titleBar: attrs.titleBars
288+
? {title: model.type.toUpperCase()}
289+
: undefined,
290+
hue: attrs.colors ? nodeHues[model.type] : undefined,
291+
addMenuItems: [
292+
m(MenuItem, {
293+
label: 'Select',
294+
icon: 'filter_alt',
295+
onclick: () => addNode('select', model.id),
296+
}),
297+
m(MenuItem, {
298+
label: 'Filter',
299+
icon: 'filter_list',
300+
onclick: () => addNode('filter', model.id),
301+
}),
302+
m(MenuItem, {
303+
label: 'Join',
304+
icon: 'join',
305+
onclick: () => addNode('join', model.id),
306+
}),
307+
],
308+
};
309+
}
312310

313-
const connections: Connection[] = [];
311+
// Render model state into NodeGraph nodes
312+
function renderNodes(): Node[] {
313+
const rootIds = getRootNodeIds();
314+
return rootIds
315+
.map((id) => {
316+
const model = modelNodes.get(id);
317+
if (!model) return null;
318+
return renderNodeChain(model);
319+
})
320+
.filter((n): n is Node => n !== null);
321+
}
314322

315-
return {
316-
view: () => {
317323
return m('div', [
318324
// Add Node button
319325
m(

ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,8 +1654,14 @@ export class WidgetsPage implements m.ClassComponent<{app: App}> {
16541654
description:
16551655
'Scroll left and right to pan / pinch to zoom. Drag nodes from output to input ports to connect nodes. Click connectors or drag outputs away from ports to remove connections.',
16561656
wide: true,
1657-
renderWidget: () => m(NodeGraphDemo),
1658-
initialOpts: {},
1657+
renderWidget: (opts) => m(NodeGraphDemo, opts),
1658+
initialOpts: {
1659+
titleBars: false,
1660+
accentBars: false,
1661+
allInputsLeft: false,
1662+
allOutputsRight: false,
1663+
colors: false,
1664+
},
16591665
}),
16601666
);
16611667
}

0 commit comments

Comments
 (0)