@theia/core 1.38.0 → 1.39.0-next.11
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 +7 -7
- package/lib/browser/about-dialog.d.ts +2 -1
- package/lib/browser/about-dialog.d.ts.map +1 -1
- package/lib/browser/about-dialog.js +5 -2
- package/lib/browser/about-dialog.js.map +1 -1
- package/lib/browser/dialogs.d.ts +15 -9
- package/lib/browser/dialogs.d.ts.map +1 -1
- package/lib/browser/dialogs.js +67 -54
- package/lib/browser/dialogs.js.map +1 -1
- package/lib/browser/frontend-application-bindings.js +1 -1
- package/lib/browser/frontend-application-bindings.js.map +1 -1
- package/lib/browser/frontend-application-module.js +1 -1
- package/lib/browser/frontend-application-module.js.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts +2 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts.map +1 -1
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js +6 -3
- package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
- package/lib/browser/preferences/injectable-preference-proxy.d.ts +1 -1
- package/lib/browser/preferences/injectable-preference-proxy.d.ts.map +1 -1
- package/lib/browser/preferences/injectable-preference-proxy.js +5 -4
- package/lib/browser/preferences/injectable-preference-proxy.js.map +1 -1
- package/lib/browser/saveable.d.ts.map +1 -1
- package/lib/browser/saveable.js +4 -2
- package/lib/browser/saveable.js.map +1 -1
- package/lib/browser/secondary-window-handler.d.ts +2 -2
- package/lib/browser/secondary-window-handler.d.ts.map +1 -1
- package/lib/browser/secondary-window-handler.js +8 -34
- package/lib/browser/secondary-window-handler.js.map +1 -1
- package/lib/browser/styling-service.d.ts +5 -2
- package/lib/browser/styling-service.d.ts.map +1 -1
- package/lib/browser/styling-service.js +15 -5
- package/lib/browser/styling-service.js.map +1 -1
- package/lib/browser/widgets/widget.d.ts +1 -0
- package/lib/browser/widgets/widget.d.ts.map +1 -1
- package/lib/browser/widgets/widget.js +7 -3
- package/lib/browser/widgets/widget.js.map +1 -1
- package/lib/browser/window/default-secondary-window-service.d.ts +5 -3
- package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-secondary-window-service.js +62 -18
- package/lib/browser/window/default-secondary-window-service.js.map +1 -1
- package/lib/browser/window/secondary-window-service.d.ts +3 -1
- package/lib/browser/window/secondary-window-service.d.ts.map +1 -1
- package/lib/browser/window/secondary-window-service.js.map +1 -1
- package/lib/common/logger.d.ts +3 -6
- package/lib/common/logger.d.ts.map +1 -1
- package/lib/common/logger.js +29 -22
- package/lib/common/logger.js.map +1 -1
- package/lib/common/message-rpc/rpc-message-encoder.d.ts +3 -3
- package/lib/common/message-rpc/rpc-message-encoder.d.ts.map +1 -1
- package/lib/common/message-rpc/rpc-message-encoder.js +2 -2
- package/lib/common/message-rpc/rpc-message-encoder.js.map +1 -1
- package/lib/common/message-rpc/rpc-protocol.d.ts +1 -1
- package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -1
- package/lib/common/message-rpc/rpc-protocol.js +8 -4
- package/lib/common/message-rpc/rpc-protocol.js.map +1 -1
- package/lib/common/messaging/proxy-factory.d.ts +6 -6
- package/lib/common/messaging/proxy-factory.d.ts.map +1 -1
- package/lib/common/messaging/proxy-factory.js +15 -10
- package/lib/common/messaging/proxy-factory.js.map +1 -1
- package/lib/common/performance/stopwatch.d.ts +1 -1
- package/lib/common/performance/stopwatch.d.ts.map +1 -1
- package/lib/common/performance/stopwatch.js.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.d.ts +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.d.ts.map +1 -1
- package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.js +2 -2
- package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.js.map +1 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts +2 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.js +5 -2
- package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
- package/lib/electron-browser/preload.d.ts.map +1 -1
- package/lib/electron-browser/preload.js +21 -16
- package/lib/electron-browser/preload.js.map +1 -1
- package/lib/electron-browser/window/electron-secondary-window-service.d.ts +3 -1
- package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-secondary-window-service.js +7 -2
- package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +3 -1
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js +2 -1
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-main/electron-api-main.d.ts +1 -0
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +19 -1
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/lib/electron-main/theia-electron-window.d.ts +1 -0
- package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
- package/lib/electron-main/theia-electron-window.js +32 -0
- package/lib/electron-main/theia-electron-window.js.map +1 -1
- package/lib/electron-node/token/electron-token-validator.d.ts +1 -1
- package/lib/electron-node/token/electron-token-validator.d.ts.map +1 -1
- package/lib/electron-node/token/electron-token-validator.js +2 -2
- package/lib/electron-node/token/electron-token-validator.js.map +1 -1
- package/lib/node/backend-application.d.ts +1 -0
- package/lib/node/backend-application.d.ts.map +1 -1
- package/lib/node/backend-application.js +5 -2
- package/lib/node/backend-application.js.map +1 -1
- package/lib/node/hosting/backend-application-hosts.d.ts +1 -1
- package/lib/node/hosting/backend-application-hosts.d.ts.map +1 -1
- package/lib/node/hosting/backend-application-hosts.js +2 -2
- package/lib/node/hosting/backend-application-hosts.js.map +1 -1
- package/package.json +5 -5
- package/src/browser/about-dialog.tsx +5 -1
- package/src/browser/dialogs.ts +69 -52
- package/src/browser/frontend-application-bindings.ts +1 -1
- package/src/browser/frontend-application-module.ts +2 -2
- package/src/browser/keyboard/browser-keyboard-layout-provider.ts +5 -1
- package/src/browser/preferences/injectable-preference-proxy.ts +5 -4
- package/src/browser/saveable.ts +4 -2
- package/src/browser/secondary-window-handler.ts +7 -38
- package/src/browser/styling-service.ts +17 -6
- package/src/browser/widgets/widget.ts +4 -0
- package/src/browser/window/default-secondary-window-service.ts +67 -18
- package/src/browser/window/secondary-window-service.ts +4 -1
- package/src/common/logger.ts +12 -14
- package/src/common/message-rpc/rpc-message-encoder.ts +4 -4
- package/src/common/message-rpc/rpc-protocol.ts +8 -4
- package/src/common/messaging/proxy-factory.ts +17 -15
- package/src/common/performance/stopwatch.ts +1 -1
- package/src/electron-browser/keyboard/electron-keyboard-layout-change-notifier.ts +1 -1
- package/src/electron-browser/menu/electron-context-menu-renderer.ts +5 -1
- package/src/electron-browser/preload.ts +22 -2
- package/src/electron-browser/window/electron-secondary-window-service.ts +8 -2
- package/src/electron-common/electron-api.ts +5 -1
- package/src/electron-main/electron-api-main.ts +22 -2
- package/src/electron-main/theia-electron-window.ts +30 -0
- package/src/electron-node/token/electron-token-validator.ts +1 -1
- package/src/node/backend-application.ts +4 -0
- package/src/node/hosting/backend-application-hosts.ts +1 -1
|
@@ -121,7 +121,7 @@ import {
|
|
|
121
121
|
BreadcrumbsService,
|
|
122
122
|
DefaultBreadcrumbRenderer,
|
|
123
123
|
} from './breadcrumbs';
|
|
124
|
-
import { RendererHost } from './widgets';
|
|
124
|
+
import { DockPanel, RendererHost } from './widgets';
|
|
125
125
|
import { TooltipService, TooltipServiceImpl } from './tooltip-service';
|
|
126
126
|
import { BackendRequestService, RequestService, REQUEST_SERVICE_PATH } from '@theia/request';
|
|
127
127
|
import { bindFrontendStopwatch, bindBackendStopwatch } from './performance';
|
|
@@ -190,7 +190,7 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
|
|
|
190
190
|
const hoverService = container.get(HoverService);
|
|
191
191
|
return new TabBarRenderer(contextMenuRenderer, tabBarDecoratorService, iconThemeService, selectionService, commandService, corePreferences, hoverService);
|
|
192
192
|
});
|
|
193
|
-
bind(TheiaDockPanel.Factory).toFactory(({ container }) => options => {
|
|
193
|
+
bind(TheiaDockPanel.Factory).toFactory(({ container }) => (options?: DockPanel.IOptions) => {
|
|
194
194
|
const corePreferences = container.get<CorePreferences>(CorePreferences);
|
|
195
195
|
return new TheiaDockPanel(options, corePreferences);
|
|
196
196
|
});
|
|
@@ -59,7 +59,11 @@ export class BrowserKeyboardLayoutProvider implements KeyboardLayoutProvider, Ke
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
@postConstruct()
|
|
62
|
-
protected
|
|
62
|
+
protected init(): void {
|
|
63
|
+
this.doInit();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected async doInit(): Promise<void> {
|
|
63
67
|
await this.loadState();
|
|
64
68
|
const keyboard = (navigator as NavigatorExtension).keyboard;
|
|
65
69
|
if (keyboard && keyboard.addEventListener) {
|
|
@@ -48,7 +48,7 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
|
|
|
48
48
|
|
|
49
49
|
@inject(PreferenceProxyOptions) protected readonly options: PreferenceProxyOptions;
|
|
50
50
|
@inject(PreferenceService) protected readonly preferences: PreferenceService;
|
|
51
|
-
@inject(PreferenceProxySchema) protected readonly promisedSchema: PreferenceSchema | Promise<PreferenceSchema>;
|
|
51
|
+
@inject(PreferenceProxySchema) protected readonly promisedSchema: () => PreferenceSchema | Promise<PreferenceSchema>;
|
|
52
52
|
@inject(PreferenceProxyFactory) protected readonly factory: PreferenceProxyFactory;
|
|
53
53
|
protected toDispose = new DisposableCollection();
|
|
54
54
|
protected _onPreferenceChangedEmitter: Emitter<PreferenceChangeEvent<T>> | undefined;
|
|
@@ -95,10 +95,11 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
|
|
|
95
95
|
|
|
96
96
|
@postConstruct()
|
|
97
97
|
protected init(): void {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
const schema = this.promisedSchema();
|
|
99
|
+
if (schema instanceof Promise) {
|
|
100
|
+
schema.then(resolvedSchema => this.schema = resolvedSchema);
|
|
100
101
|
} else {
|
|
101
|
-
this.schema =
|
|
102
|
+
this.schema = schema;
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
package/src/browser/saveable.ts
CHANGED
|
@@ -287,14 +287,16 @@ export class ShouldSaveDialog extends AbstractDialog<boolean> {
|
|
|
287
287
|
constructor(widget: Widget) {
|
|
288
288
|
super({
|
|
289
289
|
title: nls.localizeByDefault('Do you want to save the changes you made to {0}?', widget.title.label || widget.title.caption)
|
|
290
|
+
}, {
|
|
291
|
+
node: widget.node.ownerDocument.createElement('div')
|
|
290
292
|
});
|
|
291
293
|
|
|
292
|
-
const messageNode =
|
|
294
|
+
const messageNode = this.node.ownerDocument.createElement('div');
|
|
293
295
|
messageNode.textContent = nls.localizeByDefault("Your changes will be lost if you don't save them.");
|
|
294
296
|
messageNode.setAttribute('style', 'flex: 1 100%; padding-bottom: calc(var(--theia-ui-padding)*3);');
|
|
295
297
|
this.contentNode.appendChild(messageNode);
|
|
296
|
-
this.dontSaveButton = this.appendDontSaveButton();
|
|
297
298
|
this.appendCloseButton();
|
|
299
|
+
this.dontSaveButton = this.appendDontSaveButton();
|
|
298
300
|
this.appendAcceptButton(nls.localizeByDefault('Save'));
|
|
299
301
|
}
|
|
300
302
|
|
|
@@ -23,6 +23,7 @@ import { Emitter } from '../common/event';
|
|
|
23
23
|
import { SecondaryWindowService } from './window/secondary-window-service';
|
|
24
24
|
import { KeybindingRegistry } from './keybinding';
|
|
25
25
|
import { ColorApplicationContribution } from './color-application-contribution';
|
|
26
|
+
import { StylingService } from './styling-service';
|
|
26
27
|
|
|
27
28
|
/** Widget to be contained directly in a secondary window. */
|
|
28
29
|
class SecondaryWindowRootWidget extends Widget {
|
|
@@ -50,8 +51,6 @@ class SecondaryWindowRootWidget extends Widget {
|
|
|
50
51
|
*/
|
|
51
52
|
@injectable()
|
|
52
53
|
export class SecondaryWindowHandler {
|
|
53
|
-
/** List of currently open secondary windows. Window references should be removed once the window is closed. */
|
|
54
|
-
protected readonly secondaryWindows: Window[] = [];
|
|
55
54
|
/** List of widgets in secondary windows. */
|
|
56
55
|
protected readonly _widgets: ExtractableWidget[] = [];
|
|
57
56
|
|
|
@@ -63,6 +62,9 @@ export class SecondaryWindowHandler {
|
|
|
63
62
|
@inject(ColorApplicationContribution)
|
|
64
63
|
protected colorAppContribution: ColorApplicationContribution;
|
|
65
64
|
|
|
65
|
+
@inject(StylingService)
|
|
66
|
+
protected stylingService: StylingService;
|
|
67
|
+
|
|
66
68
|
protected readonly onDidAddWidgetEmitter = new Emitter<Widget>();
|
|
67
69
|
/** Subscribe to get notified when a widget is added to this handler, i.e. the widget was moved to an secondary window . */
|
|
68
70
|
readonly onDidAddWidget = this.onDidAddWidgetEmitter.event;
|
|
@@ -95,33 +97,6 @@ export class SecondaryWindowHandler {
|
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
this.applicationShell = shell;
|
|
98
|
-
|
|
99
|
-
// Set up messaging with secondary windows
|
|
100
|
-
window.addEventListener('message', (event: MessageEvent) => {
|
|
101
|
-
console.trace('Message on main window', event);
|
|
102
|
-
if (event.data.fromSecondary) {
|
|
103
|
-
console.trace('Message comes from secondary window');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (event.data.fromMain) {
|
|
107
|
-
console.trace('Message has mainWindow marker, therefore ignore it');
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Filter setImmediate messages. Do not forward because these come in with very high frequency.
|
|
112
|
-
// They are not needed in secondary windows because these messages are just a work around
|
|
113
|
-
// to make setImmediate work in the main window: https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
|
|
114
|
-
if (typeof event.data === 'string' && event.data.startsWith('setImmediate')) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
console.trace('Delegate main window message to secondary windows', event);
|
|
119
|
-
this.secondaryWindows.forEach(secondaryWindow => {
|
|
120
|
-
if (!secondaryWindow.window.closed) {
|
|
121
|
-
secondaryWindow.window.postMessage({ ...event.data, fromMain: true }, '*');
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
100
|
}
|
|
126
101
|
|
|
127
102
|
/**
|
|
@@ -139,21 +114,13 @@ export class SecondaryWindowHandler {
|
|
|
139
114
|
return;
|
|
140
115
|
}
|
|
141
116
|
|
|
142
|
-
const newWindow = this.secondaryWindowService.createSecondaryWindow(
|
|
143
|
-
this.applicationShell.closeWidget(widget.id);
|
|
144
|
-
const extIndex = this.secondaryWindows.indexOf(closed);
|
|
145
|
-
if (extIndex > -1) {
|
|
146
|
-
this.secondaryWindows.splice(extIndex, 1);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
117
|
+
const newWindow = this.secondaryWindowService.createSecondaryWindow(widget, this.applicationShell);
|
|
149
118
|
|
|
150
119
|
if (!newWindow) {
|
|
151
120
|
this.messageService.error('The widget could not be moved to a secondary window because the window creation failed. Please make sure to allow popups.');
|
|
152
121
|
return;
|
|
153
122
|
}
|
|
154
123
|
|
|
155
|
-
this.secondaryWindows.push(newWindow);
|
|
156
|
-
|
|
157
124
|
const mainWindowTitle = document.title;
|
|
158
125
|
newWindow.onload = () => {
|
|
159
126
|
this.keybindings.registerEventListeners(newWindow);
|
|
@@ -168,6 +135,7 @@ export class SecondaryWindowHandler {
|
|
|
168
135
|
return;
|
|
169
136
|
}
|
|
170
137
|
const unregisterWithColorContribution = this.colorAppContribution.registerWindow(newWindow);
|
|
138
|
+
const unregisterWithStylingService = this.stylingService.registerWindow(newWindow);
|
|
171
139
|
|
|
172
140
|
widget.secondaryWindow = newWindow;
|
|
173
141
|
const rootWidget = new SecondaryWindowRootWidget();
|
|
@@ -182,6 +150,7 @@ export class SecondaryWindowHandler {
|
|
|
182
150
|
// Close the window if the widget is disposed, e.g. by a command closing all widgets.
|
|
183
151
|
widget.disposed.connect(() => {
|
|
184
152
|
unregisterWithColorContribution.dispose();
|
|
153
|
+
unregisterWithStylingService.dispose();
|
|
185
154
|
this.removeWidget(widget);
|
|
186
155
|
if (!newWindow.closed) {
|
|
187
156
|
newWindow.close();
|
|
@@ -21,6 +21,7 @@ import { ColorRegistry } from './color-registry';
|
|
|
21
21
|
import { DecorationStyle } from './decoration-style';
|
|
22
22
|
import { FrontendApplicationContribution } from './frontend-application';
|
|
23
23
|
import { ThemeService } from './theming';
|
|
24
|
+
import { Disposable } from '../common';
|
|
24
25
|
|
|
25
26
|
export const StylingParticipant = Symbol('StylingParticipant');
|
|
26
27
|
|
|
@@ -40,8 +41,7 @@ export interface CssStyleCollector {
|
|
|
40
41
|
|
|
41
42
|
@injectable()
|
|
42
43
|
export class StylingService implements FrontendApplicationContribution {
|
|
43
|
-
|
|
44
|
-
protected cssElement = DecorationStyle.createStyleElement('contributedColorTheme');
|
|
44
|
+
protected cssElements = new Map<Window, HTMLStyleElement>();
|
|
45
45
|
|
|
46
46
|
@inject(ThemeService)
|
|
47
47
|
protected readonly themeService: ThemeService;
|
|
@@ -53,11 +53,22 @@ export class StylingService implements FrontendApplicationContribution {
|
|
|
53
53
|
protected readonly themingParticipants: ContributionProvider<StylingParticipant>;
|
|
54
54
|
|
|
55
55
|
onStart(): void {
|
|
56
|
-
this.
|
|
57
|
-
this.themeService.onDidColorThemeChange(e => this.
|
|
56
|
+
this.registerWindow(window);
|
|
57
|
+
this.themeService.onDidColorThemeChange(e => this.applyStylingToWindows(e.newTheme));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
registerWindow(win: Window): Disposable {
|
|
61
|
+
const cssElement = DecorationStyle.createStyleElement('contributedColorTheme', win.document.head);
|
|
62
|
+
this.cssElements.set(win, cssElement);
|
|
63
|
+
this.applyStyling(this.themeService.getCurrentTheme(), cssElement);
|
|
64
|
+
return Disposable.create(() => this.cssElements.delete(win));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected applyStylingToWindows(theme: Theme): void {
|
|
68
|
+
this.cssElements.forEach(cssElement => this.applyStyling(theme, cssElement));
|
|
58
69
|
}
|
|
59
70
|
|
|
60
|
-
protected applyStyling(theme: Theme): void {
|
|
71
|
+
protected applyStyling(theme: Theme, cssElement: HTMLStyleElement): void {
|
|
61
72
|
const rules: string[] = [];
|
|
62
73
|
const colorTheme: ColorTheme = {
|
|
63
74
|
type: theme.type,
|
|
@@ -71,6 +82,6 @@ export class StylingService implements FrontendApplicationContribution {
|
|
|
71
82
|
themingParticipant.registerThemeStyle(colorTheme, styleCollector);
|
|
72
83
|
}
|
|
73
84
|
const fullCss = rules.join('\n');
|
|
74
|
-
|
|
85
|
+
cssElement.innerText = fullCss;
|
|
75
86
|
}
|
|
76
87
|
}
|
|
@@ -115,6 +115,10 @@ export class BaseWidget extends Widget {
|
|
|
115
115
|
protected scrollBar?: PerfectScrollbar;
|
|
116
116
|
protected scrollOptions?: PerfectScrollbar.Options;
|
|
117
117
|
|
|
118
|
+
constructor(options?: Widget.IOptions) {
|
|
119
|
+
super(options);
|
|
120
|
+
}
|
|
121
|
+
|
|
118
122
|
override dispose(): void {
|
|
119
123
|
if (this.isDisposed) {
|
|
120
124
|
return;
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
import { inject, injectable, postConstruct } from 'inversify';
|
|
17
17
|
import { SecondaryWindowService } from './secondary-window-service';
|
|
18
18
|
import { WindowService } from './window-service';
|
|
19
|
+
import { ExtractableWidget } from '../widgets';
|
|
20
|
+
import { ApplicationShell } from '../shell';
|
|
21
|
+
import { Saveable } from '../saveable';
|
|
19
22
|
|
|
20
23
|
@injectable()
|
|
21
24
|
export class DefaultSecondaryWindowService implements SecondaryWindowService {
|
|
@@ -37,6 +40,33 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
|
|
|
37
40
|
|
|
38
41
|
@postConstruct()
|
|
39
42
|
init(): void {
|
|
43
|
+
// Set up messaging with secondary windows
|
|
44
|
+
window.addEventListener('message', (event: MessageEvent) => {
|
|
45
|
+
console.trace('Message on main window', event);
|
|
46
|
+
if (event.data.fromSecondary) {
|
|
47
|
+
console.trace('Message comes from secondary window');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (event.data.fromMain) {
|
|
51
|
+
console.trace('Message has mainWindow marker, therefore ignore it');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Filter setImmediate messages. Do not forward because these come in with very high frequency.
|
|
56
|
+
// They are not needed in secondary windows because these messages are just a work around
|
|
57
|
+
// to make setImmediate work in the main window: https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
|
|
58
|
+
if (typeof event.data === 'string' && event.data.startsWith('setImmediate')) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.trace('Delegate main window message to secondary windows', event);
|
|
63
|
+
this.secondaryWindows.forEach(secondaryWindow => {
|
|
64
|
+
if (!secondaryWindow.window.closed) {
|
|
65
|
+
secondaryWindow.window.postMessage({ ...event.data, fromMain: true }, '*');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
40
70
|
// Close all open windows when the main window is closed.
|
|
41
71
|
this.windowService.onUnload(() => {
|
|
42
72
|
// Iterate backwards because calling window.close might remove the window from the array
|
|
@@ -46,33 +76,52 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
|
|
|
46
76
|
});
|
|
47
77
|
}
|
|
48
78
|
|
|
49
|
-
createSecondaryWindow(
|
|
50
|
-
const win = this.doCreateSecondaryWindow(
|
|
79
|
+
createSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
|
|
80
|
+
const win = this.doCreateSecondaryWindow(widget, shell);
|
|
51
81
|
if (win) {
|
|
52
82
|
this.secondaryWindows.push(win);
|
|
83
|
+
win.addEventListener('close', () => {
|
|
84
|
+
const extIndex = this.secondaryWindows.indexOf(win);
|
|
85
|
+
if (extIndex > -1) {
|
|
86
|
+
this.secondaryWindows.splice(extIndex, 1);
|
|
87
|
+
};
|
|
88
|
+
});
|
|
53
89
|
}
|
|
54
90
|
return win;
|
|
55
91
|
}
|
|
56
92
|
|
|
57
|
-
protected
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
win.addEventListener('unload', () => {
|
|
63
|
-
this.handleWindowClosed(win, onClose);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
93
|
+
protected findWindow<T>(windowName: string): Window | undefined {
|
|
94
|
+
for (const w of this.secondaryWindows) {
|
|
95
|
+
if (w.name === windowName) {
|
|
96
|
+
return w;
|
|
97
|
+
}
|
|
66
98
|
}
|
|
67
|
-
return
|
|
99
|
+
return undefined;
|
|
68
100
|
}
|
|
69
101
|
|
|
70
|
-
protected
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
102
|
+
protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
|
|
103
|
+
const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), 'popup') ?? undefined;
|
|
104
|
+
if (newWindow) {
|
|
105
|
+
newWindow.addEventListener('DOMContentLoaded', () => {
|
|
106
|
+
newWindow.addEventListener('beforeunload', evt => {
|
|
107
|
+
const saveable = Saveable.get(widget);
|
|
108
|
+
const wouldLoseState = !!saveable && saveable.dirty && saveable.autoSave === 'off';
|
|
109
|
+
if (wouldLoseState) {
|
|
110
|
+
evt.returnValue = '';
|
|
111
|
+
evt.preventDefault();
|
|
112
|
+
return 'non-empty';
|
|
113
|
+
}
|
|
114
|
+
}, { capture: true });
|
|
115
|
+
|
|
116
|
+
newWindow.addEventListener('close', () => {
|
|
117
|
+
const saveable = Saveable.get(widget);
|
|
118
|
+
shell.closeWidget(widget.id, {
|
|
119
|
+
save: !!saveable && saveable.dirty && saveable.autoSave !== 'off'
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return newWindow;
|
|
76
125
|
}
|
|
77
126
|
|
|
78
127
|
focus(win: Window): void {
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
+
import { ApplicationShell } from '../shell';
|
|
18
|
+
import { ExtractableWidget } from '../widgets';
|
|
19
|
+
|
|
17
20
|
export const SecondaryWindowService = Symbol('SecondaryWindowService');
|
|
18
21
|
|
|
19
22
|
/**
|
|
@@ -29,7 +32,7 @@ export interface SecondaryWindowService {
|
|
|
29
32
|
* @param onClose optional callback that is invoked when the secondary window is closed
|
|
30
33
|
* @returns the created window or `undefined` if it could not be created
|
|
31
34
|
*/
|
|
32
|
-
createSecondaryWindow(
|
|
35
|
+
createSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined;
|
|
33
36
|
|
|
34
37
|
/** Handles focussing the given secondary window in the browser and on Electron. */
|
|
35
38
|
focus(win: Window): void;
|
package/src/common/logger.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { inject, injectable } from 'inversify';
|
|
17
|
+
import { inject, injectable, postConstruct } from 'inversify';
|
|
18
18
|
import { LoggerWatcher } from './logger-watcher';
|
|
19
19
|
import { ILoggerServer, LogLevel, ConsoleLogger, rootLoggerName } from './logger-protocol';
|
|
20
20
|
|
|
@@ -235,30 +235,28 @@ export class Logger implements ILogger {
|
|
|
235
235
|
/* A promise resolved when the logger has been created by the backend. */
|
|
236
236
|
protected created: Promise<void>;
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
@inject(ILoggerServer) protected readonly server: ILoggerServer,
|
|
243
|
-
@inject(LoggerWatcher) protected readonly loggerWatcher: LoggerWatcher,
|
|
244
|
-
@inject(LoggerFactory) protected readonly factory: LoggerFactory,
|
|
245
|
-
@inject(LoggerName) protected name: string) {
|
|
238
|
+
@inject(ILoggerServer) protected readonly server: ILoggerServer;
|
|
239
|
+
@inject(LoggerWatcher) protected readonly loggerWatcher: LoggerWatcher;
|
|
240
|
+
@inject(LoggerFactory) protected readonly factory: LoggerFactory;
|
|
241
|
+
@inject(LoggerName) protected name: string;
|
|
246
242
|
|
|
247
|
-
|
|
243
|
+
@postConstruct()
|
|
244
|
+
protected init(): void {
|
|
245
|
+
if (this.name !== rootLoggerName) {
|
|
248
246
|
/* Creating a child logger. */
|
|
249
|
-
this.created = server.child(name);
|
|
247
|
+
this.created = this.server.child(this.name);
|
|
250
248
|
} else {
|
|
251
249
|
/* Creating the root logger (it already exists at startup). */
|
|
252
250
|
this.created = Promise.resolve();
|
|
253
251
|
}
|
|
254
252
|
|
|
255
253
|
/* Fetch the log level so it's cached in the frontend. */
|
|
256
|
-
this._logLevel = this.created.then(_ => this.server.getLogLevel(name));
|
|
254
|
+
this._logLevel = this.created.then(_ => this.server.getLogLevel(this.name));
|
|
257
255
|
|
|
258
256
|
/* Update the log level if it changes in the backend. */
|
|
259
|
-
loggerWatcher.onLogLevelChanged(event => {
|
|
257
|
+
this.loggerWatcher.onLogLevelChanged(event => {
|
|
260
258
|
this.created.then(() => {
|
|
261
|
-
if (event.loggerName === name) {
|
|
259
|
+
if (event.loggerName === this.name) {
|
|
262
260
|
this._logLevel = Promise.resolve(event.newLogLevel);
|
|
263
261
|
}
|
|
264
262
|
});
|
|
@@ -51,7 +51,7 @@ export interface RequestMessage {
|
|
|
51
51
|
|
|
52
52
|
export interface NotificationMessage {
|
|
53
53
|
type: RpcMessageType.Notification;
|
|
54
|
-
id
|
|
54
|
+
id?: number;
|
|
55
55
|
method: string;
|
|
56
56
|
args: any[];
|
|
57
57
|
}
|
|
@@ -111,7 +111,7 @@ export interface RpcMessageDecoder {
|
|
|
111
111
|
export interface RpcMessageEncoder {
|
|
112
112
|
cancel(buf: WriteBuffer, requestId: number): void;
|
|
113
113
|
|
|
114
|
-
notification(buf: WriteBuffer,
|
|
114
|
+
notification(buf: WriteBuffer, method: string, args: any[], id?: number): void
|
|
115
115
|
|
|
116
116
|
request(buf: WriteBuffer, requestId: number, method: string, args: any[]): void
|
|
117
117
|
|
|
@@ -130,8 +130,8 @@ export class MsgPackMessageEncoder implements RpcMessageEncoder {
|
|
|
130
130
|
cancel(buf: WriteBuffer, requestId: number): void {
|
|
131
131
|
this.encode<CancelMessage>(buf, { type: RpcMessageType.Cancel, id: requestId });
|
|
132
132
|
}
|
|
133
|
-
notification(buf: WriteBuffer,
|
|
134
|
-
this.encode<NotificationMessage>(buf, { type: RpcMessageType.Notification,
|
|
133
|
+
notification(buf: WriteBuffer, method: string, args: any[], id?: number): void {
|
|
134
|
+
this.encode<NotificationMessage>(buf, { type: RpcMessageType.Notification, method, args, id });
|
|
135
135
|
}
|
|
136
136
|
request(buf: WriteBuffer, requestId: number, method: string, args: any[]): void {
|
|
137
137
|
this.encode<RequestMessage>(buf, { type: RpcMessageType.Request, id: requestId, method, args });
|
|
@@ -77,7 +77,11 @@ export class RpcProtocol {
|
|
|
77
77
|
this.encoder = options.encoder ?? new MsgPackMessageEncoder();
|
|
78
78
|
this.decoder = options.decoder ?? new MsgPackMessageDecoder();
|
|
79
79
|
this.toDispose.push(this.onNotificationEmitter);
|
|
80
|
-
channel.onClose(
|
|
80
|
+
channel.onClose(event => {
|
|
81
|
+
this.pendingRequests.forEach(pending => pending.reject(new Error(event.reason)));
|
|
82
|
+
this.pendingRequests.clear();
|
|
83
|
+
this.toDispose.dispose();
|
|
84
|
+
});
|
|
81
85
|
this.toDispose.push(channel.onMessage(readBuffer => this.handleMessage(this.decoder.parse(readBuffer()))));
|
|
82
86
|
this.mode = options.mode ?? 'default';
|
|
83
87
|
|
|
@@ -98,7 +102,7 @@ export class RpcProtocol {
|
|
|
98
102
|
return;
|
|
99
103
|
}
|
|
100
104
|
case RpcMessageType.Notification: {
|
|
101
|
-
this.handleNotify(message.
|
|
105
|
+
this.handleNotify(message.method, message.args, message.id);
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
108
|
}
|
|
@@ -179,7 +183,7 @@ export class RpcProtocol {
|
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
const output = this.channel.getWriteBuffer();
|
|
182
|
-
this.encoder.notification(output,
|
|
186
|
+
this.encoder.notification(output, method, args, this.nextMessageId++);
|
|
183
187
|
output.commit();
|
|
184
188
|
}
|
|
185
189
|
|
|
@@ -226,7 +230,7 @@ export class RpcProtocol {
|
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
|
|
229
|
-
protected async handleNotify(
|
|
233
|
+
protected async handleNotify(method: string, args: any[], id?: number): Promise<void> {
|
|
230
234
|
if (this.toDispose.disposed) {
|
|
231
235
|
return;
|
|
232
236
|
}
|
|
@@ -23,6 +23,7 @@ import { Emitter, Event } from '../event';
|
|
|
23
23
|
import { Channel } from '../message-rpc/channel';
|
|
24
24
|
import { RequestHandler, RpcProtocol } from '../message-rpc/rpc-protocol';
|
|
25
25
|
import { ConnectionHandler } from './handler';
|
|
26
|
+
import { Deferred } from '../promise-util';
|
|
26
27
|
|
|
27
28
|
export type JsonRpcServer<Client> = Disposable & {
|
|
28
29
|
/**
|
|
@@ -55,11 +56,11 @@ export class JsonRpcConnectionHandler<T extends object> implements ConnectionHan
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
/**
|
|
58
|
-
* Factory for creating a new {@link
|
|
59
|
+
* Factory for creating a new {@link RpcProtocol} for a given chanel and {@link RequestHandler}.
|
|
59
60
|
*/
|
|
60
|
-
export type
|
|
61
|
+
export type RpcProtocolFactory = (channel: Channel, requestHandler: RequestHandler) => RpcProtocol;
|
|
61
62
|
|
|
62
|
-
const
|
|
63
|
+
const defaultRpcProtocolFactory: RpcProtocolFactory = (channel, requestHandler) => new RpcProtocol(channel, requestHandler);
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Factory for JSON-RPC proxy objects.
|
|
@@ -109,8 +110,7 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
109
110
|
protected readonly onDidOpenConnectionEmitter = new Emitter<void>();
|
|
110
111
|
protected readonly onDidCloseConnectionEmitter = new Emitter<void>();
|
|
111
112
|
|
|
112
|
-
protected
|
|
113
|
-
protected connectionPromise: Promise<RpcProtocol>;
|
|
113
|
+
protected rpcDeferred: Deferred<RpcProtocol>;
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* Build a new JsonRpcProxyFactory.
|
|
@@ -118,16 +118,14 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
118
118
|
* @param target - The object to expose to JSON-RPC methods calls. If this
|
|
119
119
|
* is omitted, the proxy won't be able to handle requests, only send them.
|
|
120
120
|
*/
|
|
121
|
-
constructor(public target?: any, protected
|
|
121
|
+
constructor(public target?: any, protected rpcProtocolFactory = defaultRpcProtocolFactory) {
|
|
122
122
|
this.waitForConnection();
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
protected waitForConnection(): void {
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.connectionPromise.then(connection => {
|
|
130
|
-
connection.channel.onClose(() => {
|
|
126
|
+
this.rpcDeferred = new Deferred<RpcProtocol>();
|
|
127
|
+
this.rpcDeferred.promise.then(protocol => {
|
|
128
|
+
protocol.channel.onClose(() => {
|
|
131
129
|
this.onDidCloseConnectionEmitter.fire(undefined);
|
|
132
130
|
// Wait for connection in case the backend reconnects
|
|
133
131
|
this.waitForConnection();
|
|
@@ -143,10 +141,10 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
143
141
|
* response.
|
|
144
142
|
*/
|
|
145
143
|
listen(channel: Channel): void {
|
|
146
|
-
const
|
|
147
|
-
|
|
144
|
+
const protocol = this.rpcProtocolFactory(channel, (meth, args) => this.onRequest(meth, ...args));
|
|
145
|
+
protocol.onNotification(event => this.onNotification(event.method, ...event.args));
|
|
148
146
|
|
|
149
|
-
this.
|
|
147
|
+
this.rpcDeferred.resolve(protocol);
|
|
150
148
|
}
|
|
151
149
|
|
|
152
150
|
/**
|
|
@@ -241,11 +239,15 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
241
239
|
if (p === 'onDidCloseConnection') {
|
|
242
240
|
return this.onDidCloseConnectionEmitter.event;
|
|
243
241
|
}
|
|
242
|
+
if (p === 'then') {
|
|
243
|
+
// Prevent inversify from identifying this proxy as a promise object.
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
244
246
|
const isNotify = this.isNotification(p);
|
|
245
247
|
return (...args: any[]) => {
|
|
246
248
|
const method = p.toString();
|
|
247
249
|
const capturedError = new Error(`Request '${method}' failed`);
|
|
248
|
-
return this.
|
|
250
|
+
return this.rpcDeferred.promise.then(connection =>
|
|
249
251
|
new Promise<void>((resolve, reject) => {
|
|
250
252
|
try {
|
|
251
253
|
if (isNotify) {
|
|
@@ -50,7 +50,7 @@ export abstract class Stopwatch {
|
|
|
50
50
|
@inject(ILogger)
|
|
51
51
|
protected readonly logger: ILogger;
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
constructor(protected readonly defaultLogOptions: LogOptions) {
|
|
54
54
|
if (!defaultLogOptions.defaultLogLevel) {
|
|
55
55
|
defaultLogOptions.defaultLogLevel = DEFAULT_LOG_LEVEL;
|
|
56
56
|
}
|
|
@@ -32,7 +32,7 @@ export class ElectronKeyboardLayoutChangeNotifier implements KeyboardLayoutChang
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
@postConstruct()
|
|
35
|
-
protected
|
|
35
|
+
protected init(): void {
|
|
36
36
|
window.electronTheiaCore.onKeyboardLayoutChanged((newLayout: NativeKeyboardLayout) => this.nativeLayoutChanged.fire(newLayout));
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -90,7 +90,11 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
@postConstruct()
|
|
93
|
-
protected
|
|
93
|
+
protected init(): void {
|
|
94
|
+
this.doInit();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected async doInit(): Promise<void> {
|
|
94
98
|
this.useNativeStyle = await window.electronTheiaCore.getTitleBarStyleAtStartup() === 'native';
|
|
95
99
|
}
|
|
96
100
|
|