diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index bc21b7277c135..3c35877a40668 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -141,6 +141,7 @@ import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './s import { LanguageIconLabelProvider } from './language-icon-provider'; import { bindTreePreferences } from './tree'; import { OpenWithService } from './open-with-service'; +import { ViewColumnService } from './shell/view-column-service'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -462,4 +463,6 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(FrontendApplicationContribution).toService(StylingService); bind(SecondaryWindowHandler).toSelf().inSingletonScope(); + + bind(ViewColumnService).toSelf().inSingletonScope(); }); diff --git a/packages/plugin-ext/src/main/browser/view-column-service.ts b/packages/core/src/browser/shell/view-column-service.ts similarity index 92% rename from packages/plugin-ext/src/main/browser/view-column-service.ts rename to packages/core/src/browser/shell/view-column-service.ts index a44a18b6482f3..16db95bc190d8 100644 --- a/packages/plugin-ext/src/main/browser/view-column-service.ts +++ b/packages/core/src/browser/shell/view-column-service.ts @@ -14,11 +14,11 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; -import { Emitter, Event } from '@theia/core/lib/common/event'; -import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; -import { toArray } from '@theia/core/shared/@phosphor/algorithm'; -import { TabBar, Widget } from '@theia/core/shared/@phosphor/widgets'; +import { injectable, inject } from 'inversify'; +import { Emitter, Event } from '../../common/event'; +import { ApplicationShell } from './application-shell'; +import { toArray } from '@phosphor/algorithm'; +import { TabBar, Widget } from '@phosphor/widgets'; @injectable() export class ViewColumnService { diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 10c4a6f29b79c..dc8d2bfdaf5c8 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -47,7 +47,6 @@ import { PluginDebugService } from './debug/plugin-debug-service'; import { DebugService } from '@theia/debug/lib/common/debug-service'; import { PluginSharedStyle } from './plugin-shared-style'; import { SelectionProviderCommandContribution } from './selection-provider-command'; -import { ViewColumnService } from './view-column-service'; import { ViewContextKeyService } from './view/view-context-key-service'; import { PluginViewWidget, PluginViewWidgetIdentifier } from './view/plugin-view-widget'; import { TreeViewWidgetOptions, VIEW_ITEM_CONTEXT_MENU, PluginTree, TreeViewWidget, PluginTreeModel } from './view/tree-view-widget'; @@ -244,8 +243,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(PluginDebugSessionContributionRegistry).toSelf().inSingletonScope(); rebind(DebugSessionContributionRegistry).toService(PluginDebugSessionContributionRegistry); - bind(ViewColumnService).toSelf().inSingletonScope(); - bind(CommentsService).to(PluginCommentService).inSingletonScope(); bind(CommentingRangeDecorator).toSelf().inSingletonScope(); bind(CommentsContribution).toSelf().inSingletonScope(); diff --git a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts index 5375409373fb8..bd8f83885aad4 100644 --- a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts +++ b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts @@ -24,6 +24,8 @@ import { MonacoDiffEditor } from '@theia/monaco/lib/browser/monaco-diff-editor'; import { toUriComponents } from '../hierarchy/hierarchy-types-converters'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { DisposableCollection } from '@theia/core'; +import { NotebookEditorWidget } from '@theia/notebook/lib/browser'; +import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service'; interface TabInfo { tab: TabDto; @@ -46,6 +48,14 @@ export class TabsMainImpl implements TabsMain, Disposable { private currentActiveGroup: TabGroupDto; private tabGroupChanged: boolean = false; + private viewColumnService: ViewColumnService; + + private readonly defaultTabGroup: TabGroupDto = { + groupId: 0, + tabs: [], + isActive: true, + viewColumn: 0 + }; constructor( rpc: RPCProtocol, @@ -54,6 +64,7 @@ export class TabsMainImpl implements TabsMain, Disposable { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TABS_EXT); this.applicationShell = container.get(ApplicationShell); + this.viewColumnService = container.get(ViewColumnService); this.createTabsModel(); const tabBars = this.applicationShell.mainPanel.tabBars(); @@ -100,15 +111,20 @@ export class TabsMainImpl implements TabsMain, Disposable { } protected createTabsModel(): void { + if (this.applicationShell.mainAreaTabBars.length === 0) { + this.proxy.$acceptEditorTabModel([this.defaultTabGroup]); + return; + } const newTabGroupModel = new Map, TabGroupDto>(); this.tabInfoLookup.clear(); this.disposableTabBarListeners.dispose(); - this.applicationShell.mainAreaTabBars.forEach(tabBar => { - this.attachListenersToTabBar(tabBar); - const groupDto = this.createTabGroupDto(tabBar); - tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index })); - newTabGroupModel.set(tabBar, groupDto); - }); + this.applicationShell.mainAreaTabBars + .forEach((tabBar, i) => { + this.attachListenersToTabBar(tabBar); + const groupDto = this.createTabGroupDto(tabBar, i); + tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index })); + newTabGroupModel.set(tabBar, groupDto); + }); if (newTabGroupModel.size > 0 && Array.from(newTabGroupModel.values()).indexOf(this.currentActiveGroup) < 0) { this.currentActiveGroup = this.tabInfoLookup.get(this.applicationShell.mainPanel.currentTitle!)?.group ?? newTabGroupModel.values().next().value; this.currentActiveGroup.isActive = true; @@ -134,15 +150,17 @@ export class TabsMainImpl implements TabsMain, Disposable { return `${groupId}~${tabTitle.owner.id}`; } - protected createTabGroupDto(tabBar: TabBar): TabGroupDto { - const oldDto = this.tabGroupModel.get(tabBar); + protected createTabGroupDto(tabBar: TabBar, index: number): TabGroupDto { + const oldDto = index === 0 ? this.defaultTabGroup : this.tabGroupModel.get(tabBar); const groupId = oldDto?.groupId ?? this.groupIdCounter++; const tabs = tabBar.titles.map(title => this.createTabDto(title, groupId)); + const viewColumn = this.viewColumnService.getViewColumn(tabBar.id) + ?? this.applicationShell.allTabBars.indexOf(tabBar); return { groupId, tabs, isActive: false, - viewColumn: 1 + viewColumn }; } @@ -182,6 +200,12 @@ export class TabsMainImpl implements TabsMain, Disposable { return { kind: TabInputKind.TerminalEditorInput }; + } else if (widget instanceof NotebookEditorWidget) { + return { + kind: TabInputKind.NotebookInput, + notebookType: widget.notebookType, + uri: toUriComponents(widget.model?.uri.toString() ?? '') + }; } return { kind: TabInputKind.UnknownInput }; @@ -232,6 +256,9 @@ export class TabsMainImpl implements TabsMain, Disposable { } const oldTabDto = tabInfo.tab; const newTabDto = this.createTabDto(title, tabInfo.group.groupId); + if (!oldTabDto.isActive && newTabDto.isActive) { + this.currentActiveGroup.tabs.filter(tab => tab.isActive).forEach(tab => tab.isActive = false); + } if (newTabDto.isActive && !tabInfo.group.isActive) { tabInfo.group.isActive = true; this.currentActiveGroup.isActive = false; diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index cf0f319950ba4..862010101dbf1 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -23,7 +23,7 @@ import { ViewBadge, WebviewOptions, WebviewPanelOptions, WebviewPanelShowOptions import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { WebviewWidget, WebviewWidgetIdentifier } from './webview/webview'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { ViewColumnService } from './view-column-service'; +import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { JSONExt } from '@theia/core/shared/@phosphor/coreutils'; import { Mutable } from '@theia/core/lib/common/types'; diff --git a/packages/plugin-ext/src/plugin/tabs.ts b/packages/plugin-ext/src/plugin/tabs.ts index a1a4761632371..80a263b778bf3 100644 --- a/packages/plugin-ext/src/plugin/tabs.ts +++ b/packages/plugin-ext/src/plugin/tabs.ts @@ -325,8 +325,13 @@ export class TabsExtImpl implements TabsExt { this.activeGroupId = activeTabGroupId; } } - this.onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); - this.onDidChangeTabs.fire({ opened: tabsOpened, changed: [], closed: [] }); + + if (closed.length > 0 || opened.length > 0 || changed.length > 0) { + this.onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); + } + if (tabsOpened.length > 0) { + this.onDidChangeTabs.fire({ opened: tabsOpened, changed: [], closed: [] }); + } } $acceptTabGroupUpdate(groupDto: TabGroupDto): void {