@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
@@ -15,12 +15,12 @@
15
15
  ********************************************************************************/
16
16
 
17
17
  import { interfaces, injectable, inject, postConstruct } from 'inversify';
18
- import { IIterator, toArray, find, some, every, map } from '@phosphor/algorithm';
18
+ import { IIterator, toArray, find, some, every, map, ArrayExt } from '@phosphor/algorithm';
19
19
  import {
20
20
  Widget, EXPANSION_TOGGLE_CLASS, COLLAPSED_CLASS, CODICON_TREE_ITEM_CLASSES, MessageLoop, Message, SplitPanel,
21
- BaseWidget, addEventListener, SplitLayout, LayoutItem, PanelLayout, addKeyListener, waitForRevealed, UnsafeWidgetUtilities
21
+ BaseWidget, addEventListener, SplitLayout, LayoutItem, PanelLayout, addKeyListener, waitForRevealed, UnsafeWidgetUtilities, DockPanel
22
22
  } from './widgets';
23
- import { Event, Emitter } from '../common/event';
23
+ import { Event as CommonEvent, Emitter } from '../common/event';
24
24
  import { Disposable, DisposableCollection } from '../common/disposable';
25
25
  import { CommandRegistry } from '../common/command';
26
26
  import { MenuModelRegistry, MenuPath, MenuAction } from '../common/menu';
@@ -29,10 +29,15 @@ import { MAIN_AREA_ID, BOTTOM_AREA_ID } from './shell/theia-dock-panel';
29
29
  import { FrontendApplicationStateService } from './frontend-application-state';
30
30
  import { ContextMenuRenderer, Anchor } from './context-menu-renderer';
31
31
  import { parseCssMagnitude } from './browser';
32
- import { WidgetManager } from './widget-manager';
33
32
  import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar, TabBarDelegator, TabBarToolbarItem } from './shell/tab-bar-toolbar';
33
+ import { isEmpty } from '../common';
34
+ import { WidgetManager } from './widget-manager';
34
35
  import { Key } from './keys';
35
36
  import { ProgressBarFactory } from './progress-bar-factory';
37
+ import { Drag, IDragEvent } from '@phosphor/dragdrop';
38
+ import { MimeData } from '@phosphor/coreutils';
39
+ import { ElementExt } from '@phosphor/domutils';
40
+ import { TabBarDecoratorService } from './shell/tab-bar-decorator';
36
41
 
37
42
  export interface ViewContainerTitleOptions {
38
43
  label: string;
@@ -70,6 +75,11 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
70
75
 
71
76
  protected currentPart: ViewContainerPart | undefined;
72
77
 
78
+ /**
79
+ * Disable dragging parts from/to this view container.
80
+ */
81
+ disableDNDBetweenContainers = false;
82
+
73
83
  @inject(FrontendApplicationStateService)
74
84
  protected readonly applicationStateService: FrontendApplicationStateService;
75
85
 
@@ -103,6 +113,12 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
103
113
  @inject(ProgressBarFactory)
104
114
  protected readonly progressBarFactory: ProgressBarFactory;
105
115
 
116
+ @inject(ApplicationShell)
117
+ protected readonly shell: ApplicationShell;
118
+
119
+ @inject(TabBarDecoratorService)
120
+ protected readonly decoratorService: TabBarDecoratorService;
121
+
106
122
  @postConstruct()
107
123
  protected init(): void {
108
124
  this.id = this.options.id;
@@ -150,7 +166,8 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
150
166
  commandId: this.globalHideCommandId,
151
167
  label: 'Hide'
152
168
  }),
153
- this.onDidChangeTrackableWidgetsEmitter
169
+ this.onDidChangeTrackableWidgetsEmitter,
170
+ this.onDidChangeTrackableWidgets(() => this.decoratorService.fireDidChangeDecorations())
154
171
  ]);
155
172
  if (this.options.progressLocationId) {
156
173
  this.toDispose.push(this.progressBarFactory({ container: this.node, insertMode: 'prepend', locationId: this.options.progressLocationId }));
@@ -202,27 +219,46 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
202
219
  this.toDisposeOnUpdateTitle.dispose();
203
220
  this.toDispose.push(this.toDisposeOnUpdateTitle);
204
221
  this.updateTabBarDelegate();
205
- const title = this.titleOptions;
206
- if (!title) {
222
+ let title = Object.assign({}, this.titleOptions);
223
+ if (isEmpty(title)) {
207
224
  return;
208
225
  }
209
226
  const allParts = this.getParts();
210
227
  const visibleParts = allParts.filter(part => !part.isHidden);
211
228
  this.title.label = title.label;
212
- if (visibleParts.length === 1) {
229
+ // If there's only one visible part - inline it's title into the container title except in case the part
230
+ // isn't originally belongs to this container but there are other **original** hidden parts.
231
+ if (visibleParts.length === 1 && (visibleParts[0].originalContainerId === this.id || !this.findOriginalPart())) {
213
232
  const part = visibleParts[0];
214
233
  this.toDisposeOnUpdateTitle.push(part.onTitleChanged(() => this.updateTitle()));
215
234
  const partLabel = part.wrapped.title.label;
235
+ // Change the container title if it contains only one part that originally belongs to another container.
236
+ if (allParts.length === 1 && part.originalContainerId !== this.id && !this.isCurrentTitle(part.originalContainerTitle)) {
237
+ title = Object.assign({}, part.originalContainerTitle);
238
+ this.setTitleOptions(title);
239
+ return;
240
+ }
216
241
  if (partLabel) {
217
- this.title.label += ': ' + partLabel;
242
+ if (this.title.label && this.title.label !== partLabel) {
243
+ this.title.label += ': ' + partLabel;
244
+ } else {
245
+ this.title.label = partLabel;
246
+ }
218
247
  }
219
248
  part.collapsed = false;
220
249
  part.hideTitle();
221
250
  } else {
222
251
  visibleParts.forEach(part => part.showTitle());
252
+ // If at least one part originally belongs to this container the title should return to its original value.
253
+ const originalPart = this.findOriginalPart();
254
+ if (originalPart && !this.isCurrentTitle(originalPart.originalContainerTitle)) {
255
+ title = Object.assign({}, originalPart.originalContainerTitle);
256
+ this.setTitleOptions(title);
257
+ return;
258
+ }
223
259
  }
224
260
  this.updateToolbarItems(allParts);
225
- const caption = title.caption || title.label;
261
+ const caption = title?.caption || title?.label;
226
262
  if (caption) {
227
263
  this.title.caption = caption;
228
264
  if (visibleParts.length === 1) {
@@ -274,6 +310,16 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
274
310
  }
275
311
  }
276
312
 
313
+ protected findOriginalPart(): ViewContainerPart | undefined {
314
+ return this.getParts().find(part => part.originalContainerId === this.id);
315
+ }
316
+
317
+ protected isCurrentTitle(titleOptions: ViewContainerTitleOptions | undefined): boolean {
318
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
319
+ return (!!titleOptions && !!this.titleOptions && Object.keys(titleOptions).every(key => (titleOptions as any)[key] === (this.titleOptions as any)[key]))
320
+ || (!titleOptions && !this.titleOptions);
321
+ }
322
+
277
323
  protected findPartForAnchor(anchor: Anchor): ViewContainerPart | undefined {
278
324
  const element = document.elementFromPoint(anchor.x, anchor.y);
279
325
  if (element instanceof Element) {
@@ -287,22 +333,29 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
287
333
 
288
334
  protected readonly toRemoveWidgets = new Map<string, DisposableCollection>();
289
335
 
290
- addWidget(widget: Widget, options?: ViewContainer.Factory.WidgetOptions): Disposable {
336
+ protected createPartId(widget: Widget): string {
337
+ const description = this.widgetManager.getDescription(widget);
338
+ return widget.id || JSON.stringify(description);
339
+ }
340
+
341
+ addWidget(widget: Widget, options?: ViewContainer.Factory.WidgetOptions, originalContainerId?: string, originalContainerTitle?: ViewContainerTitleOptions): Disposable {
291
342
  const existing = this.toRemoveWidgets.get(widget.id);
292
343
  if (existing) {
293
344
  return existing;
294
345
  }
346
+ const partId = this.createPartId(widget);
347
+ const newPart = this.createPart(widget, partId, originalContainerId || this.id, originalContainerTitle || this.titleOptions, options);
348
+ return this.attachNewPart(newPart);
349
+ }
350
+
351
+ protected attachNewPart(newPart: ViewContainerPart, insertIndex?: number): Disposable {
295
352
  const toRemoveWidget = new DisposableCollection();
296
353
  this.toDispose.push(toRemoveWidget);
297
- this.toRemoveWidgets.set(widget.id, toRemoveWidget);
298
- toRemoveWidget.push(Disposable.create(() => this.toRemoveWidgets.delete(widget.id)));
299
-
300
- const description = this.widgetManager.getDescription(widget);
301
- const partId = description ? JSON.stringify(description) : widget.id;
302
- const newPart = this.createPart(widget, partId, options);
354
+ this.toRemoveWidgets.set(newPart.wrapped.id, toRemoveWidget);
355
+ toRemoveWidget.push(Disposable.create(() => this.toRemoveWidgets.delete(newPart.wrapped.id)));
303
356
  this.registerPart(newPart);
304
- if (newPart.options && newPart.options.order !== undefined) {
305
- const index = this.getParts().findIndex(part => part.options.order === undefined || part.options.order > newPart.options.order!);
357
+ if (insertIndex !== undefined || (newPart.options && newPart.options.order !== undefined)) {
358
+ const index = insertIndex ?? this.getParts().findIndex(part => part.options.order === undefined || part.options.order > newPart.options.order!);
306
359
  if (index >= 0) {
307
360
  this.containerLayout.insertWidget(index, newPart);
308
361
  } else {
@@ -317,10 +370,12 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
317
370
  this.update();
318
371
  this.fireDidChangeTrackableWidgets();
319
372
  toRemoveWidget.pushAll([
320
- newPart,
321
373
  Disposable.create(() => {
374
+ if (newPart.currentViewContainerId === this.id) {
375
+ newPart.dispose();
376
+ }
322
377
  this.unregisterPart(newPart);
323
- if (!newPart.isDisposed) {
378
+ if (!newPart.isDisposed && this.getPartIndex(newPart.id) > -1) {
324
379
  this.containerLayout.removeWidget(newPart);
325
380
  }
326
381
  if (!this.isDisposed) {
@@ -354,8 +409,10 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
354
409
  return toRemoveWidget;
355
410
  }
356
411
 
357
- protected createPart(widget: Widget, partId: string, options?: ViewContainer.Factory.WidgetOptions | undefined): ViewContainerPart {
358
- return new ViewContainerPart(widget, partId, this.id, this.toolbarRegistry, this.toolbarFactory, options);
412
+ protected createPart(widget: Widget, partId: string, originalContainerId: string, originalContainerTitle?: ViewContainerTitleOptions,
413
+ options?: ViewContainer.Factory.WidgetOptions): ViewContainerPart {
414
+
415
+ return new ViewContainerPart(widget, partId, this.id, originalContainerId, originalContainerTitle, this.toolbarRegistry, this.toolbarFactory, options);
359
416
  }
360
417
 
361
418
  removeWidget(widget: Widget): boolean {
@@ -371,6 +428,13 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
371
428
  return this.containerLayout.widgets;
372
429
  }
373
430
 
431
+ protected getPartIndex(partId: string | undefined): number {
432
+ if (partId) {
433
+ return this.getParts().findIndex(part => part.id === partId);
434
+ }
435
+ return -1;
436
+ }
437
+
374
438
  getPartFor(widget: Widget): ViewContainerPart | undefined {
375
439
  return this.getParts().find(p => p.wrapped.id === widget.id);
376
440
  }
@@ -413,7 +477,9 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
413
477
  partId: part.partId,
414
478
  collapsed: part.collapsed,
415
479
  hidden: part.isHidden,
416
- relativeSize: size && availableSize ? size / availableSize : undefined
480
+ relativeSize: size && availableSize ? size / availableSize : undefined,
481
+ originalContainerId: part.originalContainerId,
482
+ originalContainerTitle: part.originalContainerTitle
417
483
  };
418
484
  });
419
485
  return { parts: partStates, title: this.titleOptions };
@@ -428,7 +494,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
428
494
  // restore widgets
429
495
  for (const part of state.parts) {
430
496
  if (part.widget) {
431
- this.addWidget(part.widget);
497
+ this.addWidget(part.widget, undefined, part.originalContainerId, part.originalContainerTitle || {} as ViewContainerTitleOptions);
432
498
  }
433
499
  }
434
500
  const partStates = state.parts.filter(partState => some(this.containerLayout.iter(), p => p.partId === partState.partId));
@@ -436,7 +502,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
436
502
  // Reorder the parts according to the stored state
437
503
  for (let index = 0; index < partStates.length; index++) {
438
504
  const partState = partStates[index];
439
- const currentIndex = this.getParts().findIndex(p => p.partId === partState.partId);
505
+ const currentIndex = this.getParts().findIndex(part => part.partId === partState.partId);
440
506
  if (currentIndex > index) {
441
507
  this.containerLayout.moveWidget(currentIndex, index, this.getParts()[currentIndex]);
442
508
  }
@@ -556,6 +622,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
556
622
  return undefined;
557
623
  }
558
624
  this.updateCurrentPart(part);
625
+ part.collapsed = false;
559
626
  return part.wrapped;
560
627
  }
561
628
 
@@ -600,62 +667,174 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica
600
667
 
601
668
  protected onAfterShow(msg: Message): void {
602
669
  super.onAfterShow(msg);
670
+ this.updateTitle();
603
671
  this.lastVisibleState = undefined;
604
672
  }
605
673
 
606
- protected draggingPart: ViewContainerPart | undefined;
674
+ protected onBeforeAttach(msg: Message): void {
675
+ super.onBeforeAttach(msg);
676
+ this.node.addEventListener('p-dragenter', this, true);
677
+ this.node.addEventListener('p-dragover', this, true);
678
+ this.node.addEventListener('p-dragleave', this, true);
679
+ this.node.addEventListener('p-drop', this, true);
680
+ }
607
681
 
608
- protected registerDND(part: ViewContainerPart): Disposable {
609
- part['header'].draggable = true;
610
- const style = (event: DragEvent) => {
611
- if (!this.draggingPart) {
612
- return;
613
- }
682
+ protected onAfterDetach(msg: Message): void {
683
+ super.onAfterDetach(msg);
684
+ this.node.removeEventListener('p-dragenter', this, true);
685
+ this.node.removeEventListener('p-dragover', this, true);
686
+ this.node.removeEventListener('p-dragleave', this, true);
687
+ this.node.removeEventListener('p-drop', this, true);
688
+ }
689
+
690
+ handleEvent(event: Event): void {
691
+ switch (event.type) {
692
+ case 'p-dragenter':
693
+ this.handleDragEnter(event as IDragEvent);
694
+ break;
695
+ case 'p-dragover':
696
+ this.handleDragOver(event as IDragEvent);
697
+ break;
698
+ case 'p-dragleave':
699
+ this.handleDragLeave(event as IDragEvent);
700
+ break;
701
+ case 'p-drop':
702
+ this.handleDrop(event as IDragEvent);
703
+ break;
704
+ }
705
+ }
706
+
707
+ handleDragEnter(event: IDragEvent): void {
708
+ if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) {
614
709
  event.preventDefault();
615
- const enclosingPartNode = ViewContainerPart.closestPart(event.target);
616
- if (enclosingPartNode && enclosingPartNode !== this.draggingPart.node) {
617
- enclosingPartNode.classList.add('drop-target');
618
- }
619
- };
620
- const unstyle = (event: DragEvent) => {
621
- if (!this.draggingPart) {
622
- return;
710
+ event.stopPropagation();
711
+ }
712
+ }
713
+
714
+ toDisposeOnDragEnd = new DisposableCollection();
715
+ handleDragOver(event: IDragEvent): void {
716
+ const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory');
717
+ const widget = factory && factory();
718
+ if (!(widget instanceof ViewContainerPart)) {
719
+ return;
720
+ }
721
+ event.preventDefault();
722
+ event.stopPropagation();
723
+
724
+ const sameContainers = this.id === widget.currentViewContainerId;
725
+ const targetPart = ArrayExt.findFirstValue(this.getParts(), (p => ElementExt.hitTest(p.node, event.clientX, event.clientY)));
726
+ if (!targetPart && sameContainers) {
727
+ event.dropAction = 'none';
728
+ return;
729
+ }
730
+ if (targetPart) {
731
+ // add overlay class style to the `targetPart` node.
732
+ targetPart.node.classList.add('drop-target');
733
+ this.toDisposeOnDragEnd.push(Disposable.create(() => targetPart.node.classList.remove('drop-target')));
734
+ } else {
735
+ // show panel overlay.
736
+ const dockPanel = this.getDockPanel();
737
+ if (dockPanel) {
738
+ dockPanel.overlay.show({ top: 0, bottom: 0, right: 0, left: 0 });
739
+ this.toDisposeOnDragEnd.push(Disposable.create(() => dockPanel.overlay.hide(100)));
623
740
  }
624
- event.preventDefault();
625
- const enclosingPartNode = ViewContainerPart.closestPart(event.target);
626
- if (enclosingPartNode) {
627
- enclosingPartNode.classList.remove('drop-target');
741
+ }
742
+
743
+ const isDraggingOutsideDisabled = this.disableDNDBetweenContainers || widget.viewContainer?.disableDNDBetweenContainers
744
+ || widget.options.disableDraggingToOtherContainers;
745
+ if (isDraggingOutsideDisabled && !sameContainers) {
746
+ const { target } = event;
747
+ if (target instanceof HTMLElement) {
748
+ target.classList.add('theia-cursor-no-drop');
749
+ this.toDisposeOnDragEnd.push(Disposable.create(() => {
750
+ target.classList.remove('theia-cursor-no-drop');
751
+ }));
628
752
  }
753
+ event.dropAction = 'none';
754
+ return;
629
755
  };
756
+
757
+ event.dropAction = event.proposedAction;
758
+ };
759
+
760
+ handleDragLeave(event: IDragEvent): void {
761
+ this.toDisposeOnDragEnd.dispose();
762
+ if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) {
763
+ event.preventDefault();
764
+ event.stopPropagation();
765
+ }
766
+ };
767
+
768
+ handleDrop(event: IDragEvent): void {
769
+ this.toDisposeOnDragEnd.dispose();
770
+ const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory');
771
+ const draggedPart = factory && factory();
772
+ if (!(draggedPart instanceof ViewContainerPart)) {
773
+ event.dropAction = 'none';
774
+ return;
775
+ }
776
+ event.preventDefault();
777
+ event.stopPropagation();
778
+ const parts = this.getParts();
779
+ const toIndex = ArrayExt.findFirstIndex(parts, part => ElementExt.hitTest(part.node, event.clientX, event.clientY));
780
+ if (draggedPart.currentViewContainerId !== this.id) {
781
+ this.attachNewPart(draggedPart, toIndex > -1 ? toIndex + 1 : toIndex);
782
+ draggedPart.onPartMoved(this);
783
+ } else {
784
+ this.moveBefore(draggedPart.id, parts[toIndex].id);
785
+ }
786
+ event.dropAction = event.proposedAction;
787
+ }
788
+
789
+ protected registerDND(part: ViewContainerPart): Disposable {
790
+ part.headerElement.draggable = true;
791
+
630
792
  return new DisposableCollection(
631
- addEventListener(part['header'], 'dragstart', event => {
632
- const { dataTransfer } = event;
633
- if (dataTransfer) {
634
- this.draggingPart = part;
635
- dataTransfer.effectAllowed = 'move';
636
- dataTransfer.setData('view-container-dnd', part.id);
637
- const dragImage = document.createElement('div');
638
- dragImage.classList.add('theia-view-container-drag-image');
639
- dragImage.innerText = part.wrapped.title.label;
640
- document.body.appendChild(dragImage);
641
- dataTransfer.setDragImage(dragImage, -10, -10);
642
- setTimeout(() => document.body.removeChild(dragImage), 0);
643
- }
644
- }, false),
645
- addEventListener(part.node, 'dragend', () => this.draggingPart = undefined, false),
646
- addEventListener(part.node, 'dragover', style, false),
647
- addEventListener(part.node, 'dragleave', unstyle, false),
648
- addEventListener(part.node, 'drop', event => {
649
- const { dataTransfer } = event;
650
- if (dataTransfer) {
651
- const moveId = dataTransfer.getData('view-container-dnd');
652
- if (moveId && moveId !== part.id) {
653
- this.moveBefore(moveId, part.id);
654
- }
655
- unstyle(event);
656
- }
657
- }, false)
658
- );
793
+ addEventListener(part.headerElement, 'dragstart',
794
+ event => {
795
+ event.preventDefault();
796
+ const mimeData = new MimeData();
797
+ mimeData.setData('application/vnd.phosphor.view-container-factory', () => part);
798
+ const clonedHeader = part.headerElement.cloneNode(true) as HTMLElement;
799
+ clonedHeader.style.width = part.node.style.width;
800
+ clonedHeader.style.opacity = '0.6';
801
+ const drag = new Drag({
802
+ mimeData,
803
+ dragImage: clonedHeader,
804
+ proposedAction: 'move',
805
+ supportedActions: 'move'
806
+ });
807
+ part.node.classList.add('p-mod-hidden');
808
+ drag.start(event.clientX, event.clientY).then(dropAction => {
809
+ // The promise is resolved when the drag has ended
810
+ if (dropAction === 'move' && part.currentViewContainerId !== this.id) {
811
+ this.removeWidget(part.wrapped);
812
+ this.lastVisibleState = this.doStoreState();
813
+ }
814
+ });
815
+ setTimeout(() => { part.node.classList.remove('p-mod-hidden'); }, 0);
816
+ }, false));
817
+ }
818
+
819
+ protected getDockPanel(): DockPanel | undefined {
820
+ let panel: DockPanel | undefined;
821
+ let parent = this.parent;
822
+ while (!panel && parent) {
823
+ if (this.isSideDockPanel(parent)) {
824
+ panel = parent as DockPanel;
825
+ } else {
826
+ parent = parent.parent;
827
+ }
828
+ }
829
+ return panel;
830
+ }
831
+
832
+ protected isSideDockPanel(widget: Widget): boolean {
833
+ const { leftPanelHandler, rightPanelHandler } = this.shell;
834
+ if (widget instanceof DockPanel && (widget.id === rightPanelHandler.dockPanel.id || widget.id === leftPanelHandler.dockPanel.id)) {
835
+ return true;
836
+ }
837
+ return false;
659
838
  }
660
839
 
661
840
  }
@@ -675,6 +854,12 @@ export namespace ViewContainer {
675
854
  readonly initiallyCollapsed?: boolean;
676
855
  readonly canHide?: boolean;
677
856
  readonly initiallyHidden?: boolean;
857
+ /**
858
+ * Disable dragging this part from its original container to other containers,
859
+ * But allow dropping parts from other containers on it,
860
+ * This option only applies to the `ViewContainerPart` and has no effect on the ViewContainer.
861
+ */
862
+ readonly disableDraggingToOtherContainers?: boolean;
678
863
  }
679
864
 
680
865
  export interface WidgetDescriptor {
@@ -715,6 +900,8 @@ export class ViewContainerPart extends BaseWidget {
715
900
  readonly onTitleChanged = this.onTitleChangedEmitter.event;
716
901
  protected readonly onDidFocusEmitter = new Emitter<this>();
717
902
  readonly onDidFocus = this.onDidFocusEmitter.event;
903
+ protected readonly onPartMovedEmitter = new Emitter<ViewContainer>();
904
+ readonly onDidMove = this.onPartMovedEmitter.event;
718
905
  protected readonly onDidChangeDescriptionEmitter = new Emitter<void>();
719
906
  readonly onDidChangeDescription = this.onDidChangeDescriptionEmitter.event;
720
907
 
@@ -730,7 +917,9 @@ export class ViewContainerPart extends BaseWidget {
730
917
  constructor(
731
918
  readonly wrapped: Widget,
732
919
  readonly partId: string,
733
- viewContainerId: string,
920
+ protected currentContainerId: string,
921
+ readonly originalContainerId: string,
922
+ readonly originalContainerTitle: ViewContainerTitleOptions | undefined,
734
923
  protected readonly toolbarRegistry: TabBarToolbarRegistry,
735
924
  protected readonly toolbarFactory: TabBarToolbarFactory,
736
925
  readonly options: ViewContainer.Factory.WidgetOptions = {}
@@ -738,7 +927,7 @@ export class ViewContainerPart extends BaseWidget {
738
927
  super();
739
928
  wrapped.parent = this;
740
929
  wrapped.disposed.connect(() => this.dispose());
741
- this.id = `${viewContainerId}--${wrapped.id}`;
930
+ this.id = `${originalContainerId}--${wrapped.id}`;
742
931
  this.addClass('part');
743
932
 
744
933
  const fireTitleChanged = () => this.onTitleChangedEmitter.fire(undefined);
@@ -781,6 +970,18 @@ export class ViewContainerPart extends BaseWidget {
781
970
  }
782
971
  }
783
972
 
973
+ get viewContainer(): ViewContainer | undefined {
974
+ return this.parent ? this.parent.parent as ViewContainer : undefined;
975
+ }
976
+
977
+ get currentViewContainerId(): string {
978
+ return this.currentContainerId;
979
+ }
980
+
981
+ get headerElement(): HTMLElement {
982
+ return this.header;
983
+ }
984
+
784
985
  get collapsed(): boolean {
785
986
  return this._collapsed;
786
987
  }
@@ -809,6 +1010,11 @@ export class ViewContainerPart extends BaseWidget {
809
1010
  this.collapsedEmitter.fire(collapsed);
810
1011
  }
811
1012
 
1013
+ onPartMoved(newContainer: ViewContainer): void {
1014
+ this.currentContainerId = newContainer.id;
1015
+ this.onPartMovedEmitter.fire(newContainer);
1016
+ }
1017
+
812
1018
  setHidden(hidden: boolean): void {
813
1019
  if (!this.canHide) {
814
1020
  return;
@@ -820,11 +1026,11 @@ export class ViewContainerPart extends BaseWidget {
820
1026
  return this.options.canHide === undefined || this.options.canHide;
821
1027
  }
822
1028
 
823
- get onCollapsed(): Event<boolean> {
1029
+ get onCollapsed(): CommonEvent<boolean> {
824
1030
  return this.collapsedEmitter.event;
825
1031
  }
826
1032
 
827
- get onContextMenu(): Event<MouseEvent> {
1033
+ get onContextMenu(): CommonEvent<MouseEvent> {
828
1034
  return this.contextMenuEmitter.event;
829
1035
  }
830
1036
 
@@ -891,7 +1097,7 @@ export class ViewContainerPart extends BaseWidget {
891
1097
  const disposable = new DisposableCollection();
892
1098
  const header = document.createElement('div');
893
1099
  header.tabIndex = 0;
894
- header.classList.add('theia-header', 'header');
1100
+ header.classList.add('theia-header', 'header', 'theia-view-container-part-header');
895
1101
  disposable.push(addEventListener(header, 'click', event => {
896
1102
  if (this.toolbar && this.toolbar.shouldHandleMouseEvent(event)) {
897
1103
  return;
@@ -915,7 +1121,14 @@ export class ViewContainerPart extends BaseWidget {
915
1121
  const description = document.createElement('span');
916
1122
  description.classList.add('description');
917
1123
 
918
- const updateTitle = () => title.innerText = this.wrapped.title.label;
1124
+ const updateTitle = () => {
1125
+ if (this.currentContainerId !== this.originalContainerId && this.originalContainerTitle?.label) {
1126
+ // Creating a title in format: <original_container_title>: <part_title>.
1127
+ title.innerText = this.originalContainerTitle.label + ': ' + this.wrapped.title.label;
1128
+ } else {
1129
+ title.innerText = this.wrapped.title.label;
1130
+ }
1131
+ };
919
1132
  const updateCaption = () => title.title = this.wrapped.title.caption || this.wrapped.title.label;
920
1133
  const updateDescription = () => {
921
1134
  description.innerText = DescriptionWidget.is(this.wrapped) && !this.collapsed && this.wrapped.description || '';
@@ -928,8 +1141,9 @@ export class ViewContainerPart extends BaseWidget {
928
1141
  disposable.pushAll([
929
1142
  this.onTitleChanged(updateTitle),
930
1143
  this.onTitleChanged(updateCaption),
1144
+ this.onDidMove(updateTitle),
931
1145
  this.onDidChangeDescription(updateDescription),
932
- this.onCollapsed(updateDescription),
1146
+ this.onCollapsed(updateDescription)
933
1147
  ]);
934
1148
  header.appendChild(title);
935
1149
  header.appendChild(description);
@@ -1032,6 +1246,9 @@ export namespace ViewContainerPart {
1032
1246
  hidden: boolean;
1033
1247
  relativeSize?: number;
1034
1248
  description?: string;
1249
+ /** The original container to which this part belongs */
1250
+ originalContainerId: string;
1251
+ originalContainerTitle?: ViewContainerTitleOptions;
1035
1252
  }
1036
1253
 
1037
1254
  export function closestPart(element: Element | EventTarget | null, selector: string = 'div.part'): Element | undefined {
@@ -1064,9 +1281,24 @@ export class ViewContainerLayout extends SplitLayout {
1064
1281
  return toArray(this.iter());
1065
1282
  }
1066
1283
 
1284
+ attachWidget(index: number, widget: ViewContainerPart): void {
1285
+ super.attachWidget(index, widget);
1286
+ if (index > -1 && this.parent && this.parent.node.contains(this.widgets[index + 1]?.node)) {
1287
+ // Set the correct attach index to the DOM elements.
1288
+ const ref = this.widgets[index + 1].node;
1289
+ this.parent.node.insertBefore(widget.node, ref);
1290
+ this.parent.node.insertBefore(this.handles[index], ref);
1291
+ this.parent.fit();
1292
+ }
1293
+ }
1294
+
1067
1295
  moveWidget(fromIndex: number, toIndex: number, widget: Widget): void {
1068
1296
  const ref = this.widgets[toIndex < fromIndex ? toIndex : toIndex + 1];
1069
1297
  super.moveWidget(fromIndex, toIndex, widget);
1298
+ // Keep the order of `_widgets` array just as done before (by `super`) for the `_items` array -
1299
+ // to prevent later bugs relying on index.
1300
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1301
+ ArrayExt.move((this as any)._widgets, fromIndex, toIndex);
1070
1302
  if (ref) {
1071
1303
  this.parent!.node.insertBefore(this.handles[toIndex], ref.node);
1072
1304
  } else {
@@ -20,7 +20,7 @@ import { CorePreferences } from '../core-preferences';
20
20
  import { ContributionProvider } from '../../common/contribution-provider';
21
21
  import { FrontendApplicationContribution, FrontendApplication } from '../frontend-application';
22
22
  import { WindowService } from './window-service';
23
- import { DEFAULT_WINDOW_HASH } from './window-service';
23
+ import { DEFAULT_WINDOW_HASH } from '../../common/window';
24
24
 
25
25
  @injectable()
26
26
  export class DefaultWindowService implements WindowService, FrontendApplicationContribution {
@@ -15,21 +15,13 @@
15
15
  ********************************************************************************/
16
16
 
17
17
  import { Event } from '../../common/event';
18
-
19
- export interface NewWindowOptions {
20
- readonly external?: boolean;
21
- }
18
+ import { NewWindowOptions } from '../../common/window';
22
19
 
23
20
  /**
24
21
  * Service for opening new browser windows.
25
22
  */
26
23
  export const WindowService = Symbol('WindowService');
27
24
 
28
- /**
29
- * The window hash value that is used to spawn a new default window.
30
- */
31
- export const DEFAULT_WINDOW_HASH: string = '!empty';
32
-
33
25
  export interface WindowService {
34
26
 
35
27
  /**