@theia/core 1.39.0-next.1 → 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 +6 -6
- 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/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/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 +11 -10
- package/lib/common/messaging/proxy-factory.js.map +1 -1
- package/lib/electron-browser/preload.d.ts.map +1 -1
- package/lib/electron-browser/preload.js +20 -15
- 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 +2 -0
- 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 +16 -0
- 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/package.json +4 -4
- package/src/browser/dialogs.ts +69 -52
- 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/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 +13 -15
- package/src/electron-browser/preload.ts +21 -1
- package/src/electron-browser/window/electron-secondary-window-service.ts +8 -2
- package/src/electron-common/electron-api.ts +4 -0
- package/src/electron-main/electron-api-main.ts +19 -1
- package/src/electron-main/theia-electron-window.ts +30 -0
|
@@ -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
|
/**
|
|
@@ -249,7 +247,7 @@ export class JsonRpcProxyFactory<T extends object> implements ProxyHandler<T> {
|
|
|
249
247
|
return (...args: any[]) => {
|
|
250
248
|
const method = p.toString();
|
|
251
249
|
const capturedError = new Error(`Request '${method}' failed`);
|
|
252
|
-
return this.
|
|
250
|
+
return this.rpcDeferred.promise.then(connection =>
|
|
253
251
|
new Promise<void>((resolve, reject) => {
|
|
254
252
|
try {
|
|
255
253
|
if (isNotify) {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
15
|
//
|
|
16
|
+
import { IpcRendererEvent } from '@theia/electron/shared/electron';
|
|
16
17
|
import { Disposable } from '../common/disposable';
|
|
17
18
|
import { StopReason } from '../common/frontend-application-state';
|
|
18
19
|
import { NativeKeyboardLayout } from '../common/keyboard/keyboard-layout-provider';
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
CHANNEL_ON_WINDOW_EVENT, CHANNEL_GET_ZOOM_LEVEL, CHANNEL_SET_ZOOM_LEVEL, CHANNEL_IS_FULL_SCREENABLE, CHANNEL_TOGGLE_FULL_SCREEN,
|
|
25
26
|
CHANNEL_IS_FULL_SCREEN, CHANNEL_SET_MENU_BAR_VISIBLE, CHANNEL_REQUEST_CLOSE, CHANNEL_SET_TITLE_STYLE, CHANNEL_RESTART,
|
|
26
27
|
CHANNEL_REQUEST_RELOAD, CHANNEL_APP_STATE_CHANGED, CHANNEL_SHOW_ITEM_IN_FOLDER, CHANNEL_READ_CLIPBOARD, CHANNEL_WRITE_CLIPBOARD,
|
|
27
|
-
CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto
|
|
28
|
+
CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE
|
|
28
29
|
} from '../electron-common/electron-api';
|
|
29
30
|
|
|
30
31
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
@@ -138,6 +139,25 @@ const api: TheiaCoreAPI = {
|
|
|
138
139
|
});
|
|
139
140
|
},
|
|
140
141
|
|
|
142
|
+
setSecondaryWindowCloseRequestHandler(windowName: string, handler: () => Promise<boolean>): void {
|
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
|
+
const listener: (event: IpcRendererEvent, ...args: any[]) => void = async (event, name, confirmChannel, cancelChannel) => {
|
|
145
|
+
if (name === windowName) {
|
|
146
|
+
try {
|
|
147
|
+
if (await handler()) {
|
|
148
|
+
event.sender.send(confirmChannel);
|
|
149
|
+
ipcRenderer.removeListener(CHANNEL_REQUEST_SECONDARY_CLOSE, listener);
|
|
150
|
+
return;
|
|
151
|
+
};
|
|
152
|
+
} catch (e) {
|
|
153
|
+
console.warn('exception in close handler ', e);
|
|
154
|
+
}
|
|
155
|
+
event.sender.send(cancelChannel);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
ipcRenderer.on(CHANNEL_REQUEST_SECONDARY_CLOSE, listener);
|
|
159
|
+
},
|
|
160
|
+
|
|
141
161
|
toggleDevTools: function (): void {
|
|
142
162
|
ipcRenderer.send(CHANNEL_TOGGLE_DEVTOOLS);
|
|
143
163
|
},
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { injectable } from 'inversify';
|
|
18
18
|
import { DefaultSecondaryWindowService } from '../../browser/window/default-secondary-window-service';
|
|
19
|
+
import { ApplicationShell, ExtractableWidget } from 'src/browser';
|
|
19
20
|
|
|
20
21
|
@injectable()
|
|
21
22
|
export class ElectronSecondaryWindowService extends DefaultSecondaryWindowService {
|
|
@@ -23,11 +24,16 @@ export class ElectronSecondaryWindowService extends DefaultSecondaryWindowServic
|
|
|
23
24
|
window.electronTheiaCore.focusWindow(win.name);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
protected override doCreateSecondaryWindow(
|
|
27
|
-
const w = super.doCreateSecondaryWindow(
|
|
27
|
+
protected override doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
|
|
28
|
+
const w = super.doCreateSecondaryWindow(widget, shell);
|
|
28
29
|
if (w) {
|
|
29
30
|
window.electronTheiaCore.setMenuBarVisible(false, w.name);
|
|
31
|
+
window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(w.name, () => this.canClose(widget, shell));
|
|
30
32
|
}
|
|
31
33
|
return w;
|
|
32
34
|
}
|
|
35
|
+
private async canClose(widget: ExtractableWidget, shell: ApplicationShell): Promise<boolean> {
|
|
36
|
+
await shell.closeWidget(widget.id, undefined);
|
|
37
|
+
return widget.isDisposed;
|
|
38
|
+
}
|
|
33
39
|
}
|
|
@@ -64,6 +64,8 @@ export interface TheiaCoreAPI {
|
|
|
64
64
|
onWindowEvent(event: WindowEvent, handler: () => void): Disposable;
|
|
65
65
|
setCloseRequestHandler(handler: (reason: StopReason) => Promise<boolean>): void;
|
|
66
66
|
|
|
67
|
+
setSecondaryWindowCloseRequestHandler(windowName: string, handler: () => Promise<boolean>): void;
|
|
68
|
+
|
|
67
69
|
toggleDevTools(): void;
|
|
68
70
|
getZoomLevel(): Promise<number>;
|
|
69
71
|
setZoomLevel(desired: number): void;
|
|
@@ -121,6 +123,8 @@ export const CHANNEL_IS_FULL_SCREENABLE = 'IsFullScreenable';
|
|
|
121
123
|
export const CHANNEL_IS_FULL_SCREEN = 'IsFullScreen';
|
|
122
124
|
export const CHANNEL_TOGGLE_FULL_SCREEN = 'ToggleFullScreen';
|
|
123
125
|
|
|
126
|
+
export const CHANNEL_REQUEST_SECONDARY_CLOSE = 'RequestSecondaryClose';
|
|
127
|
+
|
|
124
128
|
export const CHANNEL_REQUEST_CLOSE = 'RequestClose';
|
|
125
129
|
export const CHANNEL_REQUEST_RELOAD = 'RequestReload';
|
|
126
130
|
export const CHANNEL_RESTART = 'Restart';
|
|
@@ -49,7 +49,8 @@ import {
|
|
|
49
49
|
InternalMenuDto,
|
|
50
50
|
CHANNEL_SET_MENU_BAR_VISIBLE,
|
|
51
51
|
CHANNEL_TOGGLE_FULL_SCREEN,
|
|
52
|
-
CHANNEL_IS_MAXIMIZED
|
|
52
|
+
CHANNEL_IS_MAXIMIZED,
|
|
53
|
+
CHANNEL_REQUEST_SECONDARY_CLOSE
|
|
53
54
|
} from '../electron-common/electron-api';
|
|
54
55
|
import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application';
|
|
55
56
|
import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common';
|
|
@@ -267,6 +268,23 @@ export namespace TheiaRendererAPI {
|
|
|
267
268
|
}).finally(() => disposables.dispose());
|
|
268
269
|
}
|
|
269
270
|
|
|
271
|
+
export function requestSecondaryClose(mainWindow: WebContents, secondaryWindow: WebContents): Promise<boolean> {
|
|
272
|
+
const channelNr = nextReplyChannel++;
|
|
273
|
+
const confirmChannel = `confirm-${channelNr}`;
|
|
274
|
+
const cancelChannel = `cancel-${channelNr}`;
|
|
275
|
+
const disposables = new DisposableCollection();
|
|
276
|
+
|
|
277
|
+
return new Promise<boolean>(resolve => {
|
|
278
|
+
mainWindow.send(CHANNEL_REQUEST_SECONDARY_CLOSE, secondaryWindow.mainFrame.name, confirmChannel, cancelChannel);
|
|
279
|
+
createDisposableListener(ipcMain, confirmChannel, e => {
|
|
280
|
+
resolve(true);
|
|
281
|
+
}, disposables);
|
|
282
|
+
createDisposableListener(ipcMain, cancelChannel, e => {
|
|
283
|
+
resolve(false);
|
|
284
|
+
}, disposables);
|
|
285
|
+
}).finally(() => disposables.dispose());
|
|
286
|
+
}
|
|
287
|
+
|
|
270
288
|
export function onRequestReload(wc: WebContents, handler: () => void): Disposable {
|
|
271
289
|
return createWindowListener(wc, CHANNEL_REQUEST_RELOAD, handler);
|
|
272
290
|
}
|
|
@@ -44,6 +44,12 @@ export const TheiaBrowserWindowOptions = Symbol('TheiaBrowserWindowOptions');
|
|
|
44
44
|
export const WindowApplicationConfig = Symbol('WindowApplicationConfig');
|
|
45
45
|
export type WindowApplicationConfig = FrontendApplicationConfig;
|
|
46
46
|
|
|
47
|
+
enum ClosingState {
|
|
48
|
+
initial,
|
|
49
|
+
inProgress,
|
|
50
|
+
readyToClose
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
@injectable()
|
|
48
54
|
export class TheiaElectronWindow {
|
|
49
55
|
@inject(TheiaBrowserWindowOptions) protected readonly options: TheiaBrowserWindowOptions;
|
|
@@ -75,8 +81,32 @@ export class TheiaElectronWindow {
|
|
|
75
81
|
this.attachCloseListeners();
|
|
76
82
|
this.trackApplicationState();
|
|
77
83
|
this.attachReloadListener();
|
|
84
|
+
this.attachSecondaryWindowListener();
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
protected attachSecondaryWindowListener(): void {
|
|
88
|
+
createDisposableListener(this._window.webContents, 'did-create-window', (newWindow: BrowserWindow) => {
|
|
89
|
+
let closingState = ClosingState.initial;
|
|
90
|
+
newWindow.on('close', event => {
|
|
91
|
+
if (closingState === ClosingState.initial) {
|
|
92
|
+
closingState = ClosingState.inProgress;
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
TheiaRendererAPI.requestSecondaryClose(this._window.webContents, newWindow.webContents).then(shouldClose => {
|
|
95
|
+
if (shouldClose) {
|
|
96
|
+
closingState = ClosingState.readyToClose;
|
|
97
|
+
newWindow.close();
|
|
98
|
+
} else {
|
|
99
|
+
closingState = ClosingState.initial;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
} else if (closingState === ClosingState.inProgress) {
|
|
103
|
+
// When the extracted widget is disposed programmatically, a dispose listener on it will try to close the window.
|
|
104
|
+
// if we dispose the widget because of closing the window, we'll get a recursive call to window.close()
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
80
110
|
/**
|
|
81
111
|
* Only show the window when the content is ready.
|
|
82
112
|
*/
|