From c004f3fe86beb193e61c74dad41bc67d24e1d3d4 Mon Sep 17 00:00:00 2001 From: Steve Golton Date: Sat, 14 Dec 2024 13:35:07 +0000 Subject: [PATCH] ui: Add TrackNode.clone() method Change-Id: I800823351847bb5005c6be15977e5657f5f1ef90 --- .../com.example.ExampleNestedTracks/index.ts | 20 ++++++++ ui/src/public/workspace.ts | 16 +++++++ ui/src/public/workspace_unittest.ts | 46 +++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/ui/src/plugins/com.example.ExampleNestedTracks/index.ts b/ui/src/plugins/com.example.ExampleNestedTracks/index.ts index da4381853a..8d2d78f30c 100644 --- a/ui/src/plugins/com.example.ExampleNestedTracks/index.ts +++ b/ui/src/plugins/com.example.ExampleNestedTracks/index.ts @@ -72,5 +72,25 @@ export default class implements PerfettoPlugin { track1.addChildLast(track12); track12.addChildLast(track121); track2.addChildLast(track21); + + ctx.commands.registerCommand({ + id: 'com.example.ExampleNestedTracks#CloneTracksToNewWorkspace', + name: 'Clone track to new workspace', + callback: () => { + const ws = ctx.workspaces.createEmptyWorkspace('New workspace'); + ws.addChildLast(trackRoot.clone()); + ctx.workspaces.switchWorkspace(ws); + }, + }); + + ctx.commands.registerCommand({ + id: 'com.example.ExampleNestedTracks#DeepCloneTracksToNewWorkspace', + name: 'Clone all tracks to new workspace', + callback: () => { + const ws = ctx.workspaces.createEmptyWorkspace('Deep workspace'); + ws.addChildLast(trackRoot.clone(true)); + ctx.workspaces.switchWorkspace(ws); + }, + }); } } diff --git a/ui/src/public/workspace.ts b/ui/src/public/workspace.ts index 3580e69885..013771df43 100644 --- a/ui/src/public/workspace.ts +++ b/ui/src/public/workspace.ts @@ -466,6 +466,22 @@ export class TrackNode { return this.tracksByUri.get(uri); } + /** + * Creates a copy of this node with a new ID. + * + * @param deep - If true, children are copied too. + * @returns - A copy of this node. + */ + clone(deep = false): TrackNode { + const cloned = new TrackNode({...this, id: undefined}); + if (deep) { + this.children.forEach((c) => { + cloned.addChildLast(c.clone(deep)); + }); + } + return cloned; + } + private adopt(child: TrackNode): void { if (child.parent) { child.parent.removeChild(child); diff --git a/ui/src/public/workspace_unittest.ts b/ui/src/public/workspace_unittest.ts index fd36498193..333496a374 100644 --- a/ui/src/public/workspace_unittest.ts +++ b/ui/src/public/workspace_unittest.ts @@ -224,3 +224,49 @@ test('TrackNode::flatTracks', () => { ); expect(root.flatTracks.length).toBe(4); }); + +test('TrackNode::clone', () => { + const root = new TrackNode(); + const childA = new TrackNode(); + root.addChildLast(childA); + + const childB = new TrackNode(); + root.addChildLast(childB); + + const cloned = root.clone(); + + expect(cloned.id).not.toBe(root.id); // id should be different + expect(cloned.uri).toBe(root.uri); + expect(cloned.expanded).toBe(root.expanded); + expect(cloned.title).toBe(root.title); + expect(cloned.headless).toBe(root.headless); + expect(cloned.isSummary).toBe(root.isSummary); + expect(cloned.removable).toBe(root.removable); + expect(cloned.children).toStrictEqual([]); // Children should not be copied +}); + +test('TrackNode::clone(deep)', () => { + const root = new TrackNode(); + const childA = new TrackNode(); + root.addChildLast(childA); + + const childB = new TrackNode(); + root.addChildLast(childB); + + const cloned = root.clone(true); + + expect(cloned.id).not.toBe(root.id); // id should be different + expect(cloned.uri).toBe(root.uri); + expect(cloned.expanded).toBe(root.expanded); + expect(cloned.title).toBe(root.title); + expect(cloned.headless).toBe(root.headless); + expect(cloned.isSummary).toBe(root.isSummary); + expect(cloned.removable).toBe(root.removable); + expect(cloned.children).toHaveLength(2); + + expect(cloned.children[0].title).toBe(childA.title); + expect(cloned.children[0].uri).toBe(childA.uri); + + expect(cloned.children[1].title).toBe(childB.title); + expect(cloned.children[1].uri).toBe(childB.uri); +});