@theia/core 1.50.0 → 1.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/i18n/nls.cs.json +0 -7
- package/i18n/nls.de.json +0 -7
- package/i18n/nls.es.json +0 -7
- package/i18n/nls.fr.json +0 -7
- package/i18n/nls.hu.json +0 -7
- package/i18n/nls.it.json +0 -7
- package/i18n/nls.ja.json +0 -7
- package/i18n/nls.json +0 -7
- package/i18n/nls.pl.json +0 -7
- package/i18n/nls.pt-br.json +0 -7
- package/i18n/nls.pt-pt.json +0 -7
- package/i18n/nls.ru.json +0 -7
- package/i18n/nls.zh-cn.json +0 -7
- package/lib/browser/authentication-service.d.ts +6 -3
- package/lib/browser/authentication-service.d.ts.map +1 -1
- package/lib/browser/authentication-service.js +11 -1
- package/lib/browser/authentication-service.js.map +1 -1
- package/lib/browser/browser.d.ts +6 -1
- package/lib/browser/browser.d.ts.map +1 -1
- package/lib/browser/browser.js +7 -2
- package/lib/browser/browser.js.map +1 -1
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +6 -5
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/frontend-application-module.d.ts.map +1 -1
- package/lib/browser/frontend-application-module.js +3 -3
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/http-open-handler.d.ts +1 -0
- package/lib/browser/http-open-handler.d.ts.map +1 -1
- package/lib/browser/http-open-handler.js +5 -3
- package/lib/browser/http-open-handler.js.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts +2 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +22 -19
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/messaging/service-connection-provider.d.ts +15 -1
- package/lib/browser/messaging/service-connection-provider.d.ts.map +1 -1
- package/lib/browser/messaging/service-connection-provider.js +15 -1
- package/lib/browser/messaging/service-connection-provider.js.map +1 -1
- package/lib/browser/saveable.d.ts +14 -1
- package/lib/browser/saveable.d.ts.map +1 -1
- package/lib/browser/saveable.js +10 -1
- package/lib/browser/saveable.js.map +1 -1
- package/lib/browser/shell/additional-views-menu-widget.d.ts +2 -2
- package/lib/browser/shell/additional-views-menu-widget.d.ts.map +1 -1
- package/lib/browser/shell/additional-views-menu-widget.js +8 -4
- package/lib/browser/shell/additional-views-menu-widget.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts +7 -2
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +9 -0
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/side-panel-handler.d.ts.map +1 -1
- package/lib/browser/shell/side-panel-handler.js +2 -1
- package/lib/browser/shell/side-panel-handler.js.map +1 -1
- package/lib/browser/shell/sidebar-menu-widget.d.ts +14 -1
- package/lib/browser/shell/sidebar-menu-widget.d.ts.map +1 -1
- package/lib/browser/shell/sidebar-menu-widget.js +51 -13
- package/lib/browser/shell/sidebar-menu-widget.js.map +1 -1
- package/lib/browser/shell/tab-bars.d.ts +5 -4
- package/lib/browser/shell/tab-bars.d.ts.map +1 -1
- package/lib/browser/shell/tab-bars.js +40 -51
- package/lib/browser/shell/tab-bars.js.map +1 -1
- package/lib/browser/shell/theia-dock-panel.d.ts +7 -1
- package/lib/browser/shell/theia-dock-panel.d.ts.map +1 -1
- package/lib/browser/shell/theia-dock-panel.js +5 -1
- package/lib/browser/shell/theia-dock-panel.js.map +1 -1
- package/lib/browser/widgets/react-renderer.d.ts.map +1 -1
- package/lib/browser/widgets/react-renderer.js +4 -1
- package/lib/browser/widgets/react-renderer.js.map +1 -1
- package/lib/browser-only/frontend-only-application-module.d.ts.map +1 -1
- package/lib/browser-only/frontend-only-application-module.js +1 -0
- package/lib/browser-only/frontend-only-application-module.js.map +1 -1
- package/lib/common/application-protocol.d.ts +1 -0
- package/lib/common/application-protocol.d.ts.map +1 -1
- package/lib/common/menu/menu-model-registry.d.ts +6 -0
- package/lib/common/menu/menu-model-registry.d.ts.map +1 -1
- package/lib/common/menu/menu-model-registry.js +31 -5
- package/lib/common/menu/menu-model-registry.js.map +1 -1
- package/lib/common/preferences/preference-schema.d.ts +1 -0
- package/lib/common/preferences/preference-schema.d.ts.map +1 -1
- package/lib/common/preferences/preference-schema.js.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +3 -0
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-browser/preload.js +2 -2
- package/lib/electron-browser/preload.js.map +1 -1
- package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-window-module.js +11 -7
- package/lib/electron-browser/window/electron-window-module.js.map +1 -1
- package/lib/electron-browser/window/external-app-open-handler.d.ts +12 -0
- package/lib/electron-browser/window/external-app-open-handler.d.ts.map +1 -0
- package/lib/electron-browser/window/external-app-open-handler.js +42 -0
- package/lib/electron-browser/window/external-app-open-handler.js.map +1 -0
- package/lib/electron-common/electron-api.d.ts +4 -1
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts +1 -2
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +62 -32
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-main/electron-main-constants.d.ts +1 -0
- package/lib/electron-main/electron-main-constants.d.ts.map +1 -1
- package/lib/node/application-server.d.ts +1 -0
- package/lib/node/application-server.d.ts.map +1 -1
- package/lib/node/application-server.js +3 -0
- package/lib/node/application-server.js.map +1 -1
- package/package.json +6 -6
- package/src/browser/authentication-service.ts +16 -4
- package/src/browser/browser.ts +6 -1
- package/src/browser/common-frontend-contribution.ts +9 -7
- package/src/browser/frontend-application-module.ts +6 -5
- package/src/browser/http-open-handler.ts +3 -1
- package/src/browser/menu/browser-menu-plugin.ts +27 -20
- package/src/browser/messaging/service-connection-provider.ts +15 -1
- package/src/browser/saveable.ts +17 -1
- package/src/browser/shell/additional-views-menu-widget.tsx +5 -5
- package/src/browser/shell/application-shell.ts +16 -3
- package/src/browser/shell/side-panel-handler.ts +2 -1
- package/src/browser/shell/sidebar-menu-widget.tsx +63 -20
- package/src/browser/shell/tab-bars.ts +39 -56
- package/src/browser/shell/theia-dock-panel.ts +13 -3
- package/src/browser/style/sidepanel.css +6 -3
- package/src/browser/style/tabs.css +1 -1
- package/src/browser/widgets/react-renderer.tsx +4 -1
- package/src/browser-only/frontend-only-application-module.ts +1 -0
- package/src/common/application-protocol.ts +1 -0
- package/src/common/menu/menu-model-registry.ts +36 -5
- package/src/common/preferences/preference-schema.ts +1 -0
- package/src/electron-browser/menu/electron-main-menu-factory.ts +3 -0
- package/src/electron-browser/preload.ts +2 -2
- package/src/electron-browser/window/electron-window-module.ts +11 -7
- package/src/electron-browser/window/external-app-open-handler.ts +42 -0
- package/src/electron-common/electron-api.ts +5 -1
- package/src/electron-main/electron-main-application.ts +70 -35
- package/src/electron-main/electron-main-constants.ts +4 -3
- package/src/node/application-server.ts +4 -0
package/src/browser/saveable.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { Key } from './keyboard/keys';
|
|
|
22
22
|
import { AbstractDialog } from './dialogs';
|
|
23
23
|
import { nls } from '../common/nls';
|
|
24
24
|
import { DisposableCollection, isObject } from '../common';
|
|
25
|
+
import { BinaryBuffer } from '../common/buffer';
|
|
25
26
|
|
|
26
27
|
export type AutoSaveMode = 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange';
|
|
27
28
|
|
|
@@ -46,13 +47,17 @@ export interface Saveable {
|
|
|
46
47
|
*/
|
|
47
48
|
revert?(options?: Saveable.RevertOptions): Promise<void>;
|
|
48
49
|
/**
|
|
49
|
-
* Creates a snapshot of the dirty state.
|
|
50
|
+
* Creates a snapshot of the dirty state. See also {@link Saveable.Snapshot}.
|
|
50
51
|
*/
|
|
51
52
|
createSnapshot?(): Saveable.Snapshot;
|
|
52
53
|
/**
|
|
53
54
|
* Applies the given snapshot to the dirty state.
|
|
54
55
|
*/
|
|
55
56
|
applySnapshot?(snapshot: object): void;
|
|
57
|
+
/**
|
|
58
|
+
* Serializes the full state of the saveable item to a binary buffer.
|
|
59
|
+
*/
|
|
60
|
+
serialize?(): Promise<BinaryBuffer>;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
export interface SaveableSource {
|
|
@@ -79,6 +84,7 @@ export class DelegatingSaveable implements Saveable {
|
|
|
79
84
|
revert?(options?: Saveable.RevertOptions): Promise<void>;
|
|
80
85
|
createSnapshot?(): Saveable.Snapshot;
|
|
81
86
|
applySnapshot?(snapshot: object): void;
|
|
87
|
+
serialize?(): Promise<BinaryBuffer>;
|
|
82
88
|
|
|
83
89
|
protected _delegate?: Saveable;
|
|
84
90
|
protected toDispose = new DisposableCollection();
|
|
@@ -101,6 +107,7 @@ export class DelegatingSaveable implements Saveable {
|
|
|
101
107
|
this.revert = delegate.revert?.bind(delegate);
|
|
102
108
|
this.createSnapshot = delegate.createSnapshot?.bind(delegate);
|
|
103
109
|
this.applySnapshot = delegate.applySnapshot?.bind(delegate);
|
|
110
|
+
this.serialize = delegate.serialize?.bind(delegate);
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
}
|
|
@@ -114,7 +121,16 @@ export namespace Saveable {
|
|
|
114
121
|
soft?: boolean
|
|
115
122
|
}
|
|
116
123
|
|
|
124
|
+
/**
|
|
125
|
+
* A snapshot of a saveable item.
|
|
126
|
+
* Applying a snapshot of a saveable on another (of the same type) using the `applySnapshot` should yield the state of the original saveable.
|
|
127
|
+
*/
|
|
117
128
|
export type Snapshot = { value: string } | { read(): string | null };
|
|
129
|
+
export namespace Snapshot {
|
|
130
|
+
export function read(snapshot: Snapshot): string | undefined {
|
|
131
|
+
return 'value' in snapshot ? snapshot.value : (snapshot.read() ?? undefined);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
118
134
|
export function isSource(arg: unknown): arg is SaveableSource {
|
|
119
135
|
return isObject<SaveableSource>(arg) && is(arg.saveable);
|
|
120
136
|
}
|
|
@@ -23,13 +23,13 @@ import { SideTabBar } from './tab-bars';
|
|
|
23
23
|
export const AdditionalViewsMenuWidgetFactory = Symbol('AdditionalViewsMenuWidgetFactory');
|
|
24
24
|
export type AdditionalViewsMenuWidgetFactory = (side: 'left' | 'right') => AdditionalViewsMenuWidget;
|
|
25
25
|
|
|
26
|
-
export const
|
|
27
|
-
|
|
26
|
+
export const AdditionalViewsMenuPath = Symbol('AdditionalViewsMenuPath');
|
|
28
27
|
@injectable()
|
|
29
28
|
export class AdditionalViewsMenuWidget extends SidebarMenuWidget {
|
|
30
29
|
static readonly ID = 'sidebar.additional.views';
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
@inject(AdditionalViewsMenuPath)
|
|
32
|
+
protected menuPath: MenuPath;
|
|
33
33
|
|
|
34
34
|
@inject(CommandRegistry)
|
|
35
35
|
protected readonly commandRegistry: CommandRegistry;
|
|
@@ -47,7 +47,7 @@ export class AdditionalViewsMenuWidget extends SidebarMenuWidget {
|
|
|
47
47
|
title: nls.localizeByDefault('Additional Views'),
|
|
48
48
|
iconClass: codicon('ellipsis'),
|
|
49
49
|
id: AdditionalViewsMenuWidget.ID,
|
|
50
|
-
menuPath:
|
|
50
|
+
menuPath: this.menuPath,
|
|
51
51
|
order: 0
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -66,6 +66,6 @@ export class AdditionalViewsMenuWidget extends SidebarMenuWidget {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
}));
|
|
69
|
-
this.menuDisposables.push(this.menuModelRegistry.registerMenuAction(
|
|
69
|
+
this.menuDisposables.push(this.menuModelRegistry.registerMenuAction(this.menuPath, { commandId: command.id, order: index.toString() }));
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -960,7 +960,7 @@ export class ApplicationShell extends Widget {
|
|
|
960
960
|
}
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
-
getInsertionOptions(options?: Readonly<ApplicationShell.WidgetOptions>): { area: string; addOptions:
|
|
963
|
+
getInsertionOptions(options?: Readonly<ApplicationShell.WidgetOptions>): { area: string; addOptions: TheiaDockPanel.AddOptions; } {
|
|
964
964
|
let ref: Widget | undefined = options?.ref;
|
|
965
965
|
let area: ApplicationShell.Area = options?.area || 'main';
|
|
966
966
|
if (!ref && (area === 'main' || area === 'bottom')) {
|
|
@@ -969,7 +969,7 @@ export class ApplicationShell extends Widget {
|
|
|
969
969
|
}
|
|
970
970
|
// make sure that ref belongs to area
|
|
971
971
|
area = ref && this.getAreaFor(ref) || area;
|
|
972
|
-
const addOptions:
|
|
972
|
+
const addOptions: TheiaDockPanel.AddOptions = {};
|
|
973
973
|
if (ApplicationShell.isOpenToSideMode(options?.mode)) {
|
|
974
974
|
const areaPanel = area === 'main' ? this.mainPanel : area === 'bottom' ? this.bottomPanel : undefined;
|
|
975
975
|
const sideRef = areaPanel && ref && (options?.mode === 'open-to-left' ?
|
|
@@ -981,6 +981,10 @@ export class ApplicationShell extends Widget {
|
|
|
981
981
|
addOptions.ref = ref;
|
|
982
982
|
addOptions.mode = options?.mode === 'open-to-left' ? 'split-left' : 'split-right';
|
|
983
983
|
}
|
|
984
|
+
} else if (ApplicationShell.isReplaceMode(options?.mode)) {
|
|
985
|
+
addOptions.ref = options?.ref;
|
|
986
|
+
addOptions.closeRef = true;
|
|
987
|
+
addOptions.mode = 'tab-after';
|
|
984
988
|
} else {
|
|
985
989
|
addOptions.ref = ref;
|
|
986
990
|
addOptions.mode = options?.mode;
|
|
@@ -2172,6 +2176,15 @@ export namespace ApplicationShell {
|
|
|
2172
2176
|
return mode === 'open-to-left' || mode === 'open-to-right';
|
|
2173
2177
|
}
|
|
2174
2178
|
|
|
2179
|
+
/**
|
|
2180
|
+
* Whether the `ref` of the options widget should be replaced.
|
|
2181
|
+
*/
|
|
2182
|
+
export type ReplaceMode = 'tab-replace';
|
|
2183
|
+
|
|
2184
|
+
export function isReplaceMode(mode: unknown): mode is ReplaceMode {
|
|
2185
|
+
return mode === 'tab-replace';
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2175
2188
|
/**
|
|
2176
2189
|
* Options for adding a widget to the application shell.
|
|
2177
2190
|
*/
|
|
@@ -2185,7 +2198,7 @@ export namespace ApplicationShell {
|
|
|
2185
2198
|
*
|
|
2186
2199
|
* The default is `'tab-after'`.
|
|
2187
2200
|
*/
|
|
2188
|
-
mode?: DockLayout.InsertMode | OpenToSideMode
|
|
2201
|
+
mode?: DockLayout.InsertMode | OpenToSideMode | ReplaceMode
|
|
2189
2202
|
/**
|
|
2190
2203
|
* The reference widget for the insert location.
|
|
2191
2204
|
*
|
|
@@ -211,6 +211,7 @@ export class SidePanelHandler {
|
|
|
211
211
|
protected createAdditionalViewsWidget(): AdditionalViewsMenuWidget {
|
|
212
212
|
const widget = this.additionalViewsMenuFactory(this.side);
|
|
213
213
|
widget.addClass('theia-sidebar-menu');
|
|
214
|
+
widget.addClass('theia-additional-views-menu');
|
|
214
215
|
return widget;
|
|
215
216
|
}
|
|
216
217
|
|
|
@@ -653,7 +654,7 @@ export class SidePanelHandler {
|
|
|
653
654
|
}
|
|
654
655
|
|
|
655
656
|
protected onTabsOverflowChanged(sender: SideTabBar, event: { titles: Title<Widget>[], startIndex: number }): void {
|
|
656
|
-
if (event.startIndex
|
|
657
|
+
if (event.startIndex > 0 && event.startIndex <= sender.currentIndex) {
|
|
657
658
|
sender.revealTab(sender.currentIndex);
|
|
658
659
|
} else {
|
|
659
660
|
this.additionalViewsMenu.updateAdditionalViews(sender, event);
|
|
@@ -20,6 +20,7 @@ import { ReactWidget } from '../widgets';
|
|
|
20
20
|
import { ContextMenuRenderer } from '../context-menu-renderer';
|
|
21
21
|
import { MenuPath } from '../../common/menu';
|
|
22
22
|
import { HoverService } from '../hover-service';
|
|
23
|
+
import { Event, Disposable, Emitter, DisposableCollection } from '../../common';
|
|
23
24
|
|
|
24
25
|
export const SidebarTopMenuWidgetFactory = Symbol('SidebarTopMenuWidgetFactory');
|
|
25
26
|
export const SidebarBottomMenuWidgetFactory = Symbol('SidebarBottomMenuWidgetFactory');
|
|
@@ -29,18 +30,54 @@ export interface SidebarMenu {
|
|
|
29
30
|
iconClass: string;
|
|
30
31
|
title: string;
|
|
31
32
|
menuPath: MenuPath;
|
|
33
|
+
onDidBadgeChange?: Event<number>;
|
|
32
34
|
/*
|
|
33
35
|
* Used to sort menus. The lower the value the lower they are placed in the sidebar.
|
|
34
36
|
*/
|
|
35
37
|
order: number;
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
export class SidebarMenuItem implements Disposable {
|
|
41
|
+
|
|
42
|
+
readonly menu: SidebarMenu;
|
|
43
|
+
get badge(): string {
|
|
44
|
+
if (this._badge <= 0) {
|
|
45
|
+
return '';
|
|
46
|
+
} else if (this._badge > 99) {
|
|
47
|
+
return '99+';
|
|
48
|
+
} else {
|
|
49
|
+
return this._badge.toString();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
protected readonly onDidBadgeChangeEmitter = new Emitter<number>();
|
|
53
|
+
readonly onDidBadgeChange: Event<number> = this.onDidBadgeChangeEmitter.event;
|
|
54
|
+
protected _badge = 0;
|
|
55
|
+
|
|
56
|
+
protected readonly toDispose = new DisposableCollection();
|
|
57
|
+
|
|
58
|
+
constructor(menu: SidebarMenu) {
|
|
59
|
+
this.menu = menu;
|
|
60
|
+
if (menu.onDidBadgeChange) {
|
|
61
|
+
this.toDispose.push(menu.onDidBadgeChange(value => {
|
|
62
|
+
this._badge = value;
|
|
63
|
+
this.onDidBadgeChangeEmitter.fire(value);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
dispose(): void {
|
|
69
|
+
this.toDispose.dispose();
|
|
70
|
+
this.onDidBadgeChangeEmitter.dispose();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
38
75
|
/**
|
|
39
76
|
* The menu widget placed on the sidebar.
|
|
40
77
|
*/
|
|
41
78
|
@injectable()
|
|
42
79
|
export class SidebarMenuWidget extends ReactWidget {
|
|
43
|
-
protected readonly
|
|
80
|
+
protected readonly items: SidebarMenuItem[];
|
|
44
81
|
/**
|
|
45
82
|
* The element that had focus when a menu rendered by this widget was activated.
|
|
46
83
|
*/
|
|
@@ -58,27 +95,27 @@ export class SidebarMenuWidget extends ReactWidget {
|
|
|
58
95
|
|
|
59
96
|
constructor() {
|
|
60
97
|
super();
|
|
61
|
-
this.
|
|
98
|
+
this.items = [];
|
|
62
99
|
}
|
|
63
100
|
|
|
64
101
|
addMenu(menu: SidebarMenu): void {
|
|
65
|
-
const exists = this.
|
|
102
|
+
const exists = this.items.find(item => item.menu.id === menu.id);
|
|
66
103
|
if (exists) {
|
|
67
104
|
return;
|
|
68
105
|
}
|
|
69
|
-
|
|
70
|
-
|
|
106
|
+
const newItem = new SidebarMenuItem(menu);
|
|
107
|
+
newItem.onDidBadgeChange(() => this.update());
|
|
108
|
+
this.items.push(newItem);
|
|
109
|
+
this.items.sort((a, b) => a.menu.order - b.menu.order);
|
|
71
110
|
this.update();
|
|
72
111
|
}
|
|
73
112
|
|
|
74
113
|
removeMenu(menuId: string): void {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.update();
|
|
81
|
-
}
|
|
114
|
+
const index = this.items.findIndex(m => m.menu.id === menuId);
|
|
115
|
+
if (index !== -1) {
|
|
116
|
+
this.items[index].dispose();
|
|
117
|
+
this.items.splice(index, 1);
|
|
118
|
+
this.update();
|
|
82
119
|
}
|
|
83
120
|
}
|
|
84
121
|
|
|
@@ -127,14 +164,20 @@ export class SidebarMenuWidget extends ReactWidget {
|
|
|
127
164
|
|
|
128
165
|
protected render(): React.ReactNode {
|
|
129
166
|
return <React.Fragment>
|
|
130
|
-
{this.
|
|
131
|
-
key={menu.id}
|
|
132
|
-
className={menu.iconClass}
|
|
133
|
-
onClick={e => this.onClick(e, menu.menuPath)}
|
|
134
|
-
onMouseDown={this.onMouseDown}
|
|
135
|
-
onMouseEnter={e => this.onMouseEnter(e, menu.title)}
|
|
136
|
-
onMouseLeave={this.onMouseOut}
|
|
137
|
-
/>)}
|
|
167
|
+
{this.items.map(item => this.renderItem(item))}
|
|
138
168
|
</React.Fragment>;
|
|
139
169
|
}
|
|
170
|
+
|
|
171
|
+
protected renderItem(item: SidebarMenuItem): React.ReactNode {
|
|
172
|
+
return <div
|
|
173
|
+
key={item.menu.id}
|
|
174
|
+
className='theia-sidebar-menu-item'
|
|
175
|
+
onClick={e => this.onClick(e, item.menu.menuPath)}
|
|
176
|
+
onMouseDown={this.onMouseDown}
|
|
177
|
+
onMouseEnter={e => this.onMouseEnter(e, item.menu.title)}
|
|
178
|
+
onMouseLeave={this.onMouseOut}>
|
|
179
|
+
<i className={item.menu.iconClass} />
|
|
180
|
+
{item.badge && <div className='theia-badge-decorator-sidebar'>{item.badge}</div>}
|
|
181
|
+
</div>;
|
|
182
|
+
}
|
|
140
183
|
}
|
|
@@ -1084,8 +1084,6 @@ export class SideTabBar extends ScrollableTabBar {
|
|
|
1084
1084
|
startIndex: number
|
|
1085
1085
|
};
|
|
1086
1086
|
|
|
1087
|
-
protected _rowGap: number;
|
|
1088
|
-
|
|
1089
1087
|
constructor(options?: TabBar.IOptions<Widget> & PerfectScrollbar.Options) {
|
|
1090
1088
|
super(options);
|
|
1091
1089
|
|
|
@@ -1142,31 +1140,6 @@ export class SideTabBar extends ScrollableTabBar {
|
|
|
1142
1140
|
}
|
|
1143
1141
|
}
|
|
1144
1142
|
|
|
1145
|
-
// Queries the tabRowGap value of the content node. Needed to properly compute overflowing
|
|
1146
|
-
// tabs that should be hidden
|
|
1147
|
-
protected get tabRowGap(): number {
|
|
1148
|
-
// We assume that the tab row gap is static i.e. we compute it once an then cache it
|
|
1149
|
-
if (!this._rowGap) {
|
|
1150
|
-
this._rowGap = this.computeTabRowGap();
|
|
1151
|
-
}
|
|
1152
|
-
return this._rowGap;
|
|
1153
|
-
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
protected computeTabRowGap(): number {
|
|
1157
|
-
const style = window.getComputedStyle(this.contentNode);
|
|
1158
|
-
const rowGapStyle = style.getPropertyValue('row-gap');
|
|
1159
|
-
const numericValue = parseFloat(rowGapStyle);
|
|
1160
|
-
const unit = rowGapStyle.match(/[a-zA-Z]+/)?.[0];
|
|
1161
|
-
|
|
1162
|
-
const tempDiv = document.createElement('div');
|
|
1163
|
-
tempDiv.style.height = '1' + unit;
|
|
1164
|
-
document.body.appendChild(tempDiv);
|
|
1165
|
-
const rowGapValue = numericValue * tempDiv.offsetHeight;
|
|
1166
|
-
document.body.removeChild(tempDiv);
|
|
1167
|
-
return rowGapValue;
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
1143
|
/**
|
|
1171
1144
|
* Reveal the tab with the given index by moving it into the non-overflowing tabBar section
|
|
1172
1145
|
* if necessary.
|
|
@@ -1207,18 +1180,13 @@ export class SideTabBar extends ScrollableTabBar {
|
|
|
1207
1180
|
const hiddenContent = this.hiddenContentNode;
|
|
1208
1181
|
const n = hiddenContent.children.length;
|
|
1209
1182
|
const renderData = new Array<Partial<SideBarRenderData>>(n);
|
|
1210
|
-
const availableWidth = this.node.clientHeight - this.tabRowGap;
|
|
1211
|
-
let actualWidth = 0;
|
|
1212
|
-
let overflowStartIndex = -1;
|
|
1213
1183
|
for (let i = 0; i < n; i++) {
|
|
1214
1184
|
const hiddenTab = hiddenContent.children[i];
|
|
1215
|
-
// Extract tab padding from the computed style
|
|
1185
|
+
// Extract tab padding, and margin from the computed style
|
|
1216
1186
|
const tabStyle = window.getComputedStyle(hiddenTab);
|
|
1217
|
-
const paddingTop = parseFloat(tabStyle.paddingTop!);
|
|
1218
|
-
const paddingBottom = parseFloat(tabStyle.paddingBottom!);
|
|
1219
1187
|
const rd: Partial<SideBarRenderData> = {
|
|
1220
|
-
paddingTop,
|
|
1221
|
-
paddingBottom
|
|
1188
|
+
paddingTop: parseFloat(tabStyle.paddingTop!),
|
|
1189
|
+
paddingBottom: parseFloat(tabStyle.paddingBottom!)
|
|
1222
1190
|
};
|
|
1223
1191
|
// Extract label size from the DOM
|
|
1224
1192
|
const labelElements = hiddenTab.getElementsByClassName('p-TabBar-tabLabel');
|
|
@@ -1231,38 +1199,21 @@ export class SideTabBar extends ScrollableTabBar {
|
|
|
1231
1199
|
if (iconElements.length === 1) {
|
|
1232
1200
|
const icon = iconElements[0];
|
|
1233
1201
|
rd.iconSize = { width: icon.clientWidth, height: icon.clientHeight };
|
|
1234
|
-
actualWidth += icon.clientHeight + paddingTop + paddingBottom + this.tabRowGap;
|
|
1235
|
-
|
|
1236
|
-
if (actualWidth > availableWidth && i !== 0) {
|
|
1237
|
-
rd.visible = false;
|
|
1238
|
-
if (overflowStartIndex === -1) {
|
|
1239
|
-
overflowStartIndex = i;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
renderData[i] = rd;
|
|
1243
1202
|
}
|
|
1244
|
-
}
|
|
1245
1203
|
|
|
1246
|
-
|
|
1247
|
-
if (overflowStartIndex === n - 1 && renderData[overflowStartIndex]) {
|
|
1248
|
-
if (!this.tabsOverflowData) {
|
|
1249
|
-
overflowStartIndex--;
|
|
1250
|
-
renderData[overflowStartIndex].visible = false;
|
|
1251
|
-
} else {
|
|
1252
|
-
renderData[overflowStartIndex].visible = true;
|
|
1253
|
-
overflowStartIndex = -1;
|
|
1254
|
-
}
|
|
1204
|
+
renderData[i] = rd;
|
|
1255
1205
|
}
|
|
1256
1206
|
// Render into the visible node
|
|
1257
1207
|
this.renderTabs(this.contentNode, renderData);
|
|
1258
|
-
this.computeOverflowingTabsData(
|
|
1208
|
+
this.computeOverflowingTabsData();
|
|
1259
1209
|
});
|
|
1260
1210
|
}
|
|
1261
1211
|
}
|
|
1262
1212
|
|
|
1263
|
-
protected computeOverflowingTabsData(
|
|
1213
|
+
protected computeOverflowingTabsData(): void {
|
|
1264
1214
|
// ensure that render tabs has completed
|
|
1265
1215
|
window.requestAnimationFrame(() => {
|
|
1216
|
+
const startIndex = this.hideOverflowingTabs();
|
|
1266
1217
|
if (startIndex === -1) {
|
|
1267
1218
|
if (this.tabsOverflowData) {
|
|
1268
1219
|
this.tabsOverflowData = undefined;
|
|
@@ -1286,6 +1237,38 @@ export class SideTabBar extends ScrollableTabBar {
|
|
|
1286
1237
|
});
|
|
1287
1238
|
}
|
|
1288
1239
|
|
|
1240
|
+
/**
|
|
1241
|
+
* Hide overflowing tabs and return the index of the first hidden tab.
|
|
1242
|
+
*/
|
|
1243
|
+
protected hideOverflowingTabs(): number {
|
|
1244
|
+
const availableHeight = this.node.clientHeight;
|
|
1245
|
+
const invisibleClass = 'p-mod-invisible';
|
|
1246
|
+
let startIndex = -1;
|
|
1247
|
+
const n = this.contentNode.children.length;
|
|
1248
|
+
for (let i = 0; i < n; i++) {
|
|
1249
|
+
const tab = this.contentNode.children[i] as HTMLLIElement;
|
|
1250
|
+
if (tab.offsetTop + tab.offsetHeight >= availableHeight) {
|
|
1251
|
+
tab.classList.add(invisibleClass);
|
|
1252
|
+
if (startIndex === -1) {
|
|
1253
|
+
startIndex = i;
|
|
1254
|
+
/* If only one element is overflowing and the additional menu widget is visible (i.e. this.tabsOverflowData is set)
|
|
1255
|
+
* there might already be enough space to show the last tab. In this case, we need to include the size of the
|
|
1256
|
+
* additional menu widget and recheck if the last tab is visible */
|
|
1257
|
+
if (startIndex === n - 1 && this.tabsOverflowData) {
|
|
1258
|
+
const additionalViewsMenu = this.node.parentElement?.querySelector('.theia-additional-views-menu') as HTMLDivElement;
|
|
1259
|
+
if (tab.offsetTop + tab.offsetHeight < availableHeight + additionalViewsMenu.offsetHeight) {
|
|
1260
|
+
tab.classList.remove(invisibleClass);
|
|
1261
|
+
startIndex = -1;
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
} else {
|
|
1266
|
+
tab.classList.remove(invisibleClass);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return startIndex;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1289
1272
|
/**
|
|
1290
1273
|
* Render the tab bar using the given DOM element as host. The optional `renderData` is forwarded
|
|
1291
1274
|
* to the TabBarRenderer.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { find, toArray
|
|
17
|
+
import { find, toArray } from '@phosphor/algorithm';
|
|
18
18
|
import { TabBar, Widget, DockPanel, Title, DockLayout } from '@phosphor/widgets';
|
|
19
19
|
import { Signal } from '@phosphor/signaling';
|
|
20
20
|
import { Disposable, DisposableCollection } from '../../common/disposable';
|
|
@@ -103,7 +103,7 @@ export class TheiaDockPanel extends DockPanel {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
findTabBar(title: Title<Widget>): TabBar<Widget> | undefined {
|
|
106
|
-
return find(this.tabBars(), bar =>
|
|
106
|
+
return find(this.tabBars(), bar => bar.titles.includes(title));
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
protected readonly toDisposeOnMarkAsCurrent = new DisposableCollection();
|
|
@@ -133,11 +133,14 @@ export class TheiaDockPanel extends DockPanel {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
override addWidget(widget: Widget, options?:
|
|
136
|
+
override addWidget(widget: Widget, options?: TheiaDockPanel.AddOptions): void {
|
|
137
137
|
if (this.mode === 'single-document' && widget.parent === this) {
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
super.addWidget(widget, options);
|
|
141
|
+
if (options?.closeRef) {
|
|
142
|
+
options.ref?.close();
|
|
143
|
+
}
|
|
141
144
|
this.widgetAdded.emit(widget);
|
|
142
145
|
this.markActiveTabBar(widget.title);
|
|
143
146
|
}
|
|
@@ -252,4 +255,11 @@ export namespace TheiaDockPanel {
|
|
|
252
255
|
export interface Factory {
|
|
253
256
|
(options?: DockPanel.IOptions): TheiaDockPanel;
|
|
254
257
|
}
|
|
258
|
+
|
|
259
|
+
export interface AddOptions extends DockPanel.IAddOptions {
|
|
260
|
+
/**
|
|
261
|
+
* Whether to also close the widget referenced by `ref`.
|
|
262
|
+
*/
|
|
263
|
+
closeRef?: boolean
|
|
264
|
+
}
|
|
255
265
|
}
|
|
@@ -186,23 +186,26 @@
|
|
|
186
186
|
flex-direction: column-reverse;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
.p-Widget .theia-sidebar-menu-item {
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
}
|
|
192
|
+
|
|
189
193
|
.p-Widget.theia-sidebar-menu i {
|
|
190
194
|
padding: var(--theia-private-sidebar-tab-padding-top-and-bottom)
|
|
191
195
|
var(--theia-private-sidebar-tab-padding-left-and-right);
|
|
192
196
|
display: flex;
|
|
193
197
|
justify-content: center;
|
|
194
198
|
align-items: center;
|
|
195
|
-
cursor: pointer;
|
|
196
199
|
color: var(--theia-activityBar-inactiveForeground);
|
|
197
200
|
background-color: var(--theia-activityBar-background);
|
|
198
201
|
font-size: var(--theia-private-sidebar-icon-size);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
|
-
.theia-sidebar-menu
|
|
204
|
+
.theia-sidebar-menu .theia-sidebar-menu-item:hover i {
|
|
202
205
|
color: var(--theia-activityBar-foreground);
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
.theia-sidebar-menu
|
|
208
|
+
.theia-sidebar-menu i.theia-compact-menu {
|
|
206
209
|
font-size: 16px;
|
|
207
210
|
}
|
|
208
211
|
|
|
@@ -305,7 +305,7 @@
|
|
|
305
305
|
display: none !important;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
.
|
|
308
|
+
.theia-badge-decorator-sidebar {
|
|
309
309
|
background-color: var(--theia-activityBarBadge-background);
|
|
310
310
|
border-radius: 20px;
|
|
311
311
|
color: var(--theia-activityBarBadge-foreground);
|
|
@@ -41,7 +41,10 @@ export class ReactRenderer implements Disposable {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
render(): void {
|
|
44
|
-
|
|
44
|
+
// Ignore all render calls after the host element has unmounted
|
|
45
|
+
if (!this.toDispose.disposed) {
|
|
46
|
+
this.hostRoot.render(<React.Fragment>{this.doRender()}</React.Fragment>);
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
protected doRender(): React.ReactNode {
|
|
@@ -56,6 +56,7 @@ export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind,
|
|
|
56
56
|
getExtensionsInfos: async (): Promise<ExtensionInfo[]> => [],
|
|
57
57
|
getApplicationInfo: async (): Promise<ApplicationInfo | undefined> => undefined,
|
|
58
58
|
getApplicationRoot: async (): Promise<string> => '',
|
|
59
|
+
getApplicationPlatform: () => Promise.resolve('web'),
|
|
59
60
|
getBackendOS: async (): Promise<OS.Type> => OS.Type.Linux
|
|
60
61
|
};
|
|
61
62
|
if (isBound(ApplicationServer)) {
|
|
@@ -24,6 +24,7 @@ export interface ApplicationServer {
|
|
|
24
24
|
getExtensionsInfos(): Promise<ExtensionInfo[]>;
|
|
25
25
|
getApplicationInfo(): Promise<ApplicationInfo | undefined>;
|
|
26
26
|
getApplicationRoot(): Promise<string>;
|
|
27
|
+
getApplicationPlatform(): Promise<string>;
|
|
27
28
|
/**
|
|
28
29
|
* @deprecated since 1.25.0. Use `OS.backend.type()` instead.
|
|
29
30
|
*/
|
|
@@ -18,6 +18,7 @@ import { inject, injectable, named } from 'inversify';
|
|
|
18
18
|
import { Command, CommandRegistry } from '../command';
|
|
19
19
|
import { ContributionProvider } from '../contribution-provider';
|
|
20
20
|
import { Disposable } from '../disposable';
|
|
21
|
+
import { Emitter, Event } from '../event';
|
|
21
22
|
import { ActionMenuNode } from './action-menu-node';
|
|
22
23
|
import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node';
|
|
23
24
|
import { CompoundMenuNode, MenuAction, MenuNode, MenuNodeMetadata, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types';
|
|
@@ -68,6 +69,14 @@ export class MenuModelRegistry {
|
|
|
68
69
|
protected readonly root = new CompositeMenuNode('');
|
|
69
70
|
protected readonly independentSubmenus = new Map<string, MutableCompoundMenuNode>();
|
|
70
71
|
|
|
72
|
+
protected readonly onDidChangeEmitter = new Emitter<void>();
|
|
73
|
+
|
|
74
|
+
get onDidChange(): Event<void> {
|
|
75
|
+
return this.onDidChangeEmitter.event;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected isReady = false;
|
|
79
|
+
|
|
71
80
|
constructor(
|
|
72
81
|
@inject(ContributionProvider) @named(MenuContribution)
|
|
73
82
|
protected readonly contributions: ContributionProvider<MenuContribution>,
|
|
@@ -78,6 +87,7 @@ export class MenuModelRegistry {
|
|
|
78
87
|
for (const contrib of this.contributions.getContributions()) {
|
|
79
88
|
contrib.registerMenus(this);
|
|
80
89
|
}
|
|
90
|
+
this.isReady = true;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
/**
|
|
@@ -97,7 +107,9 @@ export class MenuModelRegistry {
|
|
|
97
107
|
*/
|
|
98
108
|
registerMenuNode(menuPath: MenuPath | string, menuNode: MenuNode, group?: string): Disposable {
|
|
99
109
|
const parent = this.getMenuNode(menuPath, group);
|
|
100
|
-
|
|
110
|
+
const disposable = parent.addNode(menuNode);
|
|
111
|
+
this.fireChangeEvent();
|
|
112
|
+
return this.changeEventOnDispose(disposable);
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
getMenuNode(menuPath: MenuPath | string, group?: string): MutableCompoundMenuNode {
|
|
@@ -137,13 +149,15 @@ export class MenuModelRegistry {
|
|
|
137
149
|
const groupPath = index === 0 ? [] : menuPath.slice(0, index);
|
|
138
150
|
const parent = this.findGroup(groupPath, options);
|
|
139
151
|
let groupNode = this.findSubMenu(parent, menuId, options);
|
|
152
|
+
let disposable = Disposable.NULL;
|
|
140
153
|
if (!groupNode) {
|
|
141
154
|
groupNode = new CompositeMenuNode(menuId, label, options, parent);
|
|
142
|
-
|
|
155
|
+
disposable = this.changeEventOnDispose(parent.addNode(groupNode));
|
|
143
156
|
} else {
|
|
144
157
|
groupNode.updateOptions({ ...options, label });
|
|
145
|
-
return Disposable.NULL;
|
|
146
158
|
}
|
|
159
|
+
this.fireChangeEvent();
|
|
160
|
+
return disposable;
|
|
147
161
|
}
|
|
148
162
|
|
|
149
163
|
registerIndependentSubmenu(id: string, label: string, options?: SubMenuOptions): Disposable {
|
|
@@ -151,7 +165,7 @@ export class MenuModelRegistry {
|
|
|
151
165
|
console.debug(`Independent submenu with path ${id} registered, but given ID already exists.`);
|
|
152
166
|
}
|
|
153
167
|
this.independentSubmenus.set(id, new CompositeMenuNode(id, label, options));
|
|
154
|
-
return
|
|
168
|
+
return this.changeEventOnDispose(Disposable.create(() => this.independentSubmenus.delete(id)));
|
|
155
169
|
}
|
|
156
170
|
|
|
157
171
|
linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable {
|
|
@@ -175,7 +189,9 @@ export class MenuModelRegistry {
|
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
const wrapper = new CompositeMenuNodeWrapper(child, parent, options);
|
|
178
|
-
|
|
192
|
+
const disposable = parent.addNode(wrapper);
|
|
193
|
+
this.fireChangeEvent();
|
|
194
|
+
return this.changeEventOnDispose(disposable);
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
/**
|
|
@@ -207,6 +223,7 @@ export class MenuModelRegistry {
|
|
|
207
223
|
if (menuPath) {
|
|
208
224
|
const parent = this.findGroup(menuPath);
|
|
209
225
|
parent.removeNode(id);
|
|
226
|
+
this.fireChangeEvent();
|
|
210
227
|
return;
|
|
211
228
|
}
|
|
212
229
|
|
|
@@ -228,6 +245,7 @@ export class MenuModelRegistry {
|
|
|
228
245
|
});
|
|
229
246
|
};
|
|
230
247
|
recurse(this.root);
|
|
248
|
+
this.fireChangeEvent();
|
|
231
249
|
}
|
|
232
250
|
|
|
233
251
|
/**
|
|
@@ -321,6 +339,19 @@ export class MenuModelRegistry {
|
|
|
321
339
|
return true;
|
|
322
340
|
}
|
|
323
341
|
|
|
342
|
+
protected changeEventOnDispose(disposable: Disposable): Disposable {
|
|
343
|
+
return Disposable.create(() => {
|
|
344
|
+
disposable.dispose();
|
|
345
|
+
this.fireChangeEvent();
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
protected fireChangeEvent(): void {
|
|
350
|
+
if (this.isReady) {
|
|
351
|
+
this.onDidChangeEmitter.fire();
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
324
355
|
/**
|
|
325
356
|
* Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined.
|
|
326
357
|
* Returns `undefined` if the `parent` of any node in the chain is unknown.
|