| // 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 {Disposable} from '../base/disposable'; |
| import {DetailsPanel, LegacyDetailsPanel, TabDescriptor} from '../public'; |
| |
| export interface ResolvedTab { |
| uri: string; |
| tab?: TabDescriptor; |
| } |
| |
| /** |
| * Stores tab & current selection section registries. |
| * Keeps track of tab lifecycles. |
| */ |
| export class TabManager implements Disposable { |
| private _registry = new Map<string, TabDescriptor>(); |
| private _defaultTabs = new Set<string>(); |
| private _legacyDetailsPanelRegistry = new Set<LegacyDetailsPanel>(); |
| private _detailsPanelRegistry = new Set<DetailsPanel>(); |
| private _currentTabs = new Map<string, TabDescriptor>(); |
| |
| dispose(): void { |
| // Dispose of all tabs that are currently alive |
| for (const tab of this._currentTabs.values()) { |
| this.disposeTab(tab); |
| } |
| this._currentTabs.clear(); |
| } |
| |
| registerTab(desc: TabDescriptor): Disposable { |
| this._registry.set(desc.uri, desc); |
| return { |
| dispose: () => this._registry.delete(desc.uri), |
| }; |
| } |
| |
| addDefaultTab(uri: string): Disposable { |
| this._defaultTabs.add(uri); |
| return { |
| dispose: () => this._defaultTabs.delete(uri), |
| }; |
| } |
| |
| registerLegacyDetailsPanel(section: LegacyDetailsPanel): Disposable { |
| this._legacyDetailsPanelRegistry.add(section); |
| return { |
| dispose: () => this._legacyDetailsPanelRegistry.delete(section), |
| }; |
| } |
| |
| registerDetailsPanel(section: DetailsPanel): Disposable { |
| this._detailsPanelRegistry.add(section); |
| return { |
| dispose: () => this._detailsPanelRegistry.delete(section), |
| }; |
| } |
| |
| resolveTab(uri: string): TabDescriptor | undefined { |
| return this._registry.get(uri); |
| } |
| |
| get tabs(): TabDescriptor[] { |
| return Array.from(this._registry.values()); |
| } |
| |
| get defaultTabs(): string[] { |
| return Array.from(this._defaultTabs); |
| } |
| |
| get legacyDetailsPanels(): LegacyDetailsPanel[] { |
| return Array.from(this._legacyDetailsPanelRegistry); |
| } |
| |
| get detailsPanels(): DetailsPanel[] { |
| return Array.from(this._detailsPanelRegistry); |
| } |
| |
| /** |
| * Resolves a list of URIs to tabs and manages tab lifecycles. |
| * @param tabUris List of tabs. |
| * @return List of resolved tabs. |
| */ |
| resolveTabs(tabUris: string[]): ResolvedTab[] { |
| // Refresh the list of old tabs |
| const newTabs = new Map<string, TabDescriptor>(); |
| const tabs: ResolvedTab[] = []; |
| |
| tabUris.forEach((uri) => { |
| const newTab = this._registry.get(uri); |
| tabs.push({uri, tab: newTab}); |
| |
| if (newTab) { |
| newTabs.set(uri, newTab); |
| } |
| }); |
| |
| // Call onShow() on any new tabs. |
| for (const [uri, tab] of newTabs) { |
| const oldTab = this._currentTabs.get(uri); |
| if (!oldTab) { |
| this.initTab(tab); |
| } |
| } |
| |
| // Call onHide() on any tabs that have been removed. |
| for (const [uri, tab] of this._currentTabs) { |
| const newTab = newTabs.get(uri); |
| if (!newTab) { |
| this.disposeTab(tab); |
| } |
| } |
| |
| this._currentTabs = newTabs; |
| |
| return tabs; |
| } |
| |
| /** |
| * Call onShow() on this tab. |
| * @param tab The tab to initialize. |
| */ |
| private initTab(tab: TabDescriptor): void { |
| tab.onShow?.(); |
| } |
| |
| /** |
| * Call onHide() and maybe remove from registry if tab is ephemeral. |
| * @param tab The tab to dispose. |
| */ |
| private disposeTab(tab: TabDescriptor): void { |
| // Attempt to call onHide |
| tab.onHide?.(); |
| |
| // If ephemeral, also unregister the tab |
| if (tab.isEphemeral) { |
| this._registry.delete(tab.uri); |
| } |
| } |
| } |