Skip to content

Commit e4a0e43

Browse files
committed
NodeGraph: Add multi-node select
Change-Id: I56ca8c38029e027707d9016aaa70c31d207145b3
1 parent 9fccb63 commit e4a0e43

File tree

5 files changed

+256
-36
lines changed

5 files changed

+256
-36
lines changed

ui/src/assets/widgets/nodegraph.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,11 @@ svg {
333333
.pf-panning {
334334
cursor: grabbing;
335335
}
336+
337+
.pf-selection-rect {
338+
position: absolute;
339+
border: 2px solid var(--pf-color-accent);
340+
background: color-mix(in srgb, var(--pf-color-accent) 15%, transparent);
341+
pointer-events: none;
342+
z-index: 1000;
343+
}

ui/src/plugins/dev.perfetto.ExplorePage/query_builder/graph/graph.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -594,21 +594,21 @@ export class Graph implements m.ClassComponent<GraphAttrs> {
594594
m(NodeGraph, {
595595
nodes,
596596
connections,
597-
selectedNodeId: selectedNode?.nodeId ?? null,
597+
selectedNodeIds: selectedNode?.nodeId ? [selectedNode.nodeId] : [],
598598
hideControls: true,
599599
onReady: (api: NodeGraphApi) => {
600600
this.nodeGraphApi = api;
601601
},
602-
onNodeSelect: (nodeId: string | null) => {
603-
if (nodeId === null) {
604-
attrs.onDeselect();
605-
} else {
606-
const qnode = findQueryNode(nodeId, rootNodes);
607-
if (qnode) {
608-
attrs.onNodeSelected(qnode);
609-
}
602+
multiselect: false,
603+
onNodeSelect: (nodeId: string) => {
604+
const qnode = findQueryNode(nodeId, rootNodes);
605+
if (qnode) {
606+
attrs.onNodeSelected(qnode);
610607
}
611608
},
609+
onSelectionClear: () => {
610+
attrs.onDeselect();
611+
},
612612
onNodeDrag: (nodeId: string, x: number, y: number) => {
613613
attrs.onNodeLayoutChange(nodeId, {x, y});
614614
},

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

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,13 @@ interface NodeGraphDemoAttrs {
4848
readonly accentBars?: boolean;
4949
readonly allInputsLeft?: boolean;
5050
readonly allOutputsRight?: boolean;
51+
readonly multiselect?: boolean;
5152
readonly colors?: boolean;
5253
}
5354

5455
export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
55-
let selectedNodeId: string | null = null;
5656
let graphApi: NodeGraphApi | undefined;
57+
const selectedNodeIds = new Set<string>();
5758

5859
// State for select node checkboxes
5960
const columnOptions = {
@@ -376,17 +377,18 @@ export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
376377
m(NodeGraph, {
377378
nodes: renderNodes(),
378379
connections: connections,
379-
selectedNodeId: selectedNodeId,
380380
onReady: (api: NodeGraphApi) => {
381381
graphApi = api;
382382
},
383+
selectedNodeIds: Array.from(selectedNodeIds),
383384
onNodeDrag: (nodeId: string, x: number, y: number) => {
384385
const model = modelNodes.get(nodeId);
385386
if (model) {
386387
model.x = x;
387388
model.y = y;
388389
}
389390
},
391+
multiselect: attrs.multiselect,
390392
onConnect: (conn: Connection) => {
391393
console.log('onConnect:', conn);
392394
connections.push(conn);
@@ -408,7 +410,7 @@ export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
408410
}
409411

410412
// Clear selection if needed
411-
if (selectedNodeId === nodeId) selectedNodeId = null;
413+
selectedNodeIds.delete(nodeId);
412414

413415
// Remove any connections to/from this node
414416
for (let i = connections.length - 1; i >= 0; i--) {
@@ -425,10 +427,27 @@ export function NodeGraphDemo(): m.Component<NodeGraphDemoAttrs> {
425427

426428
console.log(`onNodeRemove: ${nodeId}`);
427429
},
428-
onNodeSelect: (nodeId: string | null) => {
429-
selectedNodeId = nodeId;
430+
onNodeSelect: (nodeId: string) => {
431+
selectedNodeIds.clear();
432+
selectedNodeIds.add(nodeId);
430433
console.log(`onNodeSelect: ${nodeId}`);
431434
},
435+
onNodeAddToSelection: (nodeId: string) => {
436+
selectedNodeIds.add(nodeId);
437+
console.log(
438+
`onNodeAddToSelection: ${nodeId} (total: ${selectedNodeIds.size})`,
439+
);
440+
},
441+
onNodeRemoveFromSelection: (nodeId: string) => {
442+
selectedNodeIds.delete(nodeId);
443+
console.log(
444+
`onNodeRemoveFromSelection: ${nodeId} (total: ${selectedNodeIds.size})`,
445+
);
446+
},
447+
onSelectionClear: () => {
448+
selectedNodeIds.clear();
449+
console.log(`onSelectionClear`);
450+
},
432451
onDock: (targetId: string, childNode: Omit<Node, 'x' | 'y'>) => {
433452
const target = modelNodes.get(targetId);
434453
const child = modelNodes.get(childNode.id);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,7 @@ export class WidgetsPage implements m.ClassComponent<{app: App}> {
16601660
accentBars: false,
16611661
allInputsLeft: false,
16621662
allOutputsRight: false,
1663+
multiselect: false,
16631664
colors: false,
16641665
},
16651666
}),

0 commit comments

Comments
 (0)