diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index 83339b3ee8bc8..b35d70970de16 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -1620,6 +1620,15 @@ export class ApplicationShell extends Widget { return [...this.tracker.widgets]; } + getWidgetById(id: string): Widget | undefined { + for (const widget of this.tracker.widgets) { + if (widget.id === id) { + return widget; + } + } + return undefined; + } + canToggleMaximized(): boolean { const area = this.currentWidget && this.getAreaFor(this.currentWidget); return area === 'main' || area === 'bottom'; diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 29b549d8f8dba..e7ffb81be6de1 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -52,7 +52,40 @@ export class EditorManager extends NavigatableWidgetOpenHandler { super.init(); this.shell.activeChanged.connect(() => this.updateActiveEditor()); this.shell.currentChanged.connect(() => this.updateCurrentEditor()); - this.onCreated(widget => widget.disposed.connect(() => this.updateCurrentEditor())); + this.onCreated(widget => { + widget.onDidChangeVisibility(() => { + if (widget.isVisible) { + this.addRecentlyVisible(widget); + this.updateCurrentEditor(); + } + }); + widget.disposed.connect(() => { + this.removeRecentlyVisible(widget); + this.updateCurrentEditor(); + }); + }); + for (const widget of this.all) { + if (widget.isVisible) { + this.addRecentlyVisible(widget); + } + } + this.updateCurrentEditor(); + } + + protected readonly recentlyVisibleIds: string[] = []; + protected get recentlyVisible(): EditorWidget | undefined { + const id = this.recentlyVisibleIds[0]; + return id && this.all.find(w => w.id === id) || undefined; + } + protected addRecentlyVisible(widget: EditorWidget): void { + this.removeRecentlyVisible(widget); + this.recentlyVisibleIds.unshift(widget.id); + } + protected removeRecentlyVisible(widget: EditorWidget): void { + const index = this.recentlyVisibleIds.indexOf(widget.id); + if (index !== -1) { + this.recentlyVisibleIds.splice(index, 1); + } } protected _activeEditor: EditorWidget | undefined; @@ -93,7 +126,7 @@ export class EditorManager extends NavigatableWidgetOpenHandler { if (widget instanceof EditorWidget) { this.setCurrentEditor(widget); } else if (!this._currentEditor || !this._currentEditor.isVisible) { - this.setCurrentEditor(undefined); + this.setCurrentEditor(this.recentlyVisible); } } diff --git a/packages/markers/src/browser/marker-tree-label-provider.spec.ts b/packages/markers/src/browser/marker-tree-label-provider.spec.ts index f3341ec6f821f..49b8b93171924 100644 --- a/packages/markers/src/browser/marker-tree-label-provider.spec.ts +++ b/packages/markers/src/browser/marker-tree-label-provider.spec.ts @@ -21,9 +21,9 @@ let disableJSDOM = enableJSDOM(); import URI from '@theia/core/lib/common/uri'; import { expect } from 'chai'; import { Container } from 'inversify'; -import { ContributionProvider } from '@theia/core/lib/common'; +import { ContributionProvider, Event } from '@theia/core/lib/common'; import { FileStat, FileSystem } from '@theia/filesystem/lib/common'; -import { LabelProvider, LabelProviderContribution, DefaultUriLabelProviderContribution, ApplicationShell } from '@theia/core/lib/browser'; +import { LabelProvider, LabelProviderContribution, DefaultUriLabelProviderContribution, ApplicationShell, WidgetManager } from '@theia/core/lib/browser'; import { MarkerInfoNode } from './marker-tree'; import { MarkerTreeLabelProvider } from './marker-tree-label-provider'; import { Signal } from '@phosphor/signaling'; @@ -46,7 +46,12 @@ before(() => { testContainer.bind(WorkspaceService).toConstantValue(workspaceService); testContainer.bind(WorkspaceVariableContribution).toSelf().inSingletonScope(); testContainer.bind(ApplicationShell).toConstantValue({ - currentChanged: new Signal({}) + currentChanged: new Signal({}), + widgets: () => [] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + testContainer.bind(WidgetManager).toConstantValue({ + onDidCreateWidget: Event.None // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); testContainer.bind(FileSystem).to(MockFilesystem).inSingletonScope(); diff --git a/packages/workspace/src/browser/workspace-uri-contribution.spec.ts b/packages/workspace/src/browser/workspace-uri-contribution.spec.ts index d3977bbd49feb..ab1b6fb64cebf 100644 --- a/packages/workspace/src/browser/workspace-uri-contribution.spec.ts +++ b/packages/workspace/src/browser/workspace-uri-contribution.spec.ts @@ -21,7 +21,8 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import { Container } from 'inversify'; import { Signal } from '@phosphor/signaling'; -import { ApplicationShell } from '@theia/core/lib/browser'; +import { Event } from '@theia/core/lib/common/event'; +import { ApplicationShell, WidgetManager } from '@theia/core/lib/browser'; import { FileStat, FileSystem } from '@theia/filesystem/lib/common/filesystem'; import { MockFilesystem } from '@theia/filesystem/lib/common/test'; import { DefaultUriLabelProviderContribution } from '@theia/core/lib/browser/label-provider'; @@ -44,7 +45,12 @@ beforeEach(() => { container = new Container(); container.bind(ApplicationShell).toConstantValue({ - currentChanged: new Signal({}) + currentChanged: new Signal({}), + widgets: () => [] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + container.bind(WidgetManager).toConstantValue({ + onDidCreateWidget: Event.None // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any); const workspaceService = new WorkspaceService(); diff --git a/packages/workspace/src/browser/workspace-variable-contribution.ts b/packages/workspace/src/browser/workspace-variable-contribution.ts index 65eb458a7f04a..7f1433bdb3109 100644 --- a/packages/workspace/src/browser/workspace-variable-contribution.ts +++ b/packages/workspace/src/browser/workspace-variable-contribution.ts @@ -18,8 +18,7 @@ import { injectable, inject, postConstruct } from 'inversify'; import URI from '@theia/core/lib/common/uri'; import { Path } from '@theia/core/lib/common/path'; import { FileSystem } from '@theia/filesystem/lib/common'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { ApplicationShell, NavigatableWidget } from '@theia/core/lib/browser'; +import { ApplicationShell, NavigatableWidget, WidgetManager } from '@theia/core/lib/browser'; import { VariableContribution, VariableRegistry, Variable } from '@theia/variable-resolver/lib/browser'; import { WorkspaceService } from './workspace-service'; @@ -32,31 +31,62 @@ export class WorkspaceVariableContribution implements VariableContribution { protected readonly shell: ApplicationShell; @inject(FileSystem) protected readonly fileSystem: FileSystem; + @inject(WidgetManager) + protected readonly widgetManager: WidgetManager; protected currentWidget: NavigatableWidget | undefined; @postConstruct() protected init(): void { - this.updateCurrentWidget(); this.shell.currentChanged.connect(() => this.updateCurrentWidget()); + this.widgetManager.onDidCreateWidget(({ widget }) => { + if (NavigatableWidget.is(widget)) { + widget.onDidChangeVisibility(() => { + if (widget.isVisible) { + this.addRecentlyVisible(widget); + this.updateCurrentWidget(); + } + }); + widget.onDidDispose(() => { + this.removeRecentlyVisible(widget); + this.updateCurrentWidget(); + }); + } + }); + for (const widget of this.shell.widgets) { + if (NavigatableWidget.is(widget) && widget.isVisible) { + this.addRecentlyVisible(widget); + } + } + this.updateCurrentWidget(); } - protected updateCurrentWidget(): void { - const { currentWidget } = this.shell; - if (NavigatableWidget.is(currentWidget)) { - this.setCurrentWidget(currentWidget); + + protected readonly recentlyVisibleIds: string[] = []; + protected get recentlyVisible(): NavigatableWidget | undefined { + const id = this.recentlyVisibleIds[0]; + const widget = id && this.shell.getWidgetById(id) || undefined; + if (NavigatableWidget.is(widget)) { + return widget; + } + return undefined; + } + protected addRecentlyVisible(widget: NavigatableWidget): void { + this.removeRecentlyVisible(widget); + this.recentlyVisibleIds.unshift(widget.id); + } + protected removeRecentlyVisible(widget: NavigatableWidget): void { + const index = this.recentlyVisibleIds.indexOf(widget.id); + if (index !== -1) { + this.recentlyVisibleIds.splice(index, 1); } } - protected readonly toDisposeOnUpdateCurrentWidget = new DisposableCollection(); - protected setCurrentWidget(currentWidget: NavigatableWidget | undefined): void { - this.toDisposeOnUpdateCurrentWidget.dispose(); - this.currentWidget = currentWidget; - if (currentWidget) { - const resetCurrentWidget = () => this.setCurrentWidget(undefined); - currentWidget.disposed.connect(resetCurrentWidget); - this.toDisposeOnUpdateCurrentWidget.push(Disposable.create(() => - currentWidget.disposed.disconnect(resetCurrentWidget) - )); + protected updateCurrentWidget(): void { + const { currentWidget } = this.shell; + if (NavigatableWidget.is(currentWidget)) { + this.currentWidget = currentWidget; + } else if (!this.currentWidget || !this.currentWidget.isVisible) { + this.currentWidget = this.recentlyVisible; } } @@ -178,7 +208,6 @@ export class WorkspaceVariableContribution implements VariableContribution { } getResourceUri(): URI | undefined { - // TODO replace with ResourceContextKey.get? return this.currentWidget && this.currentWidget.getResourceUri(); }