@theia/core 1.28.0-next.9 → 1.28.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 (201) hide show
  1. package/README.md +6 -6
  2. package/i18n/nls.cs.json +33 -2
  3. package/i18n/nls.de.json +33 -2
  4. package/i18n/nls.es.json +33 -2
  5. package/i18n/nls.fr.json +33 -2
  6. package/i18n/nls.hu.json +33 -2
  7. package/i18n/nls.it.json +33 -2
  8. package/i18n/nls.ja.json +33 -2
  9. package/i18n/nls.json +35 -4
  10. package/i18n/nls.pl.json +33 -2
  11. package/i18n/nls.pt-br.json +33 -2
  12. package/i18n/nls.pt-pt.json +33 -2
  13. package/i18n/nls.ru.json +33 -2
  14. package/i18n/nls.zh-cn.json +33 -2
  15. package/lib/browser/common-frontend-contribution.d.ts +1 -1
  16. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  17. package/lib/browser/common-frontend-contribution.js +6 -5
  18. package/lib/browser/common-frontend-contribution.js.map +1 -1
  19. package/lib/browser/context-key-service.d.ts +3 -1
  20. package/lib/browser/context-key-service.d.ts.map +1 -1
  21. package/lib/browser/context-menu-renderer.d.ts +5 -0
  22. package/lib/browser/context-menu-renderer.d.ts.map +1 -1
  23. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  24. package/lib/browser/frontend-application-module.js +2 -0
  25. package/lib/browser/frontend-application-module.js.map +1 -1
  26. package/lib/browser/frontend-application.d.ts +3 -2
  27. package/lib/browser/frontend-application.d.ts.map +1 -1
  28. package/lib/browser/frontend-application.js.map +1 -1
  29. package/lib/browser/keybinding.d.ts.map +1 -1
  30. package/lib/browser/keybinding.js +9 -7
  31. package/lib/browser/keybinding.js.map +1 -1
  32. package/lib/browser/keybinding.spec.js +13 -3
  33. package/lib/browser/keybinding.spec.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-context-menu-renderer.js +2 -2
  37. package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
  38. package/lib/browser/menu/browser-menu-plugin.d.ts +23 -24
  39. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  40. package/lib/browser/menu/browser-menu-plugin.js +62 -79
  41. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  42. package/lib/browser/resource-context-key.d.ts +1 -0
  43. package/lib/browser/resource-context-key.d.ts.map +1 -1
  44. package/lib/browser/resource-context-key.js +8 -6
  45. package/lib/browser/resource-context-key.js.map +1 -1
  46. package/lib/browser/shell/shell-layout-restorer.d.ts.map +1 -1
  47. package/lib/browser/shell/shell-layout-restorer.js +2 -1
  48. package/lib/browser/shell/shell-layout-restorer.js.map +1 -1
  49. package/lib/browser/shell/side-panel-toolbar.d.ts.map +1 -1
  50. package/lib/browser/shell/side-panel-toolbar.js +0 -1
  51. package/lib/browser/shell/side-panel-toolbar.js.map +1 -1
  52. package/lib/browser/shell/tab-bar-toolbar/index.d.ts +4 -0
  53. package/lib/browser/shell/tab-bar-toolbar/index.d.ts.map +1 -0
  54. package/lib/browser/shell/tab-bar-toolbar/index.js +31 -0
  55. package/lib/browser/shell/tab-bar-toolbar/index.js.map +1 -0
  56. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts +16 -0
  57. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.d.ts.map +1 -0
  58. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +36 -0
  59. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -0
  60. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts +51 -0
  61. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.d.ts.map +1 -0
  62. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +173 -0
  63. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -0
  64. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts +119 -0
  65. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.d.ts.map +1 -0
  66. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js +67 -0
  67. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.js.map +1 -0
  68. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +53 -0
  69. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -0
  70. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +264 -0
  71. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -0
  72. package/lib/browser/shell/{tab-bar-toolbar.spec.d.ts → tab-bar-toolbar/tab-bar-toolbar.spec.d.ts} +0 -0
  73. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.d.ts.map +1 -0
  74. package/lib/browser/shell/{tab-bar-toolbar.spec.js → tab-bar-toolbar/tab-bar-toolbar.spec.js} +3 -3
  75. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -0
  76. package/lib/browser/view-container.d.ts.map +1 -1
  77. package/lib/browser/view-container.js +1 -10
  78. package/lib/browser/view-container.js.map +1 -1
  79. package/lib/browser/widget-manager.d.ts.map +1 -1
  80. package/lib/browser/widget-manager.js +2 -1
  81. package/lib/browser/widget-manager.js.map +1 -1
  82. package/lib/browser/widget-manager.spec.js +13 -0
  83. package/lib/browser/widget-manager.spec.js.map +1 -1
  84. package/lib/browser/window/default-window-service.d.ts +2 -1
  85. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  86. package/lib/browser/window/default-window-service.js +9 -4
  87. package/lib/browser/window/default-window-service.js.map +1 -1
  88. package/lib/browser/window/window-service.d.ts +2 -1
  89. package/lib/browser/window/window-service.d.ts.map +1 -1
  90. package/lib/browser/window/window-service.js.map +1 -1
  91. package/lib/common/color.d.ts +3 -0
  92. package/lib/common/color.d.ts.map +1 -1
  93. package/lib/common/frontend-application-state.d.ts +14 -0
  94. package/lib/common/frontend-application-state.d.ts.map +1 -1
  95. package/lib/common/frontend-application-state.js +16 -0
  96. package/lib/common/frontend-application-state.js.map +1 -1
  97. package/lib/common/menu/action-menu-node.d.ts +20 -0
  98. package/lib/common/menu/action-menu-node.d.ts.map +1 -0
  99. package/lib/common/menu/action-menu-node.js +57 -0
  100. package/lib/common/menu/action-menu-node.js.map +1 -0
  101. package/lib/common/menu/composite-menu-node.d.ts +52 -0
  102. package/lib/common/menu/composite-menu-node.d.ts.map +1 -0
  103. package/lib/common/menu/composite-menu-node.js +102 -0
  104. package/lib/common/menu/composite-menu-node.js.map +1 -0
  105. package/lib/common/menu/index.d.ts +6 -0
  106. package/lib/common/menu/index.d.ts.map +1 -0
  107. package/lib/common/menu/index.js +33 -0
  108. package/lib/common/menu/index.js.map +1 -0
  109. package/lib/common/menu/menu-adapter.d.ts +36 -0
  110. package/lib/common/menu/menu-adapter.d.ts.map +1 -0
  111. package/lib/common/menu/menu-adapter.js +101 -0
  112. package/lib/common/menu/menu-adapter.js.map +1 -0
  113. package/lib/common/{menu.d.ts → menu/menu-model-registry.d.ts} +22 -124
  114. package/lib/common/menu/menu-model-registry.d.ts.map +1 -0
  115. package/lib/common/{menu.js → menu/menu-model-registry.js} +71 -147
  116. package/lib/common/menu/menu-model-registry.js.map +1 -0
  117. package/lib/common/menu/menu-types.d.ts +120 -0
  118. package/lib/common/menu/menu-types.d.ts.map +1 -0
  119. package/lib/common/menu/menu-types.js +84 -0
  120. package/lib/common/menu/menu-types.js.map +1 -0
  121. package/lib/common/{menu.spec.d.ts → menu/menu.spec.d.ts} +0 -0
  122. package/lib/common/menu/menu.spec.d.ts.map +1 -0
  123. package/lib/common/{menu.spec.js → menu/menu.spec.js} +3 -3
  124. package/lib/common/menu/menu.spec.js.map +1 -0
  125. package/lib/common/messaging/abstract-connection-provider.d.ts +1 -1
  126. package/lib/common/messaging/abstract-connection-provider.js +3 -3
  127. package/lib/common/quick-pick-service.d.ts +1 -0
  128. package/lib/common/quick-pick-service.d.ts.map +1 -1
  129. package/lib/common/quick-pick-service.js.map +1 -1
  130. package/lib/electron-browser/menu/electron-context-menu-renderer.js +2 -2
  131. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  132. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +14 -5
  133. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  134. package/lib/electron-browser/menu/electron-main-menu-factory.js +71 -82
  135. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  136. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  137. package/lib/electron-browser/menu/electron-menu-contribution.js +6 -0
  138. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  139. package/lib/electron-browser/messaging/electron-ws-connection-provider.js +1 -1
  140. package/lib/electron-browser/window/electron-window-service.js +1 -1
  141. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  142. package/lib/electron-common/messaging/electron-messages.d.ts +3 -14
  143. package/lib/electron-common/messaging/electron-messages.d.ts.map +1 -1
  144. package/lib/electron-common/messaging/electron-messages.js +3 -16
  145. package/lib/electron-common/messaging/electron-messages.js.map +1 -1
  146. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +3 -3
  147. package/lib/electron-main/messaging/electron-messaging-contribution.js +6 -6
  148. package/package.json +7 -6
  149. package/src/browser/common-frontend-contribution.ts +6 -5
  150. package/src/browser/context-key-service.ts +1 -1
  151. package/src/browser/context-menu-renderer.ts +5 -0
  152. package/src/browser/frontend-application-module.ts +7 -1
  153. package/src/browser/frontend-application.ts +3 -2
  154. package/src/browser/keybinding.spec.ts +13 -3
  155. package/src/browser/keybinding.ts +8 -9
  156. package/src/browser/menu/browser-context-menu-renderer.ts +2 -2
  157. package/src/browser/menu/browser-menu-plugin.ts +74 -86
  158. package/src/browser/resource-context-key.ts +9 -6
  159. package/src/browser/shell/shell-layout-restorer.ts +2 -1
  160. package/src/browser/shell/side-panel-toolbar.ts +0 -1
  161. package/src/browser/shell/tab-bar-toolbar/index.ts +19 -0
  162. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts +31 -0
  163. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +170 -0
  164. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +186 -0
  165. package/src/browser/shell/{tab-bar-toolbar.spec.ts → tab-bar-toolbar/tab-bar-toolbar.spec.ts} +2 -2
  166. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +261 -0
  167. package/src/browser/style/alert-messages.css +6 -0
  168. package/src/browser/view-container.ts +1 -10
  169. package/src/browser/widget-manager.spec.ts +14 -0
  170. package/src/browser/widget-manager.ts +2 -1
  171. package/src/browser/window/default-window-service.ts +9 -4
  172. package/src/browser/window/window-service.ts +2 -1
  173. package/src/common/color.ts +3 -0
  174. package/src/common/frontend-application-state.ts +15 -0
  175. package/src/common/menu/action-menu-node.ts +65 -0
  176. package/src/common/menu/composite-menu-node.ts +121 -0
  177. package/src/common/menu/index.ts +21 -0
  178. package/src/common/menu/menu-adapter.ts +103 -0
  179. package/src/common/{menu.ts → menu/menu-model-registry.ts} +61 -222
  180. package/src/common/menu/menu-types.ts +183 -0
  181. package/src/common/{menu.spec.ts → menu/menu.spec.ts} +3 -2
  182. package/src/common/messaging/abstract-connection-provider.ts +4 -4
  183. package/src/common/quick-pick-service.ts +1 -0
  184. package/src/electron-browser/menu/electron-context-menu-renderer.ts +2 -2
  185. package/src/electron-browser/menu/electron-main-menu-factory.ts +82 -96
  186. package/src/electron-browser/menu/electron-menu-contribution.ts +8 -0
  187. package/src/electron-browser/messaging/electron-ws-connection-provider.ts +1 -1
  188. package/src/electron-browser/window/electron-window-service.ts +1 -1
  189. package/src/electron-common/messaging/electron-messages.ts +4 -15
  190. package/src/electron-main/messaging/electron-messaging-contribution.ts +8 -8
  191. package/lib/browser/shell/tab-bar-toolbar.d.ts +0 -186
  192. package/lib/browser/shell/tab-bar-toolbar.d.ts.map +0 -1
  193. package/lib/browser/shell/tab-bar-toolbar.js +0 -362
  194. package/lib/browser/shell/tab-bar-toolbar.js.map +0 -1
  195. package/lib/browser/shell/tab-bar-toolbar.spec.d.ts.map +0 -1
  196. package/lib/browser/shell/tab-bar-toolbar.spec.js.map +0 -1
  197. package/lib/common/menu.d.ts.map +0 -1
  198. package/lib/common/menu.js.map +0 -1
  199. package/lib/common/menu.spec.d.ts.map +0 -1
  200. package/lib/common/menu.spec.js.map +0 -1
  201. package/src/browser/shell/tab-bar-toolbar.tsx +0 -495
@@ -0,0 +1,170 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 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
+ import debounce = require('lodash.debounce');
18
+ import { inject, injectable, named } from 'inversify';
19
+ // eslint-disable-next-line max-len
20
+ import { CommandMenuNode, CommandRegistry, CompoundMenuNode, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuPath } from '../../../common';
21
+ import { ContextKeyService } from '../../context-key-service';
22
+ import { FrontendApplicationContribution } from '../../frontend-application';
23
+ import { Widget } from '../../widgets';
24
+ import { MenuDelegate, ReactTabBarToolbarItem, TabBarToolbarItem } from './tab-bar-toolbar-types';
25
+ import { ToolbarMenuNodeWrapper } from './tab-bar-toolbar-menu-adapters';
26
+
27
+ /**
28
+ * Clients should implement this interface if they want to contribute to the tab-bar toolbar.
29
+ */
30
+ export const TabBarToolbarContribution = Symbol('TabBarToolbarContribution');
31
+ /**
32
+ * Representation of a tabbar toolbar contribution.
33
+ */
34
+ export interface TabBarToolbarContribution {
35
+ /**
36
+ * Registers toolbar items.
37
+ * @param registry the tabbar toolbar registry.
38
+ */
39
+ registerToolbarItems(registry: TabBarToolbarRegistry): void;
40
+ }
41
+
42
+ function yes(): true { return true; }
43
+ const menuDelegateSeparator = '=@=';
44
+
45
+ /**
46
+ * Main, shared registry for tab-bar toolbar items.
47
+ */
48
+ @injectable()
49
+ export class TabBarToolbarRegistry implements FrontendApplicationContribution {
50
+
51
+ protected items = new Map<string, TabBarToolbarItem | ReactTabBarToolbarItem>();
52
+ protected menuDelegates = new Map<string, MenuDelegate>();
53
+
54
+ @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
55
+ @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
56
+ @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry;
57
+
58
+ @inject(ContributionProvider) @named(TabBarToolbarContribution)
59
+ protected readonly contributionProvider: ContributionProvider<TabBarToolbarContribution>;
60
+
61
+ protected readonly onDidChangeEmitter = new Emitter<void>();
62
+ readonly onDidChange: Event<void> = this.onDidChangeEmitter.event;
63
+ // debounce in order to avoid to fire more than once in the same tick
64
+ protected fireOnDidChange = debounce(() => this.onDidChangeEmitter.fire(undefined), 0);
65
+
66
+ onStart(): void {
67
+ const contributions = this.contributionProvider.getContributions();
68
+ for (const contribution of contributions) {
69
+ contribution.registerToolbarItems(this);
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Registers the given item. Throws an error, if the corresponding command cannot be found or an item has been already registered for the desired command.
75
+ *
76
+ * @param item the item to register.
77
+ */
78
+ registerItem(item: TabBarToolbarItem | ReactTabBarToolbarItem): Disposable {
79
+ const { id } = item;
80
+ if (this.items.has(id)) {
81
+ throw new Error(`A toolbar item is already registered with the '${id}' ID.`);
82
+ }
83
+ this.items.set(id, item);
84
+ this.fireOnDidChange();
85
+ const toDispose = new DisposableCollection(
86
+ Disposable.create(() => this.fireOnDidChange()),
87
+ Disposable.create(() => this.items.delete(id))
88
+ );
89
+ if (item.onDidChange) {
90
+ toDispose.push(item.onDidChange(() => this.fireOnDidChange()));
91
+ }
92
+ return toDispose;
93
+ }
94
+
95
+ /**
96
+ * Returns an array of tab-bar toolbar items which are visible when the `widget` argument is the current one.
97
+ *
98
+ * By default returns with all items where the command is enabled and `item.isVisible` is `true`.
99
+ */
100
+ visibleItems(widget: Widget): Array<TabBarToolbarItem | ReactTabBarToolbarItem> {
101
+ if (widget.isDisposed) {
102
+ return [];
103
+ }
104
+ const result: Array<TabBarToolbarItem | ReactTabBarToolbarItem> = [];
105
+ for (const item of this.items.values()) {
106
+ const visible = TabBarToolbarItem.is(item)
107
+ ? this.commandRegistry.isVisible(item.command, widget)
108
+ : (!item.isVisible || item.isVisible(widget));
109
+ if (visible && (!item.when || this.contextKeyService.match(item.when, widget.node))) {
110
+ result.push(item);
111
+ }
112
+ }
113
+ for (const delegate of this.menuDelegates.values()) {
114
+ if (delegate.isVisible(widget)) {
115
+ const menu = this.menuRegistry.getMenu(delegate.menuPath);
116
+ const children = CompoundMenuNode.getFlatChildren(menu.children);
117
+ for (const child of children) {
118
+ if (!child.when || this.contextKeyService.match(child.when, widget.node)) {
119
+ if (child.children) {
120
+ for (const grandchild of child.children) {
121
+ if (!grandchild.when || this.contextKeyService.match(grandchild.when, widget.node)) {
122
+ if (CommandMenuNode.is(grandchild)) {
123
+ result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, delegate.menuPath));
124
+ } else if (CompoundMenuNode.is(grandchild)) {
125
+ let menuPath;
126
+ if (menuPath = this.menuRegistry.getPath(grandchild)) {
127
+ result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, menuPath));
128
+ }
129
+ }
130
+ }
131
+ }
132
+ } else if (child.command) {
133
+ result.push(new ToolbarMenuNodeWrapper(child, '', delegate.menuPath));
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+
142
+ unregisterItem(itemOrId: TabBarToolbarItem | ReactTabBarToolbarItem | string): void {
143
+ const id = typeof itemOrId === 'string' ? itemOrId : itemOrId.id;
144
+ if (this.items.delete(id)) {
145
+ this.fireOnDidChange();
146
+ }
147
+ }
148
+
149
+ registerMenuDelegate(menuPath: MenuPath, when?: string | ((widget: Widget) => boolean)): Disposable {
150
+ const id = menuPath.join(menuDelegateSeparator);
151
+ if (!this.menuDelegates.has(id)) {
152
+ const isVisible: MenuDelegate['isVisible'] = !when
153
+ ? yes
154
+ : typeof when === 'function'
155
+ ? when
156
+ : widget => this.contextKeyService.match(when, widget?.node);
157
+ this.menuDelegates.set(id, { menuPath, isVisible });
158
+ this.fireOnDidChange();
159
+ return { dispose: () => this.unregisterMenuDelegate(menuPath) };
160
+ }
161
+ console.warn('Unable to register menu delegate. Delegate has already been registered', menuPath);
162
+ return Disposable.NULL;
163
+ }
164
+
165
+ unregisterMenuDelegate(menuPath: MenuPath): void {
166
+ if (this.menuDelegates.delete(menuPath.join(menuDelegateSeparator))) {
167
+ this.fireOnDidChange();
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,186 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 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
+ import * as React from 'react';
18
+ import { ArrayUtils, Event, MenuPath } from '../../../common';
19
+ import { Widget } from '../../widgets';
20
+
21
+ /** Items whose group is exactly 'navigation' will be rendered inline. */
22
+ export const NAVIGATION = 'navigation';
23
+ export const TAB_BAR_TOOLBAR_CONTEXT_MENU = ['TAB_BAR_TOOLBAR_CONTEXT_MENU'];
24
+
25
+ export interface TabBarDelegator extends Widget {
26
+ getTabBarDelegate(): Widget | undefined;
27
+ }
28
+
29
+ export namespace TabBarDelegator {
30
+ export const is = (candidate?: Widget): candidate is TabBarDelegator => {
31
+ if (candidate) {
32
+ const asDelegator = candidate as TabBarDelegator;
33
+ return typeof asDelegator.getTabBarDelegate === 'function';
34
+ }
35
+ return false;
36
+ };
37
+ }
38
+
39
+ interface RegisteredToolbarItem {
40
+ /**
41
+ * The unique ID of the toolbar item.
42
+ */
43
+ id: string;
44
+ }
45
+
46
+ interface RenderedToolbarItem {
47
+ /**
48
+ * Optional icon for the item.
49
+ */
50
+ icon?: string | (() => string);
51
+
52
+ /**
53
+ * Optional text of the item.
54
+ *
55
+ * Strings in the format `$(iconIdentifier~animationType) will be treated as icon references.
56
+ * If the iconIdentifier begins with fa-, Font Awesome icons will be used; otherwise it will be treated as Codicon name.
57
+ *
58
+ * You can find Codicon classnames here: https://microsoft.github.io/vscode-codicons/dist/codicon.html
59
+ * You can find Font Awesome classnames here: http://fontawesome.io/icons/
60
+ * The type of animation can be either `spin` or `pulse`.
61
+ */
62
+ text?: string;
63
+
64
+ /**
65
+ * Optional tooltip for the item.
66
+ */
67
+ tooltip?: string;
68
+ }
69
+
70
+ interface SelfRenderingToolbarItem {
71
+ render(widget?: Widget): React.ReactNode;
72
+ }
73
+
74
+ interface ExecutableToolbarItem {
75
+ /**
76
+ * The command to execute when the item is selected.
77
+ */
78
+ command: string;
79
+ }
80
+
81
+ export interface MenuToolbarItem {
82
+ /**
83
+ * A menu path with which this item is associated.
84
+ * If accompanied by a command, this data will be passed to the {@link MenuCommandExecutor}.
85
+ * If no command is present, this menu will be opened.
86
+ */
87
+ menuPath: MenuPath;
88
+ }
89
+
90
+ interface ConditionalToolbarItem {
91
+ /**
92
+ * https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts
93
+ */
94
+ when?: string;
95
+ /**
96
+ * Checked before the item is shown.
97
+ */
98
+ isVisible?(widget?: Widget): boolean;
99
+ /**
100
+ * When defined, the container tool-bar will be updated if this event is fired.
101
+ *
102
+ * Note: currently, each item of the container toolbar will be re-rendered if any of the items have changed.
103
+ */
104
+ onDidChange?: Event<void>;
105
+ }
106
+
107
+ interface InlineToolbarItemMetadata {
108
+ /**
109
+ * Priority among the items. Can be negative. The smaller the number the left-most the item will be placed in the toolbar. It is `0` by default.
110
+ */
111
+ priority?: number;
112
+ group: 'navigation' | undefined;
113
+ }
114
+
115
+ interface MenuToolbarItemMetadata {
116
+ /**
117
+ * Optional group for the item. Default `navigation`.
118
+ * `navigation` group will be inlined, while all the others will appear in the `...` dropdown.
119
+ * A group in format `submenu_group_1/submenu 1/.../submenu_group_n/ submenu n/item_group` means that the item will be located in a submenu(s) of the `...` dropdown.
120
+ * The submenu's title is named by the submenu section name, e.g. `group/<submenu name>/subgroup`.
121
+ */
122
+ group: string;
123
+ /**
124
+ * Optional ordering string for placing the item within its group
125
+ */
126
+ order?: string;
127
+ }
128
+
129
+ /**
130
+ * Representation of an item in the tab
131
+ */
132
+ export interface TabBarToolbarItem extends RegisteredToolbarItem,
133
+ ExecutableToolbarItem,
134
+ RenderedToolbarItem,
135
+ Omit<ConditionalToolbarItem, 'isVisible'>,
136
+ Pick<InlineToolbarItemMetadata, 'priority'>,
137
+ Partial<MenuToolbarItemMetadata> { }
138
+
139
+ /**
140
+ * Tab-bar toolbar item backed by a `React.ReactNode`.
141
+ * Unlike the `TabBarToolbarItem`, this item is not connected to the command service.
142
+ */
143
+ export interface ReactTabBarToolbarItem extends RegisteredToolbarItem,
144
+ SelfRenderingToolbarItem,
145
+ ConditionalToolbarItem,
146
+ Pick<InlineToolbarItemMetadata, 'priority'>,
147
+ Pick<Partial<MenuToolbarItemMetadata>, 'group'> { }
148
+
149
+ export interface AnyToolbarItem extends RegisteredToolbarItem,
150
+ Partial<ExecutableToolbarItem>,
151
+ Partial<RenderedToolbarItem>,
152
+ Partial<SelfRenderingToolbarItem>,
153
+ Partial<ConditionalToolbarItem>,
154
+ Partial<MenuToolbarItem>,
155
+ Pick<InlineToolbarItemMetadata, 'priority'>,
156
+ Partial<MenuToolbarItemMetadata> { }
157
+
158
+ export interface MenuDelegate extends MenuToolbarItem, Required<Pick<ConditionalToolbarItem, 'isVisible'>> { }
159
+
160
+ export namespace TabBarToolbarItem {
161
+
162
+ /**
163
+ * Compares the items by `priority` in ascending. Undefined priorities will be treated as `0`.
164
+ */
165
+ export const PRIORITY_COMPARATOR = (left: TabBarToolbarItem, right: TabBarToolbarItem) => {
166
+ const leftGroup = left.group ?? NAVIGATION;
167
+ const rightGroup = right.group ?? NAVIGATION;
168
+ if (leftGroup === NAVIGATION && rightGroup !== NAVIGATION) { return ArrayUtils.Sort.LeftBeforeRight; }
169
+ if (rightGroup === NAVIGATION && leftGroup !== NAVIGATION) { return ArrayUtils.Sort.RightBeforeLeft; }
170
+ if (leftGroup !== rightGroup) { return leftGroup.localeCompare(rightGroup); }
171
+ return (left.priority || 0) - (right.priority || 0);
172
+ };
173
+
174
+ export function is(arg: Object | undefined): arg is TabBarToolbarItem {
175
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
+ return !!arg && 'command' in arg && typeof (arg as any).command === 'string';
177
+ }
178
+
179
+ }
180
+
181
+ export namespace MenuToolbarItem {
182
+ export function getMenuPath(item: AnyToolbarItem): MenuPath | undefined {
183
+ const asDelegate = item as MenuToolbarItem;
184
+ return Array.isArray(asDelegate.menuPath) ? asDelegate.menuPath : undefined;
185
+ }
186
+ }
@@ -14,11 +14,11 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { enableJSDOM } from '../test/jsdom';
17
+ import { enableJSDOM } from '../../test/jsdom';
18
18
 
19
19
  let disableJSDOM = enableJSDOM();
20
20
  import { expect } from 'chai';
21
- import { TabBarToolbarItem } from './tab-bar-toolbar';
21
+ import { TabBarToolbarItem } from './tab-bar-toolbar-types';
22
22
 
23
23
  disableJSDOM();
24
24
 
@@ -0,0 +1,261 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 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 { inject, injectable } from 'inversify';
18
+ import * as React from 'react';
19
+ import { CommandRegistry, CompoundMenuNodeRole, Disposable, DisposableCollection, MenuCommandExecutor, MenuModelRegistry, MenuPath, nls } from '../../../common';
20
+ import { Anchor, ContextMenuAccess, ContextMenuRenderer } from '../../context-menu-renderer';
21
+ import { LabelIcon, LabelParser } from '../../label-parser';
22
+ import { ACTION_ITEM, codicon, ReactWidget, Widget } from '../../widgets';
23
+ import { TabBarToolbarRegistry } from './tab-bar-toolbar-registry';
24
+ import { AnyToolbarItem, ReactTabBarToolbarItem, TabBarDelegator, TabBarToolbarItem, TAB_BAR_TOOLBAR_CONTEXT_MENU } from './tab-bar-toolbar-types';
25
+
26
+ /**
27
+ * Factory for instantiating tab-bar toolbars.
28
+ */
29
+ export const TabBarToolbarFactory = Symbol('TabBarToolbarFactory');
30
+ export interface TabBarToolbarFactory {
31
+ (): TabBarToolbar;
32
+ }
33
+
34
+ /**
35
+ * Tab-bar toolbar widget representing the active [tab-bar toolbar items](TabBarToolbarItem).
36
+ */
37
+ @injectable()
38
+ export class TabBarToolbar extends ReactWidget {
39
+
40
+ protected current: Widget | undefined;
41
+ protected inline = new Map<string, TabBarToolbarItem | ReactTabBarToolbarItem>();
42
+ protected more = new Map<string, TabBarToolbarItem>();
43
+
44
+ @inject(CommandRegistry) protected readonly commands: CommandRegistry;
45
+ @inject(LabelParser) protected readonly labelParser: LabelParser;
46
+ @inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry;
47
+ @inject(MenuCommandExecutor) protected readonly menuCommandExecutor: MenuCommandExecutor;
48
+ @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
49
+ @inject(TabBarToolbarRegistry) protected readonly toolbarRegistry: TabBarToolbarRegistry;
50
+
51
+ constructor() {
52
+ super();
53
+ this.addClass(TabBarToolbar.Styles.TAB_BAR_TOOLBAR);
54
+ this.hide();
55
+ }
56
+
57
+ updateItems(items: Array<TabBarToolbarItem | ReactTabBarToolbarItem>, current: Widget | undefined): void {
58
+ this.inline.clear();
59
+ this.more.clear();
60
+ for (const item of items.sort(TabBarToolbarItem.PRIORITY_COMPARATOR).reverse()) {
61
+ if ('render' in item || item.group === undefined || item.group === 'navigation') {
62
+ this.inline.set(item.id, item);
63
+ } else {
64
+ this.more.set(item.id, item);
65
+ }
66
+ }
67
+ this.setCurrent(current);
68
+ if (!items.length) {
69
+ this.hide();
70
+ }
71
+ this.onRender.push(Disposable.create(() => {
72
+ if (items.length) {
73
+ this.show();
74
+ }
75
+ }));
76
+ this.update();
77
+ }
78
+
79
+ updateTarget(current?: Widget): void {
80
+ const operativeWidget = TabBarDelegator.is(current) ? current.getTabBarDelegate() : current;
81
+ const items = operativeWidget ? this.toolbarRegistry.visibleItems(operativeWidget) : [];
82
+ this.updateItems(items, operativeWidget);
83
+ }
84
+
85
+ protected readonly toDisposeOnSetCurrent = new DisposableCollection();
86
+ protected setCurrent(current: Widget | undefined): void {
87
+ this.toDisposeOnSetCurrent.dispose();
88
+ this.toDispose.push(this.toDisposeOnSetCurrent);
89
+ this.current = current;
90
+ if (current) {
91
+ const resetCurrent = () => {
92
+ this.setCurrent(undefined);
93
+ this.update();
94
+ };
95
+ current.disposed.connect(resetCurrent);
96
+ this.toDisposeOnSetCurrent.push(Disposable.create(() =>
97
+ current.disposed.disconnect(resetCurrent)
98
+ ));
99
+ }
100
+ }
101
+
102
+ protected render(): React.ReactNode {
103
+ return <React.Fragment>
104
+ {this.renderMore()}
105
+ {[...this.inline.values()].map(item => TabBarToolbarItem.is(item) ? this.renderItem(item) : item.render(this.current))}
106
+ </React.Fragment>;
107
+ }
108
+
109
+ protected renderItem(item: AnyToolbarItem): React.ReactNode {
110
+ let innerText = '';
111
+ const classNames = [];
112
+ if (item.text) {
113
+ for (const labelPart of this.labelParser.parse(item.text)) {
114
+ if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) {
115
+ const className = `fa fa-${labelPart.name}${labelPart.animation ? ' fa-' + labelPart.animation : ''}`;
116
+ classNames.push(...className.split(' '));
117
+ } else {
118
+ innerText = labelPart;
119
+ }
120
+ }
121
+ }
122
+ const command = item.command ? this.commands.getCommand(item.command) : undefined;
123
+ let iconClass = (typeof item.icon === 'function' && item.icon()) || item.icon as string || (command && command.iconClass);
124
+ if (iconClass) {
125
+ iconClass += ` ${ACTION_ITEM}`;
126
+ classNames.push(iconClass);
127
+ }
128
+ const tooltip = item.tooltip || (command && command.label);
129
+ const toolbarItemClassNames = this.getToolbarItemClassNames(command?.id ?? item.command);
130
+ if (item.menuPath && !item.command) { toolbarItemClassNames.push('enabled'); }
131
+ return <div key={item.id}
132
+ className={toolbarItemClassNames.join(' ')}
133
+ onMouseDown={this.onMouseDownEvent}
134
+ onMouseUp={this.onMouseUpEvent}
135
+ onMouseOut={this.onMouseUpEvent} >
136
+ <div id={item.id} className={classNames.join(' ')}
137
+ onClick={this.executeCommand}
138
+ title={tooltip}>{innerText}
139
+ </div>
140
+ </div>;
141
+ }
142
+
143
+ protected getToolbarItemClassNames(commandId: string | undefined): string[] {
144
+ const classNames = [TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM];
145
+ if (commandId) {
146
+ if (this.commandIsEnabled(commandId)) {
147
+ classNames.push('enabled');
148
+ }
149
+ if (this.commandIsToggled(commandId)) {
150
+ classNames.push('toggled');
151
+ }
152
+ }
153
+ return classNames;
154
+ }
155
+
156
+ protected renderMore(): React.ReactNode {
157
+ return !!this.more.size && <div key='__more__' className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled'}>
158
+ <div id='__more__' className={codicon('ellipsis', true)} onClick={this.showMoreContextMenu}
159
+ title={nls.localizeByDefault('More Actions...')} />
160
+ </div>;
161
+ }
162
+
163
+ protected showMoreContextMenu = (event: React.MouseEvent) => {
164
+ event.stopPropagation();
165
+ event.preventDefault();
166
+ const anchor = this.toAnchor(event);
167
+ this.renderMoreContextMenu(anchor);
168
+ };
169
+
170
+ protected toAnchor(event: React.MouseEvent): Anchor {
171
+ const itemBox = event.currentTarget.closest('.' + TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM)?.getBoundingClientRect();
172
+ return itemBox ? { y: itemBox.bottom, x: itemBox.left } : event.nativeEvent;
173
+ }
174
+
175
+ renderMoreContextMenu(anchor: Anchor, subpath?: MenuPath): ContextMenuAccess {
176
+ const toDisposeOnHide = new DisposableCollection();
177
+ this.addClass('menu-open');
178
+ toDisposeOnHide.push(Disposable.create(() => this.removeClass('menu-open')));
179
+ if (subpath) {
180
+ toDisposeOnHide.push(this.menus.linkSubmenu(TAB_BAR_TOOLBAR_CONTEXT_MENU, subpath, { role: CompoundMenuNodeRole.Flat, when: '' }));
181
+ } else {
182
+ for (const item of this.more.values() as IterableIterator<AnyToolbarItem>) {
183
+ if (item.menuPath && !item.command) {
184
+ toDisposeOnHide.push(this.menus.linkSubmenu(TAB_BAR_TOOLBAR_CONTEXT_MENU, item.menuPath, { role: CompoundMenuNodeRole.Flat, when: '' }, item.group));
185
+ } else if (item.command) {
186
+ // Register a submenu for the item, if the group is in format `<submenu group>/<submenu name>/.../<item group>`
187
+ if (item.group?.includes('/')) {
188
+ const split = item.group.split('/');
189
+ const paths: string[] = [];
190
+ for (let i = 0; i < split.length - 1; i += 2) {
191
+ paths.push(split[i], split[i + 1]);
192
+ toDisposeOnHide.push(this.menus.registerSubmenu([...TAB_BAR_TOOLBAR_CONTEXT_MENU, ...paths], split[i + 1], { order: item.order }));
193
+ }
194
+ }
195
+ toDisposeOnHide.push(this.menus.registerMenuAction([...TAB_BAR_TOOLBAR_CONTEXT_MENU, ...item.group!.split('/')], {
196
+ label: item.tooltip,
197
+ commandId: item.command,
198
+ when: item.when,
199
+ order: item.order,
200
+ }));
201
+ }
202
+ }
203
+ }
204
+ return this.contextMenuRenderer.render({
205
+ menuPath: TAB_BAR_TOOLBAR_CONTEXT_MENU,
206
+ args: [this.current],
207
+ anchor,
208
+ context: this.current?.node,
209
+ onHide: () => toDisposeOnHide.dispose()
210
+ });
211
+ }
212
+
213
+ shouldHandleMouseEvent(event: MouseEvent): boolean {
214
+ return event.target instanceof Element && this.node.contains(event.target);
215
+ }
216
+
217
+ protected commandIsEnabled(command: string): boolean {
218
+ return this.commands.isEnabled(command, this.current);
219
+ }
220
+
221
+ protected commandIsToggled(command: string): boolean {
222
+ return this.commands.isToggled(command, this.current);
223
+ }
224
+
225
+ protected executeCommand = (e: React.MouseEvent<HTMLElement>) => {
226
+ e.preventDefault();
227
+ e.stopPropagation();
228
+
229
+ const item: AnyToolbarItem | undefined = this.inline.get(e.currentTarget.id);
230
+ if (item?.command && item.menuPath) {
231
+ this.menuCommandExecutor.executeCommand(item.menuPath, item.command, this.current);
232
+ } else if (item?.command) {
233
+ this.commands.executeCommand(item.command, this.current);
234
+ } else if (item?.menuPath) {
235
+ this.renderMoreContextMenu(this.toAnchor(e), item.menuPath);
236
+ }
237
+ this.update();
238
+ };
239
+
240
+ protected onMouseDownEvent = (e: React.MouseEvent<HTMLElement>) => {
241
+ if (e.button === 0) {
242
+ e.currentTarget.classList.add('active');
243
+ }
244
+ };
245
+
246
+ protected onMouseUpEvent = (e: React.MouseEvent<HTMLElement>) => {
247
+ e.currentTarget.classList.remove('active');
248
+ };
249
+
250
+ }
251
+
252
+ export namespace TabBarToolbar {
253
+
254
+ export namespace Styles {
255
+
256
+ export const TAB_BAR_TOOLBAR = 'p-TabBar-toolbar';
257
+ export const TAB_BAR_TOOLBAR_ITEM = 'item';
258
+
259
+ }
260
+
261
+ }
@@ -25,6 +25,12 @@
25
25
  padding-right: 3px;
26
26
  }
27
27
 
28
+ .theia-alert-message-container .theia-message-header,
29
+ .theia-alert-message-container .theia-message-content {
30
+ text-overflow: ellipsis;
31
+ overflow: hidden;
32
+ }
33
+
28
34
  /* information message */
29
35
  .theia-info-alert {
30
36
  background-color: var(--theia-inputValidation-infoBackground);
@@ -282,16 +282,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
282
282
  }
283
283
  }
284
284
  this.updateToolbarItems(allParts);
285
- const caption = title?.caption || title?.label;
286
- if (caption) {
287
- this.title.caption = caption;
288
- if (visibleParts.length === 1) {
289
- const partCaption = visibleParts[0].wrapped.title.caption || visibleParts[0].wrapped.title.label;
290
- if (partCaption) {
291
- this.title.caption += ': ' + partCaption;
292
- }
293
- }
294
- }
285
+ this.title.caption = title?.caption || title?.label;
295
286
  if (title.iconClass) {
296
287
  this.title.iconClass = title.iconClass;
297
288
  }
@@ -63,4 +63,18 @@ describe('widget-manager', () => {
63
63
  expect(wA).equals(await widgetManager.getOrCreateWidget('test', 'widgetA'));
64
64
  });
65
65
 
66
+ it('produces the same widget key regardless of object key order', () => {
67
+ const options1 = {
68
+ factoryId: 'a',
69
+ key1: 1,
70
+ key2: 2,
71
+ };
72
+ const options2 = {
73
+ key2: 2,
74
+ key1: 1,
75
+ factoryId: 'a',
76
+ };
77
+ expect(widgetManager['toKey'](options1)).equals(widgetManager['toKey'](options2));
78
+ });
79
+
66
80
  });