@theia/core 1.18.0 → 1.19.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.
Files changed (200) hide show
  1. package/README.md +3 -2
  2. package/lib/browser/about-dialog.d.ts.map +1 -1
  3. package/lib/browser/about-dialog.js +1 -1
  4. package/lib/browser/about-dialog.js.map +1 -1
  5. package/lib/browser/authentication-service.d.ts.map +1 -1
  6. package/lib/browser/authentication-service.js +1 -1
  7. package/lib/browser/authentication-service.js.map +1 -1
  8. package/lib/browser/common-frontend-contribution.d.ts +6 -0
  9. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  10. package/lib/browser/common-frontend-contribution.js +122 -118
  11. package/lib/browser/common-frontend-contribution.js.map +1 -1
  12. package/lib/browser/core-preferences.d.ts +1 -0
  13. package/lib/browser/core-preferences.d.ts.map +1 -1
  14. package/lib/browser/core-preferences.js +25 -18
  15. package/lib/browser/core-preferences.js.map +1 -1
  16. package/lib/browser/dialogs.d.ts +6 -0
  17. package/lib/browser/dialogs.d.ts.map +1 -1
  18. package/lib/browser/dialogs.js +10 -3
  19. package/lib/browser/dialogs.js.map +1 -1
  20. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  21. package/lib/browser/frontend-application-module.js +3 -0
  22. package/lib/browser/frontend-application-module.js.map +1 -1
  23. package/lib/browser/frontend-application.d.ts +6 -0
  24. package/lib/browser/frontend-application.d.ts.map +1 -1
  25. package/lib/browser/frontend-application.js +13 -0
  26. package/lib/browser/frontend-application.js.map +1 -1
  27. package/lib/browser/index.d.ts +1 -0
  28. package/lib/browser/index.d.ts.map +1 -1
  29. package/lib/browser/index.js +1 -0
  30. package/lib/browser/index.js.map +1 -1
  31. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.d.ts.map +1 -1
  32. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js +16 -11
  33. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js.map +1 -1
  34. package/lib/browser/menu/browser-context-menu-renderer.d.ts +1 -1
  35. package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
  36. package/lib/browser/menu/browser-menu-plugin.d.ts +3 -0
  37. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  38. package/lib/browser/menu/browser-menu-plugin.js +24 -4
  39. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  40. package/lib/browser/nls-loader.d.ts +17 -0
  41. package/lib/browser/nls-loader.d.ts.map +1 -0
  42. package/lib/browser/nls-loader.js +29 -0
  43. package/lib/browser/nls-loader.js.map +1 -0
  44. package/lib/browser/progress-status-bar-item.d.ts +1 -2
  45. package/lib/browser/progress-status-bar-item.d.ts.map +1 -1
  46. package/lib/browser/progress-status-bar-item.js.map +1 -1
  47. package/lib/browser/quick-input/quick-command-frontend-contribution.d.ts.map +1 -1
  48. package/lib/browser/quick-input/quick-command-frontend-contribution.js +2 -1
  49. package/lib/browser/quick-input/quick-command-frontend-contribution.js.map +1 -1
  50. package/lib/browser/quick-input/quick-command-service.d.ts.map +1 -1
  51. package/lib/browser/quick-input/quick-command-service.js +2 -2
  52. package/lib/browser/quick-input/quick-command-service.js.map +1 -1
  53. package/lib/browser/quick-input/quick-view-service.d.ts +1 -2
  54. package/lib/browser/quick-input/quick-view-service.d.ts.map +1 -1
  55. package/lib/browser/quick-input/quick-view-service.js.map +1 -1
  56. package/lib/browser/shell/application-shell.d.ts +3 -1
  57. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  58. package/lib/browser/shell/application-shell.js +1 -2
  59. package/lib/browser/shell/application-shell.js.map +1 -1
  60. package/lib/browser/shell/shell-layout-restorer.d.ts.map +1 -1
  61. package/lib/browser/shell/shell-layout-restorer.js +5 -3
  62. package/lib/browser/shell/shell-layout-restorer.js.map +1 -1
  63. package/lib/browser/shell/tab-bar-decorator.d.ts +1 -1
  64. package/lib/browser/shell/tab-bar-decorator.d.ts.map +1 -1
  65. package/lib/browser/shell/tab-bar-decorator.js.map +1 -1
  66. package/lib/browser/shell/tab-bar-toolbar.d.ts.map +1 -1
  67. package/lib/browser/shell/tab-bar-toolbar.js +2 -1
  68. package/lib/browser/shell/tab-bar-toolbar.js.map +1 -1
  69. package/lib/browser/shell/tab-bars.d.ts +14 -1
  70. package/lib/browser/shell/tab-bars.d.ts.map +1 -1
  71. package/lib/browser/shell/tab-bars.js +99 -7
  72. package/lib/browser/shell/tab-bars.js.map +1 -1
  73. package/lib/browser/tooltip-service.d.ts +52 -0
  74. package/lib/browser/tooltip-service.d.ts.map +1 -0
  75. package/lib/browser/tooltip-service.js +89 -0
  76. package/lib/browser/tooltip-service.js.map +1 -0
  77. package/lib/browser/tree/tree-model.d.ts.map +1 -1
  78. package/lib/browser/view-container.d.ts +55 -14
  79. package/lib/browser/view-container.d.ts.map +1 -1
  80. package/lib/browser/view-container.js +284 -66
  81. package/lib/browser/view-container.js.map +1 -1
  82. package/lib/browser/window/default-window-service.js +2 -2
  83. package/lib/browser/window/default-window-service.js.map +1 -1
  84. package/lib/browser/window/window-service.d.ts +1 -7
  85. package/lib/browser/window/window-service.d.ts.map +1 -1
  86. package/lib/browser/window/window-service.js +1 -5
  87. package/lib/browser/window/window-service.js.map +1 -1
  88. package/lib/browser/window-contribution.d.ts.map +1 -1
  89. package/lib/browser/window-contribution.js +2 -2
  90. package/lib/browser/window-contribution.js.map +1 -1
  91. package/lib/common/command.d.ts.map +1 -1
  92. package/lib/common/command.js +1 -2
  93. package/lib/common/command.js.map +1 -1
  94. package/lib/common/i18n/localization.d.ts +5 -0
  95. package/lib/common/i18n/localization.d.ts.map +1 -1
  96. package/lib/common/i18n/localization.js +43 -1
  97. package/lib/common/i18n/localization.js.map +1 -1
  98. package/lib/common/index.d.ts +1 -0
  99. package/lib/common/index.d.ts.map +1 -1
  100. package/lib/common/index.js +1 -0
  101. package/lib/common/index.js.map +1 -1
  102. package/lib/{browser → common}/nls.d.ts +3 -2
  103. package/lib/common/nls.d.ts.map +1 -0
  104. package/lib/common/nls.js +29 -0
  105. package/lib/common/nls.js.map +1 -0
  106. package/lib/common/window.d.ts +29 -0
  107. package/lib/common/window.d.ts.map +1 -0
  108. package/lib/common/window.js +23 -0
  109. package/lib/common/window.js.map +1 -0
  110. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts +9 -5
  111. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
  112. package/lib/electron-browser/menu/electron-context-menu-renderer.js +40 -15
  113. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  114. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +5 -8
  115. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  116. package/lib/electron-browser/menu/electron-main-menu-factory.js +10 -17
  117. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  118. package/lib/electron-browser/menu/electron-menu-contribution.d.ts +15 -7
  119. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  120. package/lib/electron-browser/menu/electron-menu-contribution.js +112 -42
  121. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  122. package/lib/electron-browser/window/electron-window-preferences.d.ts +1 -0
  123. package/lib/electron-browser/window/electron-window-preferences.d.ts.map +1 -1
  124. package/lib/electron-browser/window/electron-window-preferences.js +12 -1
  125. package/lib/electron-browser/window/electron-window-preferences.js.map +1 -1
  126. package/lib/electron-browser/window/electron-window-service.d.ts +1 -1
  127. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  128. package/lib/electron-common/electron-main-window-service.d.ts +1 -1
  129. package/lib/electron-common/electron-main-window-service.d.ts.map +1 -1
  130. package/lib/electron-common/electron-main-window-service.js.map +1 -1
  131. package/lib/electron-common/messaging/electron-messages.d.ts +20 -0
  132. package/lib/electron-common/messaging/electron-messages.d.ts.map +1 -0
  133. package/lib/electron-common/messaging/electron-messages.js +23 -0
  134. package/lib/electron-common/messaging/electron-messages.js.map +1 -0
  135. package/lib/electron-main/electron-main-application.d.ts +6 -0
  136. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  137. package/lib/electron-main/electron-main-application.js +80 -24
  138. package/lib/electron-main/electron-main-application.js.map +1 -1
  139. package/lib/electron-main/electron-main-window-service-impl.d.ts +1 -1
  140. package/lib/electron-main/electron-main-window-service-impl.d.ts.map +1 -1
  141. package/lib/electron-main/electron-main-window-service-impl.js.map +1 -1
  142. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +1 -1
  143. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts.map +1 -1
  144. package/lib/electron-main/messaging/electron-messaging-contribution.js +1 -2
  145. package/lib/electron-main/messaging/electron-messaging-contribution.js.map +1 -1
  146. package/package.json +6 -4
  147. package/shared/@theia/application-package/lib/api.d.ts +1 -0
  148. package/shared/@theia/application-package/lib/api.js +1 -0
  149. package/src/browser/about-dialog.tsx +2 -2
  150. package/src/browser/authentication-service.ts +1 -2
  151. package/src/browser/common-frontend-contribution.ts +99 -95
  152. package/src/browser/core-preferences.ts +28 -18
  153. package/src/browser/dialogs.ts +10 -3
  154. package/src/browser/frontend-application-module.ts +4 -0
  155. package/src/browser/frontend-application.ts +13 -0
  156. package/src/browser/index.ts +1 -0
  157. package/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +16 -11
  158. package/src/browser/menu/browser-context-menu-renderer.ts +1 -1
  159. package/src/browser/menu/browser-menu-plugin.ts +25 -5
  160. package/src/browser/nls-loader.ts +26 -0
  161. package/src/browser/progress-status-bar-item.ts +1 -2
  162. package/src/browser/quick-input/quick-command-frontend-contribution.ts +2 -2
  163. package/src/browser/quick-input/quick-command-service.ts +2 -2
  164. package/src/browser/quick-input/quick-view-service.ts +1 -2
  165. package/src/browser/shell/application-shell.ts +4 -3
  166. package/src/browser/shell/shell-layout-restorer.ts +4 -3
  167. package/src/browser/shell/tab-bar-decorator.ts +1 -1
  168. package/src/browser/shell/tab-bar-toolbar.tsx +3 -1
  169. package/src/browser/shell/tab-bars.ts +103 -8
  170. package/src/browser/style/index.css +5 -0
  171. package/src/browser/style/sidepanel.css +8 -2
  172. package/src/browser/style/tabs.css +30 -0
  173. package/src/browser/style/tooltip.css +28 -0
  174. package/src/browser/style/view-container.css +9 -9
  175. package/src/browser/tooltip-service.tsx +98 -0
  176. package/src/browser/tree/tree-model.ts +1 -1
  177. package/src/browser/view-container.ts +312 -80
  178. package/src/browser/window/default-window-service.ts +1 -1
  179. package/src/browser/window/window-service.ts +1 -9
  180. package/src/browser/window-contribution.ts +2 -2
  181. package/src/common/command.ts +1 -2
  182. package/src/common/i18n/localization.ts +44 -0
  183. package/src/common/index.ts +1 -0
  184. package/src/common/nls.ts +30 -0
  185. package/src/common/window.ts +30 -0
  186. package/src/electron-browser/menu/electron-context-menu-renderer.ts +38 -16
  187. package/src/electron-browser/menu/electron-main-menu-factory.ts +10 -15
  188. package/src/electron-browser/menu/electron-menu-contribution.ts +129 -39
  189. package/src/electron-browser/menu/electron-menu-style.css +84 -0
  190. package/src/electron-browser/window/electron-window-preferences.ts +13 -1
  191. package/src/electron-browser/window/electron-window-service.ts +1 -1
  192. package/src/electron-common/electron-main-window-service.ts +1 -2
  193. package/src/electron-common/messaging/electron-messages.ts +20 -0
  194. package/src/electron-main/electron-main-application.ts +85 -21
  195. package/src/electron-main/electron-main-window-service-impl.ts +1 -2
  196. package/src/electron-main/messaging/electron-messaging-contribution.ts +1 -2
  197. package/lib/browser/nls.d.ts.map +0 -1
  198. package/lib/browser/nls.js +0 -64
  199. package/lib/browser/nls.js.map +0 -1
  200. package/src/browser/nls.ts +0 -65
@@ -23,10 +23,10 @@ import { CommonMenus } from '../browser/common-frontend-contribution';
23
23
 
24
24
  export namespace WindowCommands {
25
25
 
26
- export const NEW_WINDOW: Command = {
26
+ export const NEW_WINDOW = Command.toLocalizedCommand({
27
27
  id: 'workbench.action.newWindow',
28
28
  label: 'New Window'
29
- };
29
+ }, 'vscode/windowActions/newWindow');
30
30
  }
31
31
 
32
32
  @injectable()
@@ -18,8 +18,7 @@ import { injectable, inject, named } from 'inversify';
18
18
  import { Event, Emitter, WaitUntilEvent } from './event';
19
19
  import { Disposable, DisposableCollection } from './disposable';
20
20
  import { ContributionProvider } from './contribution-provider';
21
- // eslint-disable-next-line @theia/runtime-import-check
22
- import { nls } from '../browser/nls';
21
+ import { nls } from './nls';
23
22
 
24
23
  /**
25
24
  * A command is a unique identifier of a function
@@ -31,3 +31,47 @@ export interface Localization {
31
31
  localizedLanguageName?: string;
32
32
  translations: { [key: string]: string };
33
33
  }
34
+
35
+ export type FormatType = string | number | boolean | undefined;
36
+
37
+ export namespace Localization {
38
+
39
+ function format(message: string, args: FormatType[]): string {
40
+ let result = message;
41
+ if (args.length > 0) {
42
+ result = message.replace(/\{(\d+)\}/g, (match, rest) => {
43
+ const index = rest[0];
44
+ const arg = args[index];
45
+ let replacement = match;
46
+ if (typeof arg === 'string') {
47
+ replacement = arg;
48
+ } else if (typeof arg === 'number' || typeof arg === 'boolean' || !arg) {
49
+ replacement = String(arg);
50
+ }
51
+ return replacement;
52
+ });
53
+ }
54
+ return result;
55
+ }
56
+
57
+ export function localize(localization: Localization | undefined, key: string, defaultValue: string, ...args: FormatType[]): string {
58
+ let value = defaultValue;
59
+ if (localization) {
60
+ const translation = localization.translations[key];
61
+ if (translation) {
62
+ // vscode's localizations often contain additional '&&' symbols, which we simply ignore
63
+ value = translation.replace(/&&/g, '');
64
+ }
65
+ }
66
+ return format(value, args);
67
+ }
68
+
69
+ export function transformKey(key: string): string {
70
+ let nlsKey = key;
71
+ const keySlashIndex = key.lastIndexOf('/');
72
+ if (keySlashIndex >= 0) {
73
+ nlsKey = key.substring(keySlashIndex + 1);
74
+ }
75
+ return nlsKey;
76
+ }
77
+ }
@@ -38,6 +38,7 @@ export * from './strings';
38
38
  export * from './application-error';
39
39
  export * from './lsp-types';
40
40
  export * from './contribution-filter';
41
+ export * from './nls';
41
42
 
42
43
  import { environment } from '@theia/application-package/lib/environment';
43
44
  export { environment };
@@ -0,0 +1,30 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2021 TypeFox and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ import { FormatType, Localization } from './i18n/localization';
18
+
19
+ export namespace nls {
20
+
21
+ export let localization: Localization | undefined;
22
+
23
+ export const localeId = 'localeId';
24
+
25
+ export const locale = typeof window === 'object' && window && window.localStorage.getItem(localeId) || undefined;
26
+
27
+ export function localize(key: string, defaultValue: string, ...args: FormatType[]): string {
28
+ return Localization.localize(localization, key, defaultValue, ...args);
29
+ }
30
+ }
@@ -0,0 +1,30 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2021 Ericsson and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ /**
18
+ * The window hash value that is used to spawn a new default window.
19
+ */
20
+ export const DEFAULT_WINDOW_HASH: string = '!empty';
21
+
22
+ /**
23
+ * The options for opening new windows.
24
+ */
25
+ export interface NewWindowOptions {
26
+ /**
27
+ * Controls whether the window should be opened externally.
28
+ */
29
+ readonly external?: boolean;
30
+ }
@@ -17,11 +17,15 @@
17
17
  /* eslint-disable @typescript-eslint/no-explicit-any */
18
18
 
19
19
  import * as electron from '../../../shared/electron';
20
- import { inject, injectable } from 'inversify';
21
- import { ContextMenuRenderer, RenderContextMenuOptions, ContextMenuAccess, FrontendApplicationContribution, CommonCommands, coordinateFromAnchor } from '../../browser';
20
+ import { inject, injectable, postConstruct } from 'inversify';
21
+ import {
22
+ ContextMenuRenderer, RenderContextMenuOptions, ContextMenuAccess, FrontendApplicationContribution, CommonCommands, coordinateFromAnchor, PreferenceService
23
+ } from '../../browser';
22
24
  import { ElectronMainMenuFactory } from './electron-main-menu-factory';
23
25
  import { ContextMenuContext } from '../../browser/menu/context-menu-context';
24
26
  import { MenuPath, MenuContribution, MenuModelRegistry } from '../../common';
27
+ import { BrowserContextMenuRenderer } from '../../browser/menu/browser-context-menu-renderer';
28
+ import { RequestTitleBarStyle, TitleBarStyleAtStartup } from '../../electron-common/messaging/electron-messages';
25
29
 
26
30
  export class ElectronContextMenuAccess extends ContextMenuAccess {
27
31
  constructor(readonly menu: electron.Menu) {
@@ -73,27 +77,45 @@ export class ElectronTextInputContextMenuContribution implements FrontendApplica
73
77
  }
74
78
 
75
79
  @injectable()
76
- export class ElectronContextMenuRenderer extends ContextMenuRenderer {
80
+ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
77
81
 
78
82
  @inject(ContextMenuContext)
79
83
  protected readonly context: ContextMenuContext;
80
84
 
81
- constructor(@inject(ElectronMainMenuFactory) private menuFactory: ElectronMainMenuFactory) {
82
- super();
85
+ @inject(PreferenceService)
86
+ protected readonly preferenceService: PreferenceService;
87
+
88
+ protected useNativeStyle: boolean = true;
89
+
90
+ constructor(@inject(ElectronMainMenuFactory) private electronMenuFactory: ElectronMainMenuFactory) {
91
+ super(electronMenuFactory);
92
+ }
93
+
94
+ @postConstruct()
95
+ protected async init(): Promise<void> {
96
+ electron.ipcRenderer.on(TitleBarStyleAtStartup, (_event, style: string) => {
97
+ this.useNativeStyle = style === 'native';
98
+ });
99
+ electron.ipcRenderer.send(RequestTitleBarStyle);
83
100
  }
84
101
 
85
- protected doRender({ menuPath, anchor, args, onHide }: RenderContextMenuOptions): ElectronContextMenuAccess {
86
- const menu = this.menuFactory.createContextMenu(menuPath, args);
87
- const { x, y } = coordinateFromAnchor(anchor);
88
- const zoom = electron.webFrame.getZoomFactor();
89
- // x and y values must be Ints or else there is a conversion error
90
- menu.popup({ x: Math.round(x * zoom), y: Math.round(y * zoom) });
91
- // native context menu stops the event loop, so there is no keyboard events
92
- this.context.resetAltPressed();
93
- if (onHide) {
94
- menu.once('menu-will-close', () => onHide());
102
+ protected doRender(options: RenderContextMenuOptions): ContextMenuAccess {
103
+ if (this.useNativeStyle) {
104
+ const { menuPath, anchor, args, onHide } = options;
105
+ const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args);
106
+ const { x, y } = coordinateFromAnchor(anchor);
107
+ const zoom = electron.webFrame.getZoomFactor();
108
+ // x and y values must be Ints or else there is a conversion error
109
+ menu.popup({ x: Math.round(x * zoom), y: Math.round(y * zoom) });
110
+ // native context menu stops the event loop, so there is no keyboard events
111
+ this.context.resetAltPressed();
112
+ if (onHide) {
113
+ menu.once('menu-will-close', () => onHide());
114
+ }
115
+ return new ElectronContextMenuAccess(menu);
116
+ } else {
117
+ return super.doRender(options);
95
118
  }
96
- return new ElectronContextMenuAccess(menu);
97
119
  }
98
120
 
99
121
  }
@@ -24,10 +24,9 @@ import {
24
24
  } from '../../common';
25
25
  import { Keybinding } from '../../common/keybinding';
26
26
  import { PreferenceService, KeybindingRegistry, CommonCommands } from '../../browser';
27
- import { ContextKeyService } from '../../browser/context-key-service';
28
27
  import debounce = require('lodash.debounce');
29
- import { ContextMenuContext } from '../../browser/menu/context-menu-context';
30
28
  import { MAXIMIZED_CLASS } from '../../browser/shell/theia-dock-panel';
29
+ import { BrowserMainMenuFactory } from '../../browser/menu/browser-menu-plugin';
31
30
 
32
31
  /**
33
32
  * Representation of possible electron menu options.
@@ -55,23 +54,18 @@ export type ElectronMenuItemRole = ('undo' | 'redo' | 'cut' | 'copy' | 'paste' |
55
54
  'moveTabToNewWindow' | 'windowMenu');
56
55
 
57
56
  @injectable()
58
- export class ElectronMainMenuFactory {
57
+ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
59
58
 
60
59
  protected _menu: Electron.Menu | undefined;
61
60
  protected _toggledCommands: Set<string> = new Set();
62
61
 
63
- @inject(ContextKeyService)
64
- protected readonly contextKeyService: ContextKeyService;
65
-
66
- @inject(ContextMenuContext)
67
- protected readonly context: ContextMenuContext;
68
-
69
62
  constructor(
70
63
  @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry,
71
64
  @inject(PreferenceService) protected readonly preferencesService: PreferenceService,
72
65
  @inject(MenuModelRegistry) protected readonly menuProvider: MenuModelRegistry,
73
66
  @inject(KeybindingRegistry) protected readonly keybindingRegistry: KeybindingRegistry
74
67
  ) {
68
+ super();
75
69
  preferencesService.onPreferenceChanged(
76
70
  debounce(e => {
77
71
  if (e.preferenceName === 'window.menuBarVisibility') {
@@ -92,15 +86,16 @@ export class ElectronMainMenuFactory {
92
86
 
93
87
  async setMenuBar(): Promise<void> {
94
88
  await this.preferencesService.ready;
95
- const createdMenuBar = this.createMenuBar();
96
89
  if (isOSX) {
90
+ const createdMenuBar = this.createElectronMenuBar();
97
91
  electron.remote.Menu.setApplicationMenu(createdMenuBar);
98
- } else {
92
+ } else if (this.preferencesService.get('window.titleBarStyle') === 'native') {
93
+ const createdMenuBar = this.createElectronMenuBar();
99
94
  electron.remote.getCurrentWindow().setMenu(createdMenuBar);
100
95
  }
101
96
  }
102
97
 
103
- createMenuBar(): Electron.Menu | null {
98
+ createElectronMenuBar(): Electron.Menu | null {
104
99
  const preference = this.preferencesService.get<string>('window.menuBarVisibility') || 'classic';
105
100
  const maxWidget = document.getElementsByClassName(MAXIMIZED_CLASS);
106
101
  if (preference === 'visible' || (preference === 'classic' && maxWidget.length === 0)) {
@@ -118,7 +113,7 @@ export class ElectronMainMenuFactory {
118
113
  return null;
119
114
  }
120
115
 
121
- createContextMenu(menuPath: MenuPath, args?: any[]): Electron.Menu {
116
+ createElectronContextMenu(menuPath: MenuPath, args?: any[]): Electron.Menu {
122
117
  const menuModel = this.menuProvider.getMenu(menuPath);
123
118
  const template = this.fillMenuTemplate([], menuModel, args, { showDisabled: false });
124
119
  return electron.remote.Menu.buildFromTemplate(template);
@@ -221,13 +216,13 @@ export class ElectronMainMenuFactory {
221
216
  this._toggledCommands.add(commandId);
222
217
  }
223
218
  } else {
224
- items.push(...this.handleDefault(menu, args, options));
219
+ items.push(...this.handleElectronDefault(menu, args, options));
225
220
  }
226
221
  }
227
222
  return items;
228
223
  }
229
224
 
230
- protected handleDefault(menuNode: MenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
225
+ protected handleElectronDefault(menuNode: MenuNode, args: any[] = [], options?: ElectronMenuOptions): Electron.MenuItemConstructorOptions[] {
231
226
  return [];
232
227
  }
233
228
 
@@ -18,44 +18,51 @@ import * as electron from '../../../shared/electron';
18
18
  import { inject, injectable } from 'inversify';
19
19
  import {
20
20
  Command, CommandContribution, CommandRegistry,
21
- isOSX, isWindows, MenuModelRegistry, MenuContribution, Disposable
21
+ isOSX, isWindows, MenuModelRegistry, MenuContribution, Disposable, nls
22
22
  } from '../../common';
23
- import { ApplicationShell, KeybindingContribution, KeybindingRegistry, PreferenceScope, PreferenceService } from '../../browser';
24
- import { FrontendApplication, FrontendApplicationContribution, CommonMenus } from '../../browser';
23
+ import {
24
+ ApplicationShell, codicon, ConfirmDialog, KeybindingContribution, KeybindingRegistry,
25
+ PreferenceScope, Widget, FrontendApplication, FrontendApplicationContribution, CommonMenus, CommonCommands, Dialog
26
+ } from '../../browser';
25
27
  import { ElectronMainMenuFactory } from './electron-main-menu-factory';
26
28
  import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state';
29
+ import { FrontendApplicationConfigProvider } from '../../browser/frontend-application-config-provider';
30
+ import { RequestTitleBarStyle, Restart, TitleBarStyleAtStartup, TitleBarStyleChanged } from '../../electron-common/messaging/electron-messages';
27
31
  import { ZoomLevel } from '../window/electron-window-preferences';
32
+ import { BrowserMenuBarContribution } from '../../browser/menu/browser-menu-plugin';
33
+
34
+ import '../../../src/electron-browser/menu/electron-menu-style.css';
28
35
 
29
36
  export namespace ElectronCommands {
30
- export const TOGGLE_DEVELOPER_TOOLS: Command = {
37
+ export const TOGGLE_DEVELOPER_TOOLS = Command.toLocalizedCommand({
31
38
  id: 'theia.toggleDevTools',
32
39
  label: 'Toggle Developer Tools'
33
- };
34
- export const RELOAD: Command = {
40
+ }, 'vscode/developerActions/toggleDevTools');
41
+ export const RELOAD = Command.toLocalizedCommand({
35
42
  id: 'view.reload',
36
43
  label: 'Reload Window'
37
- };
38
- export const ZOOM_IN: Command = {
44
+ }, 'vscode/windowActions/reloadWindow');
45
+ export const ZOOM_IN = Command.toLocalizedCommand({
39
46
  id: 'view.zoomIn',
40
47
  label: 'Zoom In'
41
- };
42
- export const ZOOM_OUT: Command = {
48
+ }, 'vscode/windowActions/zoomIn');
49
+ export const ZOOM_OUT = Command.toLocalizedCommand({
43
50
  id: 'view.zoomOut',
44
51
  label: 'Zoom Out'
45
- };
46
- export const RESET_ZOOM: Command = {
52
+ }, 'vscode/windowActions/zoomOut');
53
+ export const RESET_ZOOM = Command.toLocalizedCommand({
47
54
  id: 'view.resetZoom',
48
55
  label: 'Reset Zoom'
49
- };
50
- export const CLOSE_WINDOW: Command = {
56
+ }, 'vscode/windowActions/zoomReset');
57
+ export const CLOSE_WINDOW = Command.toLocalizedCommand({
51
58
  id: 'close.window',
52
59
  label: 'Close Window'
53
- };
54
- export const TOGGLE_FULL_SCREEN: Command = {
60
+ }, 'vscode/windowActions/close');
61
+ export const TOGGLE_FULL_SCREEN = Command.toLocalizedCommand({
55
62
  id: 'workbench.action.toggleFullScreen',
56
- category: 'View',
63
+ category: CommonCommands.VIEW_CATEGORY,
57
64
  label: 'Toggle Full Screen'
58
- };
65
+ }, 'vscode/windowActions/toggleFullScreen', CommonCommands.VIEW_CATEGORY_KEY);
59
66
  }
60
67
 
61
68
  export namespace ElectronMenus {
@@ -72,38 +79,33 @@ export namespace ElectronMenus {
72
79
  }
73
80
 
74
81
  @injectable()
75
- export class ElectronMenuContribution implements FrontendApplicationContribution, CommandContribution, MenuContribution, KeybindingContribution {
82
+ export class ElectronMenuContribution extends BrowserMenuBarContribution implements FrontendApplicationContribution, CommandContribution, MenuContribution, KeybindingContribution {
76
83
 
77
84
  @inject(FrontendApplicationStateService)
78
85
  protected readonly stateService: FrontendApplicationStateService;
79
86
 
80
- @inject(PreferenceService)
81
- protected readonly preferenceService: PreferenceService;
87
+ protected titleBarStyleChangeFlag = false;
88
+ protected titleBarStyle?: string;
82
89
 
83
90
  constructor(
84
91
  @inject(ElectronMainMenuFactory) protected readonly factory: ElectronMainMenuFactory,
85
92
  @inject(ApplicationShell) protected shell: ApplicationShell
86
- ) { }
93
+ ) {
94
+ super(factory);
95
+ }
87
96
 
88
97
  onStart(app: FrontendApplication): void {
89
- this.hideTopPanel(app);
90
- this.preferenceService.ready.then(() => {
91
- this.setMenu();
92
- electron.remote.getCurrentWindow().setMenuBarVisibility(true);
93
- });
98
+ this.handleTitleBarStyling(app);
94
99
  if (isOSX) {
95
100
  // OSX: Recreate the menus when changing windows.
96
101
  // OSX only has one menu bar for all windows, so we need to swap
97
102
  // between them as the user switches windows.
98
- electron.remote.getCurrentWindow().on('focus', () => this.setMenu());
103
+ electron.remote.getCurrentWindow().on('focus', () => this.setMenu(app));
99
104
  }
100
105
  // Make sure the application menu is complete, once the frontend application is ready.
101
106
  // https://github.com/theia-ide/theia/issues/5100
102
107
  let onStateChange: Disposable | undefined = undefined;
103
108
  const stateServiceListener = (state: FrontendApplicationState) => {
104
- if (state === 'ready') {
105
- this.setMenu();
106
- }
107
109
  if (state === 'closing_window') {
108
110
  if (!!onStateChange) {
109
111
  onStateChange.dispose();
@@ -119,6 +121,30 @@ export class ElectronMenuContribution implements FrontendApplicationContribution
119
121
  });
120
122
  }
121
123
 
124
+ handleTitleBarStyling(app: FrontendApplication): void {
125
+ this.hideTopPanel(app);
126
+ electron.ipcRenderer.on(TitleBarStyleAtStartup, (_event, style: string) => {
127
+ this.titleBarStyle = style;
128
+ this.preferenceService.ready.then(() => {
129
+ this.preferenceService.set('window.titleBarStyle', this.titleBarStyle, PreferenceScope.User);
130
+ });
131
+ });
132
+ electron.ipcRenderer.send(RequestTitleBarStyle);
133
+ this.preferenceService.ready.then(() => {
134
+ this.setMenu(app);
135
+ electron.remote.getCurrentWindow().setMenuBarVisibility(['classic', 'visible'].includes(this.preferenceService.get('window.menuBarVisibility', 'classic')));
136
+ });
137
+ this.preferenceService.onPreferenceChanged(change => {
138
+ if (change.preferenceName === 'window.titleBarStyle') {
139
+ if (this.titleBarStyleChangeFlag && this.titleBarStyle !== change.newValue && electron.remote.getCurrentWindow().isFocused()) {
140
+ electron.ipcRenderer.send(TitleBarStyleChanged, change.newValue);
141
+ this.handleRequiredRestart();
142
+ }
143
+ this.titleBarStyleChangeFlag = true;
144
+ }
145
+ });
146
+ }
147
+
122
148
  handleToggleMaximized(): void {
123
149
  const preference = this.preferenceService.get('window.menuBarVisibility');
124
150
  if (preference === 'classic') {
@@ -127,31 +153,95 @@ export class ElectronMenuContribution implements FrontendApplicationContribution
127
153
  }
128
154
 
129
155
  /**
130
- * Makes the `theia-top-panel` hidden as it is unused for the electron-based application.
156
+ * Hides the `theia-top-panel` depending on the selected `titleBarStyle`.
131
157
  * The `theia-top-panel` is used as the container of the main, application menu-bar for the
132
- * browser. Electron has it's own.
158
+ * browser. Native Electron has it's own.
133
159
  * By default, this method is called on application `onStart`.
134
160
  */
135
161
  protected hideTopPanel(app: FrontendApplication): void {
136
162
  const itr = app.shell.children();
137
163
  let child = itr.next();
138
164
  while (child) {
139
- // Top panel for the menu contribution is not required for Electron.
165
+ // Top panel for the menu contribution is not required for native Electron title bar.
140
166
  if (child.id === 'theia-top-panel') {
141
- child.setHidden(true);
142
- child = undefined;
167
+ child.setHidden(this.titleBarStyle !== 'custom');
168
+ break;
143
169
  } else {
144
170
  child = itr.next();
145
171
  }
146
172
  }
147
173
  }
148
174
 
149
- private setMenu(menu: electron.Menu | null = this.factory.createMenuBar(), electronWindow: electron.BrowserWindow = electron.remote.getCurrentWindow()): void {
175
+ protected setMenu(app: FrontendApplication, electronMenu: electron.Menu | null = this.factory.createElectronMenuBar(),
176
+ electronWindow: electron.BrowserWindow = electron.remote.getCurrentWindow()): void {
150
177
  if (isOSX) {
151
- electron.remote.Menu.setApplicationMenu(menu);
178
+ electron.remote.Menu.setApplicationMenu(electronMenu);
152
179
  } else {
180
+ this.hideTopPanel(app);
181
+ if (this.titleBarStyle === 'custom' && !this.menuBar) {
182
+ this.createCustomTitleBar(app, electronWindow);
183
+ }
153
184
  // Unix/Windows: Set the per-window menus
154
- electronWindow.setMenu(menu);
185
+ electronWindow.setMenu(electronMenu);
186
+ }
187
+ }
188
+
189
+ protected createCustomTitleBar(app: FrontendApplication, electronWindow: electron.BrowserWindow): void {
190
+ const dragPanel = new Widget();
191
+ dragPanel.id = 'theia-drag-panel';
192
+ app.shell.addWidget(dragPanel, { area: 'top' });
193
+ this.appendMenu(app.shell);
194
+ const controls = document.createElement('div');
195
+ controls.id = 'window-controls';
196
+ controls.append(
197
+ this.createControlButton('minimize', () => electronWindow.minimize()),
198
+ this.createControlButton('maximize', () => electronWindow.maximize()),
199
+ this.createControlButton('restore', () => electronWindow.unmaximize()),
200
+ this.createControlButton('close', () => electronWindow.close())
201
+ );
202
+ app.shell.topPanel.node.append(controls);
203
+ this.handleWindowControls(electronWindow);
204
+ }
205
+
206
+ protected handleWindowControls(electronWindow: electron.BrowserWindow): void {
207
+ toggleControlButtons();
208
+ electronWindow.on('maximize', toggleControlButtons);
209
+ electronWindow.on('unmaximize', toggleControlButtons);
210
+
211
+ function toggleControlButtons(): void {
212
+ if (electronWindow.isMaximized()) {
213
+ document.body.classList.add('maximized');
214
+ } else {
215
+ document.body.classList.remove('maximized');
216
+ }
217
+ }
218
+ }
219
+
220
+ protected createControlButton(id: string, handler: () => void): HTMLElement {
221
+ const button = document.createElement('div');
222
+ button.id = `${id}-button`;
223
+ button.className = `control-button ${codicon(`chrome-${id}`)}`;
224
+ button.addEventListener('click', handler);
225
+ return button;
226
+ }
227
+
228
+ protected async handleRequiredRestart(): Promise<void> {
229
+ const msgNode = document.createElement('div');
230
+ const message = document.createElement('p');
231
+ message.textContent = nls.localize('vscode/relauncher.contribution/relaunchSettingMessage', 'A setting has changed that requires a restart to take effect');
232
+ const detail = document.createElement('p');
233
+ detail.textContent = nls.localize('vscode/relauncher.contribution/relaunchSettingDetail',
234
+ 'Press the restart button to restart {0} and enable the setting.', FrontendApplicationConfigProvider.get().applicationName);
235
+ msgNode.append(message, detail);
236
+ const restart = nls.localize('vscode/relauncher.contribution/restart', 'Restart');
237
+ const dialog = new ConfirmDialog({
238
+ title: restart,
239
+ msg: msgNode,
240
+ ok: restart,
241
+ cancel: Dialog.CANCEL
242
+ });
243
+ if (await dialog.open()) {
244
+ electron.ipcRenderer.send(Restart);
155
245
  }
156
246
  }
157
247
 
@@ -0,0 +1,84 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2021 TypeFox and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ #theia-drag-panel {
18
+ position: absolute;
19
+ display: block;
20
+ top: 0;
21
+ left: 0;
22
+ width: 100%;
23
+ height: calc(100% - 4px);
24
+ margin: 4px;
25
+ -webkit-app-region: drag !important;
26
+ }
27
+
28
+ #theia-top-panel > * {
29
+ -webkit-app-region: no-drag;
30
+ }
31
+
32
+ #window-controls {
33
+ display: grid;
34
+ grid-template-columns: repeat(3, 48px);
35
+ position: absolute;
36
+ top: 0;
37
+ right: 0;
38
+ height: 100%;
39
+ }
40
+
41
+ #window-controls .control-button {
42
+ display: flex;
43
+ line-height: 30px;
44
+ justify-content: center;
45
+ align-items: center;
46
+ width: 100%;
47
+ height: 100%;
48
+ }
49
+
50
+ #minimize-button {
51
+ grid-column: 1;
52
+ }
53
+
54
+ #maximize-button, #restore-button {
55
+ grid-column: 2;
56
+ }
57
+
58
+ #close-button {
59
+ grid-column: 3;
60
+ }
61
+
62
+ #window-controls .control-button {
63
+ user-select: none;
64
+ }
65
+
66
+ #window-controls .control-button:hover {
67
+ background: rgba(50%, 50%, 50%, 0.2);
68
+ }
69
+
70
+ #window-controls #close-button:hover {
71
+ background: #E81123;
72
+ }
73
+
74
+ #window-controls #close-button:hover:before {
75
+ color: white;
76
+ }
77
+
78
+ body:not(.maximized) #restore-button {
79
+ display: none;
80
+ }
81
+
82
+ body.maximized #maximize-button {
83
+ display: none;
84
+ }