Skip to content

Commit

Permalink
ui: Add WorkspaceManager
Browse files Browse the repository at this point in the history
Encapsulate the workspace-management logic into a class rather than
being baked into Globals. This makes it easier to move it into AppImpl
into next CLs.

Change-Id: I2bb562c9a5ab176615a7e59ab81d610acb68caa9
  • Loading branch information
primiano committed Sep 9, 2024
1 parent b4a9941 commit 6a4a99c
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 25 deletions.
6 changes: 5 additions & 1 deletion ui/src/common/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {defaultPlugins} from '../core/default_plugins';
import {PromptOption} from '../public/omnibox';
import {DisposableStack} from '../base/disposable_stack';
import {TraceInfo} from '../public/trace_info';
import {Workspace} from '../public/workspace';
import {Workspace, WorkspaceManager} from '../public/workspace';
import {Migrate, Store} from '../base/store';
import {LegacyDetailsPanel} from '../public/details_panel';

Expand Down Expand Up @@ -236,6 +236,10 @@ class PluginContextTraceImpl implements Trace, Disposable {
return globals.store.createSubStore(['plugins', this.pluginId], migrate);
}

get workspaces(): WorkspaceManager {
return globals.workspaceManager;
}

get traceInfo(): TraceInfo {
return globals.traceContext;
}
Expand Down
49 changes: 49 additions & 0 deletions ui/src/core/workspace_manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (C) 2024 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {assertTrue} from '../base/logging';
import {Workspace, WorkspaceManager} from '../public/workspace';

const DEFAULT_WORKSPACE_NAME = 'Default Workspace';

export class WorkspaceManagerImpl implements WorkspaceManager {
private _workspaces: Workspace[] = [];
private _currentWorkspace: Workspace;

constructor() {
// TS compiler cannot see that we are indirectly initializing
// _currentWorkspace via resetWorkspaces(), hence the re-assignment.
this._currentWorkspace = this.createEmptyWorkspace(DEFAULT_WORKSPACE_NAME);
}

createEmptyWorkspace(displayName: string): Workspace {
const workspace = new Workspace(displayName);
this._workspaces.push(workspace);
return workspace;
}

switchWorkspace(workspace: Workspace): void {
// If this fails the workspace doesn't come from createEmptyWorkspace().
assertTrue(this._workspaces.includes(workspace));
this._currentWorkspace = workspace;
}

get all(): ReadonlyArray<Workspace> {
return this._workspaces;
}

get currentWorkspace() {
return this._currentWorkspace;
}
}
14 changes: 7 additions & 7 deletions ui/src/core_plugins/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
addSqlTableTabImpl,
SqlTableTabConfig,
} from '../../frontend/sql_table_tab';
import {Workspace} from '../../public/workspace';

const SQL_STATS = `
with first as (select started as ts from sqlstats limit 1)
Expand Down Expand Up @@ -298,29 +297,30 @@ class CoreCommandsPlugin implements PerfettoPlugin {
callback: async () => {
const name = await ctx.omnibox.prompt('Give it a name...');
if (name === undefined || name === '') return;
const newWorkspace = new Workspace(name);
globals.workspaces.push(newWorkspace);
globals.switchWorkspace(newWorkspace);
globals.workspaceManager.switchWorkspace(
globals.workspaceManager.createEmptyWorkspace(name),
);
},
});

ctx.commands.registerCommand({
id: 'switchWorkspace',
name: 'Switch workspace',
callback: async () => {
const options = globals.workspaces.map((ws) => {
const workspaceManager = globals.workspaceManager;
const options = workspaceManager.all.map((ws) => {
return {key: ws.uuid, displayName: ws.displayName};
});
const workspaceUuid = await ctx.omnibox.prompt(
'Choose a workspace...',
options,
);
if (workspaceUuid === undefined) return;
const workspace = globals.workspaces.find(
const workspace = workspaceManager.all.find(
(ws) => ws.uuid === workspaceUuid,
);
if (workspace) {
globals.switchWorkspace(workspace);
workspaceManager.switchWorkspace(workspace);
}
},
});
Expand Down
24 changes: 8 additions & 16 deletions ui/src/frontend/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ import {
import {TraceInfo} from '../public/trace_info';
import {Registry} from '../base/registry';
import {SidebarMenuItem} from '../public/sidebar';
import {Workspace} from '../public/workspace';
import {Workspace, WorkspaceManager} from '../public/workspace';
import {ratelimit} from './rate_limiters';
import {NoteManagerImpl} from '../core/note_manager';
import {SearchManagerImpl} from '../core/search_manager';
import {SearchResult} from '../public/search';
import {selectCurrentSearchResult} from './search_handler';
import {WorkspaceManagerImpl} from '../core/workspace_manager';

const INSTANT_FOCUS_DURATION = 1n;
const INCOMPLETE_SLICE_DURATION = 30_000n;
Expand Down Expand Up @@ -174,8 +175,6 @@ interface SqlPackage {
readonly modules: SqlModule[];
}

const DEFAULT_WORKSPACE_NAME = 'Default Workspace';

/**
* Global accessors for state/dispatch in the frontend.
*/
Expand Down Expand Up @@ -216,8 +215,7 @@ class Globals {
private _noteManager = new NoteManagerImpl(this._selectionManager);
private _hasFtrace: boolean = false;
private _searchOverviewTrack?: SearchOverviewTrack;
readonly workspaces: Workspace[] = [];
private _currentWorkspace: Workspace;
private _workspaceManager = new WorkspaceManagerImpl();
readonly omnibox = new OmniboxManagerImpl();

// TODO(primiano): this is a hack to work around circular deps in globals.
Expand All @@ -237,11 +235,11 @@ class Globals {
readonly sidebarMenuItems = new Registry<SidebarMenuItem>((m) => m.commandId);

get workspace(): Workspace {
return this._currentWorkspace;
return this._workspaceManager.currentWorkspace;
}

switchWorkspace(workspace: Workspace): void {
this._currentWorkspace = workspace;
get workspaceManager(): WorkspaceManager {
return this._workspaceManager;
}

// This is the app's equivalent of a plugin's onTraceLoad() function.
Expand All @@ -252,10 +250,7 @@ class Globals {
this.traceContext = traceCtx;

// Reset workspaces
this.workspaces.length = 0;
const defaultWorkspace = new Workspace(DEFAULT_WORKSPACE_NAME);
this.workspaces.push(defaultWorkspace);
this._currentWorkspace = defaultWorkspace;
this._workspaceManager = new WorkspaceManagerImpl();

const {start, end} = traceCtx;
this._timeline = new TimelineImpl(new TimeSpan(start, end));
Expand All @@ -269,7 +264,7 @@ class Globals {
this._searchManager = new SearchManagerImpl({
timeline: this._timeline,
trackManager: this._trackManager,
workspace: this._currentWorkspace,
workspace: this._workspaceManager.currentWorkspace,
engine,
onResultStep: (step: SearchResult) => {
selectCurrentSearchResult(
Expand Down Expand Up @@ -316,9 +311,6 @@ class Globals {
constructor() {
const {start, end} = defaultTraceContext;
this._timeline = new TimelineImpl(new TimeSpan(start, end));
const defaultWorkspace = new Workspace(DEFAULT_WORKSPACE_NAME);
this.workspaces.push(defaultWorkspace);
this._currentWorkspace = defaultWorkspace;

this._selectionManager.onSelectionChange = (
_s: Selection,
Expand Down
3 changes: 2 additions & 1 deletion ui/src/public/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import {App} from './app';
import {TabManager} from './tab';
import {TrackManager} from './track';
import {Timeline} from './timeline';
import {Workspace, WorkspaceManager} from './workspace';
import {LegacyDetailsPanel} from './details_panel';
import {Workspace} from './workspace';
import {SelectionManager} from './selection';

/**
Expand All @@ -38,6 +38,7 @@ export interface Trace extends App {
readonly tracks: TrackManager;
readonly selection: SelectionManager;
readonly workspace: Workspace;
readonly workspaces: WorkspaceManager;
readonly traceInfo: TraceInfo;

// TODO(primiano): remove this once the Legacy vs non-Legacy details panel is
Expand Down
8 changes: 8 additions & 0 deletions ui/src/public/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ import {Optional} from '../base/utils';
import {uuidv4} from '../base/uuid';
import {raf} from '../core/raf_scheduler';

export interface WorkspaceManager {
// This is the same of ctx.workspace, exposed for consistency also here.
readonly currentWorkspace: Workspace;
readonly all: ReadonlyArray<Workspace>;
createEmptyWorkspace(displayName: string): Workspace;
switchWorkspace(workspace: Workspace): void;
}

export class TrackNode {
// This is the URI of the track this node references.
public readonly uri: string;
Expand Down

0 comments on commit 6a4a99c

Please sign in to comment.