@theia/core 1.46.0-next.106 → 1.46.0-next.144

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.
Files changed (125) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +4 -0
  3. package/i18n/nls.de.json +4 -0
  4. package/i18n/nls.es.json +4 -0
  5. package/i18n/nls.fr.json +4 -0
  6. package/i18n/nls.hu.json +4 -0
  7. package/i18n/nls.it.json +4 -0
  8. package/i18n/nls.ja.json +4 -0
  9. package/i18n/nls.json +4 -0
  10. package/i18n/nls.pl.json +4 -0
  11. package/i18n/nls.pt-br.json +4 -0
  12. package/i18n/nls.pt-pt.json +4 -0
  13. package/i18n/nls.ru.json +4 -0
  14. package/i18n/nls.zh-cn.json +4 -0
  15. package/lib/browser/color-application-contribution.d.ts +4 -2
  16. package/lib/browser/color-application-contribution.d.ts.map +1 -1
  17. package/lib/browser/color-application-contribution.js +11 -1
  18. package/lib/browser/color-application-contribution.js.map +1 -1
  19. package/lib/browser/command-open-handler.js +1 -1
  20. package/lib/browser/command-open-handler.js.map +1 -1
  21. package/lib/browser/common-frontend-contribution.d.ts +6 -0
  22. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  23. package/lib/browser/common-frontend-contribution.js +43 -1
  24. package/lib/browser/common-frontend-contribution.js.map +1 -1
  25. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  26. package/lib/browser/frontend-application-module.js +2 -0
  27. package/lib/browser/frontend-application-module.js.map +1 -1
  28. package/lib/browser/index.d.ts +1 -0
  29. package/lib/browser/index.d.ts.map +1 -1
  30. package/lib/browser/index.js +1 -0
  31. package/lib/browser/index.js.map +1 -1
  32. package/lib/browser/keybinding.js +1 -1
  33. package/lib/browser/keybinding.js.map +1 -1
  34. package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
  35. package/lib/browser/menu/browser-context-menu-renderer.js +2 -1
  36. package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
  37. package/lib/browser/menu/browser-menu-plugin.d.ts +1 -1
  38. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  39. package/lib/browser/menu/browser-menu-plugin.js +2 -2
  40. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  41. package/lib/browser/open-with-service.d.ts +44 -0
  42. package/lib/browser/open-with-service.d.ts.map +1 -0
  43. package/lib/browser/open-with-service.js +66 -0
  44. package/lib/browser/open-with-service.js.map +1 -0
  45. package/lib/browser/secondary-window-handler.d.ts +12 -11
  46. package/lib/browser/secondary-window-handler.d.ts.map +1 -1
  47. package/lib/browser/secondary-window-handler.js +16 -23
  48. package/lib/browser/secondary-window-handler.js.map +1 -1
  49. package/lib/browser/shell/application-shell.d.ts +3 -1
  50. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  51. package/lib/browser/shell/application-shell.js +28 -20
  52. package/lib/browser/shell/application-shell.js.map +1 -1
  53. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +1 -0
  54. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  55. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +12 -7
  56. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  57. package/lib/browser/shell/view-contribution.d.ts.map +1 -1
  58. package/lib/browser/shell/view-contribution.js +2 -1
  59. package/lib/browser/shell/view-contribution.js.map +1 -1
  60. package/lib/browser/styling-service.d.ts +3 -2
  61. package/lib/browser/styling-service.d.ts.map +1 -1
  62. package/lib/browser/styling-service.js +11 -2
  63. package/lib/browser/styling-service.js.map +1 -1
  64. package/lib/browser/window/default-secondary-window-service.d.ts +1 -1
  65. package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
  66. package/lib/browser/window/default-secondary-window-service.js +22 -25
  67. package/lib/browser/window/default-secondary-window-service.js.map +1 -1
  68. package/lib/browser/window/default-window-service.d.ts +1 -0
  69. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  70. package/lib/browser/window/default-window-service.js +3 -0
  71. package/lib/browser/window/default-window-service.js.map +1 -1
  72. package/lib/browser/window/test/mock-window-service.d.ts +1 -0
  73. package/lib/browser/window/test/mock-window-service.d.ts.map +1 -1
  74. package/lib/browser/window/test/mock-window-service.js +1 -0
  75. package/lib/browser/window/test/mock-window-service.js.map +1 -1
  76. package/lib/browser/window/window-service.d.ts +4 -0
  77. package/lib/browser/window/window-service.d.ts.map +1 -1
  78. package/lib/common/event.d.ts +5 -0
  79. package/lib/common/event.d.ts.map +1 -1
  80. package/lib/common/event.js +14 -1
  81. package/lib/common/event.js.map +1 -1
  82. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
  83. package/lib/electron-browser/menu/electron-context-menu-renderer.js +3 -1
  84. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  85. package/lib/electron-browser/preload.js +2 -2
  86. package/lib/electron-browser/preload.js.map +1 -1
  87. package/lib/electron-browser/window/electron-secondary-window-service.d.ts +1 -1
  88. package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
  89. package/lib/electron-browser/window/electron-secondary-window-service.js +4 -8
  90. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  91. package/lib/electron-browser/window/electron-window-service.d.ts +1 -0
  92. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  93. package/lib/electron-browser/window/electron-window-service.js +3 -0
  94. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  95. package/lib/electron-common/electron-api.d.ts +2 -2
  96. package/lib/electron-common/electron-api.d.ts.map +1 -1
  97. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  98. package/lib/electron-main/electron-api-main.js +12 -2
  99. package/lib/electron-main/electron-api-main.js.map +1 -1
  100. package/package.json +6 -6
  101. package/src/browser/color-application-contribution.ts +11 -2
  102. package/src/browser/command-open-handler.ts +1 -1
  103. package/src/browser/common-frontend-contribution.ts +44 -1
  104. package/src/browser/frontend-application-module.ts +3 -0
  105. package/src/browser/index.ts +1 -0
  106. package/src/browser/keybinding.ts +1 -1
  107. package/src/browser/menu/browser-context-menu-renderer.ts +2 -1
  108. package/src/browser/menu/browser-menu-plugin.ts +2 -2
  109. package/src/browser/open-with-service.ts +93 -0
  110. package/src/browser/secondary-window-handler.ts +21 -22
  111. package/src/browser/shell/application-shell.ts +25 -18
  112. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +15 -10
  113. package/src/browser/shell/view-contribution.ts +3 -2
  114. package/src/browser/styling-service.ts +12 -3
  115. package/src/browser/window/default-secondary-window-service.ts +24 -26
  116. package/src/browser/window/default-window-service.ts +4 -0
  117. package/src/browser/window/test/mock-window-service.ts +1 -0
  118. package/src/browser/window/window-service.ts +5 -0
  119. package/src/common/event.ts +18 -0
  120. package/src/electron-browser/menu/electron-context-menu-renderer.ts +3 -1
  121. package/src/electron-browser/preload.ts +3 -3
  122. package/src/electron-browser/window/electron-secondary-window-service.ts +4 -8
  123. package/src/electron-browser/window/electron-window-service.ts +3 -0
  124. package/src/electron-common/electron-api.ts +2 -2
  125. package/src/electron-main/electron-api-main.ts +11 -2
@@ -44,6 +44,7 @@ import { SecondaryWindowHandler } from '../secondary-window-handler';
44
44
  import URI from '../../common/uri';
45
45
  import { OpenerService } from '../opener-service';
46
46
  import { PreviewableWidget } from '../widgets/previewable-widget';
47
+ import { WindowService } from '../window/window-service';
47
48
 
48
49
  /** The class name added to ApplicationShell instances. */
49
50
  const APPLICATION_SHELL_CLASS = 'theia-ApplicationShell';
@@ -273,6 +274,7 @@ export class ApplicationShell extends Widget {
273
274
  @inject(CorePreferences) protected readonly corePreferences: CorePreferences,
274
275
  @inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService,
275
276
  @inject(SecondaryWindowHandler) protected readonly secondaryWindowHandler: SecondaryWindowHandler,
277
+ @inject(WindowService) protected readonly windowService: WindowService
276
278
  ) {
277
279
  super(options as Widget.IOptions);
278
280
 
@@ -338,8 +340,8 @@ export class ApplicationShell extends Widget {
338
340
  this.rightPanelHandler.dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget));
339
341
 
340
342
  this.secondaryWindowHandler.init(this);
341
- this.secondaryWindowHandler.onDidAddWidget(widget => this.fireDidAddWidget(widget));
342
- this.secondaryWindowHandler.onDidRemoveWidget(widget => this.fireDidRemoveWidget(widget));
343
+ this.secondaryWindowHandler.onDidAddWidget(([widget, window]) => this.fireDidAddWidget(widget));
344
+ this.secondaryWindowHandler.onDidRemoveWidget(([widget, window]) => this.fireDidRemoveWidget(widget));
343
345
 
344
346
  this.layout = this.createLayout();
345
347
 
@@ -1323,20 +1325,23 @@ export class ApplicationShell extends Widget {
1323
1325
  let widget = find(this.mainPanel.widgets(), w => w.id === id);
1324
1326
  if (widget) {
1325
1327
  this.mainPanel.activateWidget(widget);
1326
- return widget;
1327
1328
  }
1328
- widget = find(this.bottomPanel.widgets(), w => w.id === id);
1329
- if (widget) {
1330
- this.expandBottomPanel();
1331
- this.bottomPanel.activateWidget(widget);
1332
- return widget;
1329
+ if (!widget) {
1330
+ widget = find(this.bottomPanel.widgets(), w => w.id === id);
1331
+ if (widget) {
1332
+ this.expandBottomPanel();
1333
+ this.bottomPanel.activateWidget(widget);
1334
+ }
1333
1335
  }
1334
- widget = this.leftPanelHandler.activate(id);
1335
- if (widget) {
1336
- return widget;
1336
+ if (!widget) {
1337
+ widget = this.leftPanelHandler.activate(id);
1338
+ }
1339
+
1340
+ if (!widget) {
1341
+ widget = this.rightPanelHandler.activate(id);
1337
1342
  }
1338
- widget = this.rightPanelHandler.activate(id);
1339
1343
  if (widget) {
1344
+ this.windowService.focus();
1340
1345
  return widget;
1341
1346
  }
1342
1347
  return this.secondaryWindowHandler.activateWidget(id);
@@ -1433,17 +1438,19 @@ export class ApplicationShell extends Widget {
1433
1438
  if (tabBar) {
1434
1439
  tabBar.currentTitle = widget.title;
1435
1440
  }
1436
- return widget;
1437
1441
  }
1438
- widget = this.leftPanelHandler.expand(id);
1439
- if (widget) {
1440
- return widget;
1442
+ if (!widget) {
1443
+ widget = this.leftPanelHandler.expand(id);
1444
+ }
1445
+ if (!widget) {
1446
+ widget = this.rightPanelHandler.expand(id);
1441
1447
  }
1442
- widget = this.rightPanelHandler.expand(id);
1443
1448
  if (widget) {
1449
+ this.windowService.focus();
1444
1450
  return widget;
1451
+ } else {
1452
+ return this.secondaryWindowHandler.revealWidget(id);
1445
1453
  }
1446
- return this.secondaryWindowHandler.revealWidget(id);
1447
1454
  }
1448
1455
 
1449
1456
  /**
@@ -70,11 +70,11 @@ export class TabBarToolbar extends ReactWidget {
70
70
 
71
71
  @postConstruct()
72
72
  protected init(): void {
73
- this.toDispose.push(this.keybindings.onKeybindingsChanged(() => this.update()));
73
+ this.toDispose.push(this.keybindings.onKeybindingsChanged(() => this.maybeUpdate()));
74
74
 
75
75
  this.toDispose.push(this.contextKeyService.onDidChange(e => {
76
76
  if (e.affects(this.keybindingContextKeys)) {
77
- this.update();
77
+ this.maybeUpdate();
78
78
  }
79
79
  }));
80
80
  }
@@ -90,7 +90,7 @@ export class TabBarToolbar extends ReactWidget {
90
90
  if ('command' in item) {
91
91
  this.commands.getAllHandlers(item.command).forEach(handler => {
92
92
  if (handler.onDidChangeEnabled) {
93
- this.toDisposeOnUpdateItems.push(handler.onDidChangeEnabled(() => this.update()));
93
+ this.toDisposeOnUpdateItems.push(handler.onDidChangeEnabled(() => this.maybeUpdate()));
94
94
  }
95
95
  });
96
96
  }
@@ -113,7 +113,7 @@ export class TabBarToolbar extends ReactWidget {
113
113
  } else {
114
114
  this.hide();
115
115
  }
116
- this.update();
116
+ this.maybeUpdate();
117
117
  }
118
118
 
119
119
  updateTarget(current?: Widget): void {
@@ -130,7 +130,7 @@ export class TabBarToolbar extends ReactWidget {
130
130
  if (current) {
131
131
  const resetCurrent = () => {
132
132
  this.setCurrent(undefined);
133
- this.update();
133
+ this.maybeUpdate();
134
134
  };
135
135
  current.disposed.connect(resetCurrent);
136
136
  this.toDisposeOnSetCurrent.push(Disposable.create(() =>
@@ -144,7 +144,7 @@ export class TabBarToolbar extends ReactWidget {
144
144
  if (contextKeys.size > 0) {
145
145
  this.contextKeyListener = this.contextKeyService.onDidChange(event => {
146
146
  if (event.affects(contextKeys)) {
147
- this.update();
147
+ this.maybeUpdate();
148
148
  }
149
149
  });
150
150
  }
@@ -321,8 +321,8 @@ export class TabBarToolbar extends ReactWidget {
321
321
  protected renderMenuItem(item: TabBarToolbarItem & MenuToolbarItem): React.ReactNode {
322
322
  const icon = typeof item.icon === 'function' ? item.icon() : item.icon ?? 'ellipsis';
323
323
  return <div key={item.id}
324
- className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu'}
325
- onClick={this.showPopupMenu.bind(this, item.menuPath)}>
324
+ className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu'}
325
+ onClick={this.showPopupMenu.bind(this, item.menuPath)}>
326
326
  <div id={item.id} className={codicon(icon, true)}
327
327
  title={item.text} />
328
328
  <div className={codicon('chevron-down') + ' chevron'} />
@@ -397,9 +397,15 @@ export class TabBarToolbar extends ReactWidget {
397
397
  } else if (item.menuPath) {
398
398
  this.renderMoreContextMenu(this.toAnchor(e), item.menuPath);
399
399
  }
400
- this.update();
400
+ this.maybeUpdate();
401
401
  };
402
402
 
403
+ protected maybeUpdate(): void {
404
+ if (!this.isDisposed) {
405
+ this.update();
406
+ }
407
+ }
408
+
403
409
  protected onMouseDownEvent = (e: React.MouseEvent<HTMLElement>) => {
404
410
  if (e.button === 0) {
405
411
  e.currentTarget.classList.add('active');
@@ -419,5 +425,4 @@ export namespace TabBarToolbar {
419
425
  export const TAB_BAR_TOOLBAR_ITEM = 'item';
420
426
 
421
427
  }
422
-
423
428
  }
@@ -18,7 +18,7 @@ import { injectable, inject, interfaces, optional } from 'inversify';
18
18
  import { Widget } from '@phosphor/widgets';
19
19
  import {
20
20
  MenuModelRegistry, Command, CommandContribution,
21
- MenuContribution, CommandRegistry
21
+ MenuContribution, CommandRegistry, nls
22
22
  } from '../../common';
23
23
  import { KeybindingContribution, KeybindingRegistry } from '../keybinding';
24
24
  import { WidgetManager } from '../widget-manager';
@@ -69,7 +69,8 @@ export abstract class AbstractViewContribution<T extends Widget> implements Comm
69
69
  if (options.toggleCommandId) {
70
70
  this.toggleCommand = {
71
71
  id: options.toggleCommandId,
72
- label: 'Toggle ' + this.viewLabel + ' View'
72
+ category: nls.localizeByDefault('View'),
73
+ label: nls.localizeByDefault('Toggle {0}', this.viewLabel)
73
74
  };
74
75
  }
75
76
  }
@@ -21,7 +21,7 @@ import { ColorRegistry } from './color-registry';
21
21
  import { DecorationStyle } from './decoration-style';
22
22
  import { FrontendApplicationContribution } from './frontend-application-contribution';
23
23
  import { ThemeService } from './theming';
24
- import { Disposable } from '../common';
24
+ import { SecondaryWindowHandler } from './secondary-window-handler';
25
25
 
26
26
  export const StylingParticipant = Symbol('StylingParticipant');
27
27
 
@@ -52,16 +52,25 @@ export class StylingService implements FrontendApplicationContribution {
52
52
  @inject(ContributionProvider) @named(StylingParticipant)
53
53
  protected readonly themingParticipants: ContributionProvider<StylingParticipant>;
54
54
 
55
+ @inject(SecondaryWindowHandler)
56
+ protected readonly secondaryWindowHandler: SecondaryWindowHandler;
57
+
55
58
  onStart(): void {
56
59
  this.registerWindow(window);
60
+ this.secondaryWindowHandler.onWillAddWidget(([widget, window]) => {
61
+ this.registerWindow(window);
62
+ });
63
+ this.secondaryWindowHandler.onWillRemoveWidget(([widget, window]) => {
64
+ this.cssElements.delete(window);
65
+ });
66
+
57
67
  this.themeService.onDidColorThemeChange(e => this.applyStylingToWindows(e.newTheme));
58
68
  }
59
69
 
60
- registerWindow(win: Window): Disposable {
70
+ registerWindow(win: Window): void {
61
71
  const cssElement = DecorationStyle.createStyleElement('contributedColorTheme', win.document.head);
62
72
  this.cssElements.set(win, cssElement);
63
73
  this.applyStyling(this.themeService.getCurrentTheme(), cssElement);
64
- return Disposable.create(() => this.cssElements.delete(win));
65
74
  }
66
75
 
67
76
  protected applyStylingToWindows(theme: Theme): void {
@@ -82,37 +82,14 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
82
82
  }
83
83
 
84
84
  createSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
85
- const win = this.doCreateSecondaryWindow(widget, shell);
86
- if (win) {
87
- this.secondaryWindows.push(win);
88
- win.addEventListener('close', () => {
89
- const extIndex = this.secondaryWindows.indexOf(win);
90
- if (extIndex > -1) {
91
- this.secondaryWindows.splice(extIndex, 1);
92
- };
93
- });
94
- }
95
- return win;
96
- }
97
-
98
- protected findWindow<T>(windowName: string): Window | undefined {
99
- for (const w of this.secondaryWindows) {
100
- if (w.name === windowName) {
101
- return w;
102
- }
103
- }
104
- return undefined;
105
- }
106
-
107
- protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
108
- let options;
109
85
  const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget);
110
- options = `popup=1,width=${width},height=${height},left=${left},top=${top}`;
86
+ let options = `popup=1,width=${width},height=${height},left=${left},top=${top}`;
111
87
  if (this.preferenceService.get('window.secondaryWindowAlwaysOnTop')) {
112
88
  options += ',alwaysOnTop=true';
113
89
  }
114
90
  const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined;
115
91
  if (newWindow) {
92
+ this.secondaryWindows.push(newWindow);
116
93
  newWindow.addEventListener('DOMContentLoaded', () => {
117
94
  newWindow.addEventListener('beforeunload', evt => {
118
95
  const saveable = Saveable.get(widget);
@@ -124,17 +101,38 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
124
101
  }
125
102
  }, { capture: true });
126
103
 
127
- newWindow.addEventListener('close', () => {
104
+ newWindow.addEventListener('unload', () => {
128
105
  const saveable = Saveable.get(widget);
129
106
  shell.closeWidget(widget.id, {
130
107
  save: !!saveable && saveable.dirty && saveable.autoSave !== 'off'
131
108
  });
109
+
110
+ const extIndex = this.secondaryWindows.indexOf(newWindow);
111
+ if (extIndex > -1) {
112
+ this.secondaryWindows.splice(extIndex, 1);
113
+ };
132
114
  });
115
+ this.windowCreated(newWindow, widget, shell);
133
116
  });
134
117
  }
135
118
  return newWindow;
136
119
  }
137
120
 
121
+ protected windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void {
122
+ newWindow.addEventListener('unload', () => {
123
+ shell.closeWidget(widget.id);
124
+ });
125
+ }
126
+
127
+ protected findWindow<T>(windowName: string): Window | undefined {
128
+ for (const w of this.secondaryWindows) {
129
+ if (w.name === windowName) {
130
+ return w;
131
+ }
132
+ }
133
+ return undefined;
134
+ }
135
+
138
136
  protected findSecondaryWindowCoordinates(widget: ExtractableWidget): (number | undefined)[] {
139
137
  const clientBounds = widget.node.getBoundingClientRect();
140
138
  const preference = this.preferenceService.get('window.secondaryWindowPlacement');
@@ -57,6 +57,10 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
57
57
  this.openNewWindow(`#${DEFAULT_WINDOW_HASH}`);
58
58
  }
59
59
 
60
+ focus(): void {
61
+ window.focus();
62
+ }
63
+
60
64
  /**
61
65
  * Returns a list of actions that {@link FrontendApplicationContribution}s would like to take before shutdown
62
66
  * It is expected that this will succeed - i.e. return an empty array - at most once per session. If no vetoes are received
@@ -21,6 +21,7 @@ import { WindowService } from '../window-service';
21
21
  export class MockWindowService implements WindowService {
22
22
  openNewWindow(): undefined { return undefined; }
23
23
  openNewDefaultWindow(): void { }
24
+ focus(): void { }
24
25
  reload(): void { }
25
26
  isSafeToShutDown(): Promise<boolean> { return Promise.resolve(true); }
26
27
  setSafeToShutDown(): void { }
@@ -42,6 +42,11 @@ export interface WindowService {
42
42
  */
43
43
  openNewDefaultWindow(params?: WindowReloadOptions): void;
44
44
 
45
+ /**
46
+ * Reveal and focuses the current window
47
+ */
48
+ focus(): void;
49
+
45
50
  /**
46
51
  * Fires when the `window` unloads. The unload event is inevitable. On this event, the frontend application can save its state and release resource.
47
52
  * Saving the state and releasing any resources must be a synchronous call. Any asynchronous calls invoked after emitting this event might be ignored.
@@ -467,3 +467,21 @@ export class AsyncEmitter<T extends WaitUntilEvent> extends Emitter<T> {
467
467
  }
468
468
 
469
469
  }
470
+
471
+ export class QueueableEmitter<T> extends Emitter<T[]> {
472
+
473
+ currentQueue?: T[];
474
+
475
+ queue(...arg: T[]): void {
476
+ if (!this.currentQueue) {
477
+ this.currentQueue = [];
478
+ }
479
+ this.currentQueue.push(...arg);
480
+ }
481
+
482
+ override fire(): void {
483
+ super.fire(this.currentQueue || []);
484
+ this.currentQueue = undefined;
485
+ }
486
+
487
+ }
@@ -104,11 +104,13 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
104
104
  const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
105
105
  const { x, y } = coordinateFromAnchor(anchor);
106
106
 
107
+ const windowName = options.context?.ownerDocument.defaultView?.Window.name;
108
+
107
109
  const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => {
108
110
  if (onHide) {
109
111
  onHide();
110
112
  }
111
- });
113
+ }, windowName);
112
114
  // native context menu stops the event loop, so there is no keyboard events
113
115
  this.context.resetAltPressed();
114
116
  return new ElectronContextMenuAccess(menuHandle);
@@ -75,17 +75,17 @@ const api: TheiaCoreAPI = {
75
75
  ipcRenderer.send(CHANNEL_SET_MENU, mainMenuId, convertMenu(menu, handlers));
76
76
  },
77
77
  getSecurityToken: () => ipcRenderer.sendSync(CHANNEL_GET_SECURITY_TOKEN),
78
- focusWindow: (name: string) => ipcRenderer.send(CHANNEL_FOCUS_WINDOW, name),
78
+ focusWindow: (name?: string) => ipcRenderer.send(CHANNEL_FOCUS_WINDOW, name),
79
79
  showItemInFolder: fsPath => {
80
80
  ipcRenderer.send(CHANNEL_SHOW_ITEM_IN_FOLDER, fsPath);
81
81
  },
82
82
  attachSecurityToken: (endpoint: string) => ipcRenderer.invoke(CHANNEL_ATTACH_SECURITY_TOKEN, endpoint),
83
83
 
84
- popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise<number> {
84
+ popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise<number> {
85
85
  const menuId = nextMenuId++;
86
86
  const handlers = new Map<number, () => void>();
87
87
  commandHandlers.set(menuId, handlers);
88
- const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y);
88
+ const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y, windowName);
89
89
  const closeListener = () => {
90
90
  ipcRenderer.removeListener(CHANNEL_ON_CLOSE_POPUP, closeListener);
91
91
  commandHandlers.delete(menuId);
@@ -24,16 +24,12 @@ export class ElectronSecondaryWindowService extends DefaultSecondaryWindowServic
24
24
  window.electronTheiaCore.focusWindow(win.name);
25
25
  }
26
26
 
27
- protected override doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
28
- const w = super.doCreateSecondaryWindow(widget, shell);
29
- if (w) {
30
- window.electronTheiaCore.setMenuBarVisible(false, w.name);
31
- window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(w.name, () => this.canClose(widget, shell));
32
- }
33
- return w;
27
+ protected override windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void {
28
+ window.electronTheiaCore.setMenuBarVisible(false, newWindow.name);
29
+ window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(newWindow.name, () => this.canClose(widget, shell));
34
30
  }
35
31
  private async canClose(widget: ExtractableWidget, shell: ApplicationShell): Promise<boolean> {
36
- await shell.closeWidget(widget.id, undefined);
32
+ await shell.closeWidget(widget.id);
37
33
  return widget.isDisposed;
38
34
  }
39
35
  }
@@ -57,6 +57,9 @@ export class ElectronWindowService extends DefaultWindowService {
57
57
  this.delegate.openNewDefaultWindow(params);
58
58
  }
59
59
 
60
+ override focus(): void {
61
+ window.electronTheiaCore.focusWindow();
62
+ }
60
63
  @postConstruct()
61
64
  protected init(): void {
62
65
  // Update the default zoom level on startup when the preferences event is fired.
@@ -50,10 +50,10 @@ export interface TheiaCoreAPI {
50
50
  setMenuBarVisible(visible: boolean, windowName?: string): void;
51
51
  setMenu(menu: MenuDto[] | undefined): void;
52
52
 
53
- popup(menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise<number>;
53
+ popup(menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise<number>;
54
54
  closePopup(handle: number): void;
55
55
 
56
- focusWindow(name: string): void;
56
+ focusWindow(name?: string): void;
57
57
 
58
58
  showItemInFolder(fsPath: string): void;
59
59
 
@@ -115,7 +115,7 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
115
115
  });
116
116
 
117
117
  // popup menu
118
- ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y) => {
118
+ ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y, windowName?: string) => {
119
119
  const zoom = event.sender.getZoomFactor();
120
120
  // TODO: Remove the offset once Electron fixes https://github.com/electron/electron/issues/31641
121
121
  const offset = process.platform === 'win32' ? 0 : 2;
@@ -124,7 +124,14 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
124
124
  y = Math.round(y * zoom) + offset;
125
125
  const popup = Menu.buildFromTemplate(this.fromMenuDto(event.sender, menuId, menu));
126
126
  this.openPopups.set(menuId, popup);
127
+ let electronWindow: BrowserWindow | undefined;
128
+ if (windowName) {
129
+ electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
130
+ } else {
131
+ electronWindow = BrowserWindow.fromWebContents(event.sender) || undefined;
132
+ }
127
133
  popup.popup({
134
+ window: electronWindow,
128
135
  callback: () => {
129
136
  this.openPopups.delete(menuId);
130
137
  event.sender.send(CHANNEL_ON_CLOSE_POPUP, menuId);
@@ -140,7 +147,9 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
140
147
 
141
148
  // focus windows for secondary window support
142
149
  ipcMain.on(CHANNEL_FOCUS_WINDOW, (event, windowName) => {
143
- const electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
150
+ const electronWindow = windowName
151
+ ? BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName)
152
+ : BrowserWindow.fromWebContents(event.sender);
144
153
  if (electronWindow) {
145
154
  if (electronWindow.isMinimized()) {
146
155
  electronWindow.restore();