UNPKG

62.5 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2018-2019 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17var ViewContainer_1;
18Object.defineProperty(exports, "__esModule", { value: true });
19exports.ViewContainerLayout = exports.ViewContainerPart = exports.ViewContainer = exports.DynamicToolbarWidget = exports.BadgeWidget = exports.DescriptionWidget = exports.ViewContainerIdentifier = void 0;
20const tslib_1 = require("tslib");
21const inversify_1 = require("inversify");
22const algorithm_1 = require("@phosphor/algorithm");
23const widgets_1 = require("./widgets");
24const event_1 = require("../common/event");
25const disposable_1 = require("../common/disposable");
26const command_1 = require("../common/command");
27const menu_1 = require("../common/menu");
28const shell_1 = require("./shell");
29const theia_dock_panel_1 = require("./shell/theia-dock-panel");
30const frontend_application_state_1 = require("./frontend-application-state");
31const context_menu_renderer_1 = require("./context-menu-renderer");
32const browser_1 = require("./browser");
33const tab_bar_toolbar_1 = require("./shell/tab-bar-toolbar");
34const common_1 = require("../common");
35const widget_manager_1 = require("./widget-manager");
36const keys_1 = require("./keys");
37const progress_bar_factory_1 = require("./progress-bar-factory");
38const dragdrop_1 = require("@phosphor/dragdrop");
39const coreutils_1 = require("@phosphor/coreutils");
40const domutils_1 = require("@phosphor/domutils");
41const tab_bar_decorator_1 = require("./shell/tab-bar-decorator");
42let ViewContainerIdentifier = class ViewContainerIdentifier {
43};
44ViewContainerIdentifier = (0, tslib_1.__decorate)([
45 (0, inversify_1.injectable)()
46], ViewContainerIdentifier);
47exports.ViewContainerIdentifier = ViewContainerIdentifier;
48var DescriptionWidget;
49(function (DescriptionWidget) {
50 function is(arg) {
51 return (0, common_1.isObject)(arg) && 'onDidChangeDescription' in arg;
52 }
53 DescriptionWidget.is = is;
54})(DescriptionWidget = exports.DescriptionWidget || (exports.DescriptionWidget = {}));
55var BadgeWidget;
56(function (BadgeWidget) {
57 function is(arg) {
58 return (0, common_1.isObject)(arg) && 'onDidChangeBadge' in arg && 'onDidChangeBadgeTooltip' in arg;
59 }
60 BadgeWidget.is = is;
61})(BadgeWidget = exports.BadgeWidget || (exports.BadgeWidget = {}));
62var DynamicToolbarWidget;
63(function (DynamicToolbarWidget) {
64 function is(arg) {
65 return (0, common_1.isObject)(arg) && 'onDidChangeToolbarItems' in arg;
66 }
67 DynamicToolbarWidget.is = is;
68})(DynamicToolbarWidget = exports.DynamicToolbarWidget || (exports.DynamicToolbarWidget = {}));
69/**
70 * A view container holds an arbitrary number of widgets inside a split panel.
71 * Each widget is wrapped in a _part_ that displays the widget title and toolbar
72 * and allows to collapse / expand the widget content.
73 */
74let ViewContainer = ViewContainer_1 = class ViewContainer extends widgets_1.BaseWidget {
75 constructor() {
76 super(...arguments);
77 /**
78 * Disable dragging parts from/to this view container.
79 */
80 this.disableDNDBetweenContainers = false;
81 this.onDidChangeTrackableWidgetsEmitter = new event_1.Emitter();
82 this.onDidChangeTrackableWidgets = this.onDidChangeTrackableWidgetsEmitter.event;
83 this.toDisposeOnCurrentPart = new disposable_1.DisposableCollection();
84 this.toDisposeOnUpdateTitle = new disposable_1.DisposableCollection();
85 this._tabBarDelegate = this;
86 this.toRemoveWidgets = new Map();
87 this.toDisposeOnDragEnd = new disposable_1.DisposableCollection();
88 }
89 init() {
90 this.id = this.options.id;
91 this.addClass('theia-view-container');
92 const layout = new widgets_1.PanelLayout();
93 this.layout = layout;
94 this.panel = new widgets_1.SplitPanel({
95 layout: new ViewContainerLayout({
96 renderer: widgets_1.SplitPanel.defaultRenderer,
97 orientation: this.orientation,
98 spacing: 2,
99 headerSize: ViewContainerPart.HEADER_HEIGHT,
100 animationDuration: 200
101 }, this.splitPositionHandler)
102 });
103 this.panel.node.tabIndex = -1;
104 this.configureLayout(layout);
105 const { commandRegistry, menuRegistry, contextMenuRenderer } = this;
106 this.toDispose.pushAll([
107 (0, widgets_1.addEventListener)(this.node, 'contextmenu', event => {
108 if (event.button === 2 && (0, algorithm_1.every)(this.containerLayout.iter(), part => !!part.isHidden)) {
109 event.stopPropagation();
110 event.preventDefault();
111 contextMenuRenderer.render({ menuPath: this.contextMenuPath, anchor: event });
112 }
113 }),
114 commandRegistry.registerCommand({ id: this.globalHideCommandId }, {
115 execute: (anchor) => {
116 const toHide = this.findPartForAnchor(anchor);
117 if (toHide && toHide.canHide) {
118 toHide.hide();
119 }
120 },
121 isVisible: (anchor) => {
122 const toHide = this.findPartForAnchor(anchor);
123 if (toHide) {
124 return toHide.canHide && !toHide.isHidden;
125 }
126 else {
127 return (0, algorithm_1.some)(this.containerLayout.iter(), part => !part.isHidden);
128 }
129 }
130 }),
131 menuRegistry.registerMenuAction([...this.contextMenuPath, '0_global'], {
132 commandId: this.globalHideCommandId,
133 label: common_1.nls.localizeByDefault('Hide')
134 }),
135 this.onDidChangeTrackableWidgetsEmitter,
136 this.onDidChangeTrackableWidgets(() => this.decoratorService.fireDidChangeDecorations())
137 ]);
138 if (this.options.progressLocationId) {
139 this.toDispose.push(this.progressBarFactory({ container: this.node, insertMode: 'prepend', locationId: this.options.progressLocationId }));
140 }
141 }
142 configureLayout(layout) {
143 layout.addWidget(this.panel);
144 }
145 updateCurrentPart(part) {
146 if (part && this.getParts().indexOf(part) !== -1) {
147 this.currentPart = part;
148 }
149 if (this.currentPart && !this.currentPart.isDisposed) {
150 return;
151 }
152 const visibleParts = this.getParts().filter(p => !p.isHidden);
153 const expandedParts = visibleParts.filter(p => !p.collapsed);
154 this.currentPart = expandedParts[0] || visibleParts[0];
155 }
156 updateSplitterVisibility() {
157 const className = 'p-first-visible';
158 let firstFound = false;
159 for (const part of this.getParts()) {
160 if (!part.isHidden && !firstFound) {
161 part.addClass(className);
162 firstFound = true;
163 }
164 else {
165 part.removeClass(className);
166 }
167 }
168 }
169 setTitleOptions(titleOptions) {
170 this.titleOptions = titleOptions;
171 this.updateTitle();
172 }
173 updateTabBarDelegate() {
174 const visibleParts = this.getParts().filter(part => !part.isHidden);
175 if (visibleParts.length === 1) {
176 this._tabBarDelegate = visibleParts[0].wrapped;
177 }
178 else {
179 this._tabBarDelegate = this;
180 }
181 }
182 getTabBarDelegate() {
183 return this._tabBarDelegate;
184 }
185 updateTitle() {
186 var _a;
187 this.toDisposeOnUpdateTitle.dispose();
188 this.toDispose.push(this.toDisposeOnUpdateTitle);
189 this.updateTabBarDelegate();
190 let title = Object.assign({}, this.titleOptions);
191 if ((0, common_1.isEmpty)(title)) {
192 return;
193 }
194 const allParts = this.getParts();
195 const visibleParts = allParts.filter(part => !part.isHidden);
196 this.title.label = title.label;
197 // If there's only one visible part - inline it's title into the container title except in case the part
198 // isn't originally belongs to this container but there are other **original** hidden parts.
199 if (visibleParts.length === 1 && (visibleParts[0].originalContainerId === this.id || !this.findOriginalPart())) {
200 const part = visibleParts[0];
201 this.toDisposeOnUpdateTitle.push(part.onTitleChanged(() => this.updateTitle()));
202 const partLabel = part.wrapped.title.label;
203 // Change the container title if it contains only one part that originally belongs to another container.
204 if (allParts.length === 1 && part.originalContainerId !== this.id && !this.isCurrentTitle(part.originalContainerTitle)) {
205 title = Object.assign({}, part.originalContainerTitle);
206 this.setTitleOptions(title);
207 return;
208 }
209 if (partLabel) {
210 if (this.title.label && this.title.label !== partLabel) {
211 this.title.label += ': ' + partLabel;
212 }
213 else {
214 this.title.label = partLabel;
215 }
216 }
217 part.collapsed = false;
218 part.hideTitle();
219 }
220 else {
221 visibleParts.forEach(part => part.showTitle());
222 // If at least one part originally belongs to this container the title should return to its original value.
223 const originalPart = this.findOriginalPart();
224 if (originalPart && !this.isCurrentTitle(originalPart.originalContainerTitle)) {
225 title = Object.assign({}, originalPart.originalContainerTitle);
226 this.setTitleOptions(title);
227 return;
228 }
229 }
230 this.updateToolbarItems(allParts);
231 this.title.caption = (title === null || title === void 0 ? void 0 : title.caption) || (title === null || title === void 0 ? void 0 : title.label);
232 if (title.iconClass) {
233 this.title.iconClass = title.iconClass;
234 }
235 if (this.title.className.includes(widgets_1.PINNED_CLASS)) {
236 (_a = this.title).closable && (_a.closable = false);
237 }
238 else if (title.closeable !== undefined) {
239 this.title.closable = title.closeable;
240 }
241 }
242 updateToolbarItems(allParts) {
243 if (allParts.length > 1) {
244 const group = this.getToggleVisibilityGroupLabel();
245 for (const part of allParts) {
246 const existingId = this.toggleVisibilityCommandId(part);
247 const { caption, label, dataset: { visibilityCommandLabel } } = part.wrapped.title;
248 this.registerToolbarItem(existingId, { tooltip: visibilityCommandLabel || caption || label, group });
249 }
250 }
251 }
252 getToggleVisibilityGroupLabel() {
253 return 'view';
254 }
255 registerToolbarItem(commandId, options) {
256 const newId = `${this.id}-tabbar-toolbar-${commandId}`;
257 const existingHandler = this.commandRegistry.getAllHandlers(commandId)[0];
258 const existingCommand = this.commandRegistry.getCommand(commandId);
259 if (existingHandler && existingCommand) {
260 this.toDisposeOnUpdateTitle.push(this.commandRegistry.registerCommand({ ...existingCommand, id: newId }, {
261 execute: (_widget, ...args) => this.commandRegistry.executeCommand(commandId, ...args),
262 isToggled: (_widget, ...args) => this.commandRegistry.isToggled(commandId, ...args),
263 isEnabled: (_widget, ...args) => this.commandRegistry.isEnabled(commandId, ...args),
264 isVisible: (widget, ...args) => widget === this.getTabBarDelegate() && this.commandRegistry.isVisible(commandId, ...args),
265 }));
266 this.toDisposeOnUpdateTitle.push(this.toolbarRegistry.registerItem({
267 ...options,
268 id: newId,
269 command: newId,
270 }));
271 }
272 }
273 findOriginalPart() {
274 return this.getParts().find(part => part.originalContainerId === this.id);
275 }
276 isCurrentTitle(titleOptions) {
277 // eslint-disable-next-line @typescript-eslint/no-explicit-any
278 return (!!titleOptions && !!this.titleOptions && Object.keys(titleOptions).every(key => titleOptions[key] === this.titleOptions[key]))
279 || (!titleOptions && !this.titleOptions);
280 }
281 findPartForAnchor(anchor) {
282 const element = document.elementFromPoint(anchor.x, anchor.y);
283 if (element instanceof Element) {
284 const closestPart = ViewContainerPart.closestPart(element);
285 if (closestPart && closestPart.id) {
286 return (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === closestPart.id);
287 }
288 }
289 return undefined;
290 }
291 createPartId(widget) {
292 const description = this.widgetManager.getDescription(widget);
293 return widget.id || JSON.stringify(description);
294 }
295 addWidget(widget, options, originalContainerId, originalContainerTitle) {
296 const existing = this.toRemoveWidgets.get(widget.id);
297 if (existing) {
298 return existing;
299 }
300 const partId = this.createPartId(widget);
301 const newPart = this.createPart(widget, partId, originalContainerId || this.id, originalContainerTitle || this.titleOptions, options);
302 return this.attachNewPart(newPart);
303 }
304 attachNewPart(newPart, insertIndex) {
305 const toRemoveWidget = new disposable_1.DisposableCollection();
306 this.toDispose.push(toRemoveWidget);
307 this.toRemoveWidgets.set(newPart.wrapped.id, toRemoveWidget);
308 toRemoveWidget.push(disposable_1.Disposable.create(() => this.toRemoveWidgets.delete(newPart.wrapped.id)));
309 this.registerPart(newPart);
310 if (insertIndex !== undefined || (newPart.options && newPart.options.order !== undefined)) {
311 const index = insertIndex !== null && insertIndex !== void 0 ? insertIndex : this.getParts().findIndex(part => part.options.order === undefined || part.options.order > newPart.options.order);
312 if (index >= 0) {
313 this.containerLayout.insertWidget(index, newPart);
314 }
315 else {
316 this.containerLayout.addWidget(newPart);
317 }
318 }
319 else {
320 this.containerLayout.addWidget(newPart);
321 }
322 this.refreshMenu(newPart);
323 this.updateTitle();
324 this.updateCurrentPart();
325 this.updateSplitterVisibility();
326 this.update();
327 this.fireDidChangeTrackableWidgets();
328 toRemoveWidget.pushAll([
329 disposable_1.Disposable.create(() => {
330 if (newPart.currentViewContainerId === this.id) {
331 newPart.dispose();
332 }
333 this.unregisterPart(newPart);
334 if (!newPart.isDisposed && this.getPartIndex(newPart.id) > -1) {
335 this.containerLayout.removeWidget(newPart);
336 }
337 if (!this.isDisposed) {
338 this.update();
339 this.updateTitle();
340 this.updateCurrentPart();
341 this.updateSplitterVisibility();
342 this.fireDidChangeTrackableWidgets();
343 }
344 }),
345 this.registerDND(newPart),
346 newPart.onDidChangeVisibility(() => {
347 this.updateTitle();
348 this.updateCurrentPart();
349 this.updateSplitterVisibility();
350 this.containerLayout.updateSashes();
351 }),
352 newPart.onCollapsed(() => {
353 this.containerLayout.updateCollapsed(newPart, this.enableAnimation);
354 this.containerLayout.updateSashes();
355 this.updateCurrentPart();
356 }),
357 newPart.onContextMenu(event => {
358 if (event.button === 2) {
359 event.preventDefault();
360 event.stopPropagation();
361 this.contextMenuRenderer.render({ menuPath: this.contextMenuPath, anchor: event });
362 }
363 }),
364 newPart.onTitleChanged(() => this.refreshMenu(newPart)),
365 newPart.onDidFocus(() => this.updateCurrentPart(newPart))
366 ]);
367 newPart.disposed.connect(() => toRemoveWidget.dispose());
368 return toRemoveWidget;
369 }
370 createPart(widget, partId, originalContainerId, originalContainerTitle, options) {
371 return new ViewContainerPart(widget, partId, this.id, originalContainerId, originalContainerTitle, this.toolbarRegistry, this.toolbarFactory, options);
372 }
373 removeWidget(widget) {
374 const disposable = this.toRemoveWidgets.get(widget.id);
375 if (disposable) {
376 disposable.dispose();
377 return true;
378 }
379 return false;
380 }
381 getParts() {
382 return this.containerLayout.widgets;
383 }
384 getPartIndex(partId) {
385 if (partId) {
386 return this.getParts().findIndex(part => part.id === partId);
387 }
388 return -1;
389 }
390 getPartFor(widget) {
391 return this.getParts().find(p => p.wrapped.id === widget.id);
392 }
393 get containerLayout() {
394 const layout = this.panel.layout;
395 if (layout instanceof ViewContainerLayout) {
396 return layout;
397 }
398 throw new Error('view container is disposed');
399 }
400 get orientation() {
401 return ViewContainer_1.getOrientation(this.node);
402 }
403 get enableAnimation() {
404 return this.applicationStateService.state === 'ready';
405 }
406 storeState() {
407 if (!this.isVisible && this.lastVisibleState) {
408 return this.lastVisibleState;
409 }
410 return this.doStoreState();
411 }
412 doStoreState() {
413 const parts = this.getParts();
414 const availableSize = this.containerLayout.getAvailableSize();
415 const orientation = this.orientation;
416 const partStates = parts.map(part => {
417 let size = this.containerLayout.getPartSize(part);
418 if (size && size > ViewContainerPart.HEADER_HEIGHT && orientation === 'vertical') {
419 size -= ViewContainerPart.HEADER_HEIGHT;
420 }
421 return {
422 widget: part.wrapped,
423 partId: part.partId,
424 collapsed: part.collapsed,
425 hidden: part.isHidden,
426 relativeSize: size && availableSize ? size / availableSize : undefined,
427 originalContainerId: part.originalContainerId,
428 originalContainerTitle: part.originalContainerTitle
429 };
430 });
431 return { parts: partStates, title: this.titleOptions };
432 }
433 restoreState(state) {
434 this.lastVisibleState = state;
435 this.doRestoreState(state);
436 }
437 doRestoreState(state) {
438 this.setTitleOptions(state.title);
439 // restore widgets
440 for (const part of state.parts) {
441 if (part.widget) {
442 this.addWidget(part.widget, undefined, part.originalContainerId, part.originalContainerTitle || {});
443 }
444 }
445 const partStates = state.parts.filter(partState => (0, algorithm_1.some)(this.containerLayout.iter(), p => p.partId === partState.partId));
446 // Reorder the parts according to the stored state
447 for (let index = 0; index < partStates.length; index++) {
448 const partState = partStates[index];
449 const widget = this.getParts().find(part => part.partId === partState.partId);
450 if (widget) {
451 this.containerLayout.insertWidget(index, widget);
452 }
453 }
454 // Restore visibility and collapsed state
455 const parts = this.getParts();
456 for (let index = 0; index < parts.length; index++) {
457 const part = parts[index];
458 const partState = partStates.find(s => part.partId === s.partId);
459 if (partState) {
460 part.setHidden(partState.hidden);
461 part.collapsed = partState.collapsed || !partState.relativeSize;
462 }
463 else if (part.canHide) {
464 part.hide();
465 }
466 this.refreshMenu(part);
467 }
468 // Restore part sizes
469 (0, widgets_1.waitForRevealed)(this).then(() => {
470 this.containerLayout.setPartSizes(partStates.map(partState => partState.relativeSize));
471 this.updateSplitterVisibility();
472 });
473 }
474 /**
475 * Register a command to toggle the visibility of the new part.
476 */
477 registerPart(toRegister) {
478 const commandId = this.toggleVisibilityCommandId(toRegister);
479 this.commandRegistry.registerCommand({ id: commandId }, {
480 execute: () => {
481 const toHide = (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === toRegister.id);
482 if (toHide) {
483 toHide.setHidden(!toHide.isHidden);
484 }
485 },
486 isToggled: () => {
487 if (!toRegister.canHide) {
488 return true;
489 }
490 const widgetToToggle = (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === toRegister.id);
491 if (widgetToToggle) {
492 return !widgetToToggle.isHidden;
493 }
494 return false;
495 },
496 isEnabled: arg => toRegister.canHide && (!this.titleOptions || !(arg instanceof widgets_1.Widget) || (arg instanceof ViewContainer_1 && arg.id === this.id)),
497 isVisible: arg => !this.titleOptions || !(arg instanceof widgets_1.Widget) || (arg instanceof ViewContainer_1 && arg.id === this.id)
498 });
499 }
500 /**
501 * Register a menu action to toggle the visibility of the new part.
502 * The menu action is unregistered first to enable refreshing the order of menu actions.
503 */
504 refreshMenu(part) {
505 const commandId = this.toggleVisibilityCommandId(part);
506 this.menuRegistry.unregisterMenuAction(commandId);
507 if (!part.wrapped.title.label) {
508 return;
509 }
510 const { dataset: { visibilityCommandLabel }, caption, label } = part.wrapped.title;
511 const action = {
512 commandId: commandId,
513 label: visibilityCommandLabel || caption || label,
514 order: this.getParts().indexOf(part).toString()
515 };
516 this.menuRegistry.registerMenuAction([...this.contextMenuPath, '1_widgets'], action);
517 if (this.titleOptions) {
518 this.menuRegistry.registerMenuAction([...shell_1.SIDE_PANEL_TOOLBAR_CONTEXT_MENU, 'navigation'], action);
519 }
520 }
521 unregisterPart(part) {
522 const commandId = this.toggleVisibilityCommandId(part);
523 this.commandRegistry.unregisterCommand(commandId);
524 this.menuRegistry.unregisterMenuAction(commandId);
525 }
526 get contextMenuPath() {
527 return [`${this.id}-context-menu`];
528 }
529 toggleVisibilityCommandId(part) {
530 return `${this.id}:toggle-visibility-${part.id}`;
531 }
532 get globalHideCommandId() {
533 return `${this.id}:toggle-visibility`;
534 }
535 moveBefore(toMovedId, moveBeforeThisId) {
536 const parts = this.getParts();
537 const indexToMove = parts.findIndex(part => part.id === toMovedId);
538 const targetIndex = parts.findIndex(part => part.id === moveBeforeThisId);
539 if (indexToMove >= 0 && targetIndex >= 0) {
540 this.containerLayout.insertWidget(targetIndex, parts[indexToMove]);
541 for (let index = Math.min(indexToMove, targetIndex); index < parts.length; index++) {
542 this.refreshMenu(parts[index]);
543 this.activate();
544 }
545 }
546 this.updateSplitterVisibility();
547 }
548 getTrackableWidgets() {
549 return this.getParts().map(w => w.wrapped);
550 }
551 fireDidChangeTrackableWidgets() {
552 this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
553 }
554 activateWidget(id) {
555 const part = this.revealPart(id);
556 if (!part) {
557 return undefined;
558 }
559 this.updateCurrentPart(part);
560 part.collapsed = false;
561 return part.wrapped;
562 }
563 revealWidget(id) {
564 const part = this.revealPart(id);
565 return part && part.wrapped;
566 }
567 revealPart(id) {
568 const part = this.getParts().find(p => p.wrapped.id === id);
569 if (!part) {
570 return undefined;
571 }
572 part.setHidden(false);
573 return part;
574 }
575 onActivateRequest(msg) {
576 super.onActivateRequest(msg);
577 if (this.currentPart) {
578 this.currentPart.activate();
579 }
580 else {
581 this.panel.node.focus({ preventScroll: true });
582 }
583 }
584 onAfterAttach(msg) {
585 const orientation = this.orientation;
586 this.containerLayout.orientation = orientation;
587 if (orientation === 'horizontal') {
588 for (const part of this.getParts()) {
589 part.collapsed = false;
590 }
591 }
592 super.onAfterAttach(msg);
593 }
594 onBeforeHide(msg) {
595 super.onBeforeHide(msg);
596 this.lastVisibleState = this.storeState();
597 }
598 onAfterShow(msg) {
599 super.onAfterShow(msg);
600 this.updateTitle();
601 this.lastVisibleState = undefined;
602 }
603 onBeforeAttach(msg) {
604 super.onBeforeAttach(msg);
605 this.node.addEventListener('p-dragenter', this, true);
606 this.node.addEventListener('p-dragover', this, true);
607 this.node.addEventListener('p-dragleave', this, true);
608 this.node.addEventListener('p-drop', this, true);
609 }
610 onAfterDetach(msg) {
611 super.onAfterDetach(msg);
612 this.node.removeEventListener('p-dragenter', this, true);
613 this.node.removeEventListener('p-dragover', this, true);
614 this.node.removeEventListener('p-dragleave', this, true);
615 this.node.removeEventListener('p-drop', this, true);
616 }
617 handleEvent(event) {
618 switch (event.type) {
619 case 'p-dragenter':
620 this.handleDragEnter(event);
621 break;
622 case 'p-dragover':
623 this.handleDragOver(event);
624 break;
625 case 'p-dragleave':
626 this.handleDragLeave(event);
627 break;
628 case 'p-drop':
629 this.handleDrop(event);
630 break;
631 }
632 }
633 handleDragEnter(event) {
634 if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) {
635 event.preventDefault();
636 event.stopPropagation();
637 }
638 }
639 handleDragOver(event) {
640 var _a;
641 const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory');
642 const widget = factory && factory();
643 if (!(widget instanceof ViewContainerPart)) {
644 return;
645 }
646 event.preventDefault();
647 event.stopPropagation();
648 const sameContainers = this.id === widget.currentViewContainerId;
649 const targetPart = algorithm_1.ArrayExt.findFirstValue(this.getParts(), (p => domutils_1.ElementExt.hitTest(p.node, event.clientX, event.clientY)));
650 if (!targetPart && sameContainers) {
651 event.dropAction = 'none';
652 return;
653 }
654 if (targetPart) {
655 // add overlay class style to the `targetPart` node.
656 targetPart.node.classList.add('drop-target');
657 this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => targetPart.node.classList.remove('drop-target')));
658 }
659 else {
660 // show panel overlay.
661 const dockPanel = this.getDockPanel();
662 if (dockPanel) {
663 dockPanel.overlay.show({ top: 0, bottom: 0, right: 0, left: 0 });
664 this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => dockPanel.overlay.hide(100)));
665 }
666 }
667 const isDraggingOutsideDisabled = this.disableDNDBetweenContainers || ((_a = widget.viewContainer) === null || _a === void 0 ? void 0 : _a.disableDNDBetweenContainers)
668 || widget.options.disableDraggingToOtherContainers;
669 if (isDraggingOutsideDisabled && !sameContainers) {
670 const { target } = event;
671 if (target instanceof HTMLElement) {
672 target.classList.add('theia-cursor-no-drop');
673 this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => {
674 target.classList.remove('theia-cursor-no-drop');
675 }));
676 }
677 event.dropAction = 'none';
678 return;
679 }
680 ;
681 event.dropAction = event.proposedAction;
682 }
683 ;
684 handleDragLeave(event) {
685 this.toDisposeOnDragEnd.dispose();
686 if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) {
687 event.preventDefault();
688 event.stopPropagation();
689 }
690 }
691 ;
692 handleDrop(event) {
693 this.toDisposeOnDragEnd.dispose();
694 const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory');
695 const draggedPart = factory && factory();
696 if (!(draggedPart instanceof ViewContainerPart)) {
697 event.dropAction = 'none';
698 return;
699 }
700 event.preventDefault();
701 event.stopPropagation();
702 const parts = this.getParts();
703 const toIndex = algorithm_1.ArrayExt.findFirstIndex(parts, part => domutils_1.ElementExt.hitTest(part.node, event.clientX, event.clientY));
704 if (draggedPart.currentViewContainerId !== this.id) {
705 this.attachNewPart(draggedPart, toIndex > -1 ? toIndex + 1 : toIndex);
706 draggedPart.onPartMoved(this);
707 }
708 else {
709 this.moveBefore(draggedPart.id, parts[toIndex].id);
710 }
711 event.dropAction = event.proposedAction;
712 }
713 registerDND(part) {
714 part.headerElement.draggable = true;
715 return new disposable_1.DisposableCollection((0, widgets_1.addEventListener)(part.headerElement, 'dragstart', event => {
716 event.preventDefault();
717 const mimeData = new coreutils_1.MimeData();
718 mimeData.setData('application/vnd.phosphor.view-container-factory', () => part);
719 const clonedHeader = part.headerElement.cloneNode(true);
720 clonedHeader.style.width = part.node.style.width;
721 clonedHeader.style.opacity = '0.6';
722 const drag = new dragdrop_1.Drag({
723 mimeData,
724 dragImage: clonedHeader,
725 proposedAction: 'move',
726 supportedActions: 'move'
727 });
728 part.node.classList.add('p-mod-hidden');
729 drag.start(event.clientX, event.clientY).then(dropAction => {
730 // The promise is resolved when the drag has ended
731 if (dropAction === 'move' && part.currentViewContainerId !== this.id) {
732 this.removeWidget(part.wrapped);
733 this.lastVisibleState = this.doStoreState();
734 }
735 });
736 setTimeout(() => { part.node.classList.remove('p-mod-hidden'); }, 0);
737 }, false));
738 }
739 getDockPanel() {
740 let panel;
741 let parent = this.parent;
742 while (!panel && parent) {
743 if (this.isSideDockPanel(parent)) {
744 panel = parent;
745 }
746 else {
747 parent = parent.parent;
748 }
749 }
750 return panel;
751 }
752 isSideDockPanel(widget) {
753 const { leftPanelHandler, rightPanelHandler } = this.shell;
754 if (widget instanceof widgets_1.DockPanel && (widget.id === rightPanelHandler.dockPanel.id || widget.id === leftPanelHandler.dockPanel.id)) {
755 return true;
756 }
757 return false;
758 }
759};
760(0, tslib_1.__decorate)([
761 (0, inversify_1.inject)(frontend_application_state_1.FrontendApplicationStateService),
762 (0, tslib_1.__metadata)("design:type", frontend_application_state_1.FrontendApplicationStateService)
763], ViewContainer.prototype, "applicationStateService", void 0);
764(0, tslib_1.__decorate)([
765 (0, inversify_1.inject)(context_menu_renderer_1.ContextMenuRenderer),
766 (0, tslib_1.__metadata)("design:type", context_menu_renderer_1.ContextMenuRenderer)
767], ViewContainer.prototype, "contextMenuRenderer", void 0);
768(0, tslib_1.__decorate)([
769 (0, inversify_1.inject)(command_1.CommandRegistry),
770 (0, tslib_1.__metadata)("design:type", command_1.CommandRegistry)
771], ViewContainer.prototype, "commandRegistry", void 0);
772(0, tslib_1.__decorate)([
773 (0, inversify_1.inject)(menu_1.MenuModelRegistry),
774 (0, tslib_1.__metadata)("design:type", menu_1.MenuModelRegistry)
775], ViewContainer.prototype, "menuRegistry", void 0);
776(0, tslib_1.__decorate)([
777 (0, inversify_1.inject)(widget_manager_1.WidgetManager),
778 (0, tslib_1.__metadata)("design:type", widget_manager_1.WidgetManager)
779], ViewContainer.prototype, "widgetManager", void 0);
780(0, tslib_1.__decorate)([
781 (0, inversify_1.inject)(shell_1.SplitPositionHandler),
782 (0, tslib_1.__metadata)("design:type", shell_1.SplitPositionHandler)
783], ViewContainer.prototype, "splitPositionHandler", void 0);
784(0, tslib_1.__decorate)([
785 (0, inversify_1.inject)(ViewContainerIdentifier),
786 (0, tslib_1.__metadata)("design:type", ViewContainerIdentifier)
787], ViewContainer.prototype, "options", void 0);
788(0, tslib_1.__decorate)([
789 (0, inversify_1.inject)(tab_bar_toolbar_1.TabBarToolbarRegistry),
790 (0, tslib_1.__metadata)("design:type", tab_bar_toolbar_1.TabBarToolbarRegistry)
791], ViewContainer.prototype, "toolbarRegistry", void 0);
792(0, tslib_1.__decorate)([
793 (0, inversify_1.inject)(tab_bar_toolbar_1.TabBarToolbarFactory),
794 (0, tslib_1.__metadata)("design:type", Function)
795], ViewContainer.prototype, "toolbarFactory", void 0);
796(0, tslib_1.__decorate)([
797 (0, inversify_1.inject)(progress_bar_factory_1.ProgressBarFactory),
798 (0, tslib_1.__metadata)("design:type", Function)
799], ViewContainer.prototype, "progressBarFactory", void 0);
800(0, tslib_1.__decorate)([
801 (0, inversify_1.inject)(shell_1.ApplicationShell),
802 (0, tslib_1.__metadata)("design:type", shell_1.ApplicationShell)
803], ViewContainer.prototype, "shell", void 0);
804(0, tslib_1.__decorate)([
805 (0, inversify_1.inject)(tab_bar_decorator_1.TabBarDecoratorService),
806 (0, tslib_1.__metadata)("design:type", tab_bar_decorator_1.TabBarDecoratorService)
807], ViewContainer.prototype, "decoratorService", void 0);
808(0, tslib_1.__decorate)([
809 (0, inversify_1.postConstruct)(),
810 (0, tslib_1.__metadata)("design:type", Function),
811 (0, tslib_1.__metadata)("design:paramtypes", []),
812 (0, tslib_1.__metadata)("design:returntype", void 0)
813], ViewContainer.prototype, "init", null);
814ViewContainer = ViewContainer_1 = (0, tslib_1.__decorate)([
815 (0, inversify_1.injectable)()
816], ViewContainer);
817exports.ViewContainer = ViewContainer;
818(function (ViewContainer) {
819 ViewContainer.Factory = Symbol('ViewContainerFactory');
820 function getOrientation(node) {
821 if (node.closest(`#${theia_dock_panel_1.MAIN_AREA_ID}`) || node.closest(`#${theia_dock_panel_1.BOTTOM_AREA_ID}`)) {
822 return 'horizontal';
823 }
824 return 'vertical';
825 }
826 ViewContainer.getOrientation = getOrientation;
827})(ViewContainer = exports.ViewContainer || (exports.ViewContainer = {}));
828exports.ViewContainer = ViewContainer;
829/**
830 * Wrapper around a widget held by a view container. Adds a header to display the
831 * title, toolbar, and collapse / expand handle.
832 */
833class ViewContainerPart extends widgets_1.BaseWidget {
834 constructor(wrapped, partId, currentContainerId, originalContainerId, originalContainerTitle, toolbarRegistry, toolbarFactory, options = {}) {
835 var _a;
836 super();
837 this.wrapped = wrapped;
838 this.partId = partId;
839 this.currentContainerId = currentContainerId;
840 this.originalContainerId = originalContainerId;
841 this.originalContainerTitle = originalContainerTitle;
842 this.toolbarRegistry = toolbarRegistry;
843 this.toolbarFactory = toolbarFactory;
844 this.options = options;
845 this.collapsedEmitter = new event_1.Emitter();
846 this.contextMenuEmitter = new event_1.Emitter();
847 this.onTitleChangedEmitter = new event_1.Emitter();
848 this.onTitleChanged = this.onTitleChangedEmitter.event;
849 this.onDidFocusEmitter = new event_1.Emitter();
850 this.onDidFocus = this.onDidFocusEmitter.event;
851 this.onPartMovedEmitter = new event_1.Emitter();
852 this.onDidMove = this.onPartMovedEmitter.event;
853 this.onDidChangeDescriptionEmitter = new event_1.Emitter();
854 this.onDidChangeDescription = this.onDidChangeDescriptionEmitter.event;
855 this.onDidChangeBadgeEmitter = new event_1.Emitter();
856 this.onDidChangeBadge = this.onDidChangeBadgeEmitter.event;
857 this.onDidChangeBadgeTooltipEmitter = new event_1.Emitter();
858 this.onDidChangeBadgeTooltip = this.onDidChangeBadgeTooltipEmitter.event;
859 this.toShowHeader = new disposable_1.DisposableCollection();
860 wrapped.parent = this;
861 wrapped.disposed.connect(() => this.dispose());
862 this.id = `${originalContainerId}--${wrapped.id}`;
863 this.addClass('part');
864 const fireTitleChanged = () => this.onTitleChangedEmitter.fire(undefined);
865 this.wrapped.title.changed.connect(fireTitleChanged);
866 this.toDispose.push(disposable_1.Disposable.create(() => this.wrapped.title.changed.disconnect(fireTitleChanged)));
867 if (DescriptionWidget.is(this.wrapped)) {
868 (_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.onDidChangeDescription(() => this.onDidChangeDescriptionEmitter.fire(), undefined, this.toDispose);
869 }
870 if (BadgeWidget.is(this.wrapped)) {
871 this.wrapped.onDidChangeBadge(() => this.onDidChangeBadgeEmitter.fire(), undefined, this.toDispose);
872 this.wrapped.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire(), undefined, this.toDispose);
873 }
874 if (DynamicToolbarWidget.is(this.wrapped)) {
875 this.wrapped.onDidChangeToolbarItems(() => {
876 var _a;
877 this.toolbar.updateTarget(this.wrapped);
878 (_a = this.viewContainer) === null || _a === void 0 ? void 0 : _a.update();
879 });
880 }
881 const { header, body, disposable } = this.createContent();
882 this.header = header;
883 this.body = body;
884 this.toNoDisposeWrapped = this.toDispose.push(wrapped);
885 this.toolbar = this.toolbarFactory();
886 this.toolbar.addClass('theia-view-container-part-title');
887 this.toDispose.pushAll([
888 disposable,
889 this.toolbar,
890 this.toolbarRegistry.onDidChange(() => this.toolbar.updateTarget(this.wrapped)),
891 this.collapsedEmitter,
892 this.contextMenuEmitter,
893 this.onTitleChangedEmitter,
894 this.onDidChangeDescriptionEmitter,
895 this.onDidChangeBadgeEmitter,
896 this.onDidChangeBadgeTooltipEmitter,
897 this.registerContextMenu(),
898 this.onDidFocusEmitter,
899 // focus event does not bubble, capture it
900 (0, widgets_1.addEventListener)(this.node, 'focus', () => this.onDidFocusEmitter.fire(this), true)
901 ]);
902 this.scrollOptions = {
903 suppressScrollX: true,
904 minScrollbarLength: 35
905 };
906 this.collapsed = !!options.initiallyCollapsed;
907 if (options.initiallyHidden && this.canHide) {
908 this.hide();
909 }
910 }
911 get viewContainer() {
912 return this.parent ? this.parent.parent : undefined;
913 }
914 get currentViewContainerId() {
915 return this.currentContainerId;
916 }
917 get headerElement() {
918 return this.header;
919 }
920 get collapsed() {
921 return this._collapsed;
922 }
923 set collapsed(collapsed) {
924 // Cannot collapse/expand if the orientation of the container is `horizontal`.
925 const orientation = ViewContainer.getOrientation(this.node);
926 if (this._collapsed === collapsed || (orientation === 'horizontal' && collapsed)) {
927 return;
928 }
929 this._collapsed = collapsed;
930 this.node.classList.toggle('collapsed', collapsed);
931 if (collapsed && this.wrapped.node.contains(document.activeElement)) {
932 this.header.focus();
933 }
934 this.wrapped.setHidden(collapsed);
935 const toggleIcon = this.header.querySelector(`span.${widgets_1.EXPANSION_TOGGLE_CLASS}`);
936 if (toggleIcon) {
937 if (collapsed) {
938 toggleIcon.classList.add(widgets_1.COLLAPSED_CLASS);
939 }
940 else {
941 toggleIcon.classList.remove(widgets_1.COLLAPSED_CLASS);
942 }
943 }
944 this.update();
945 this.collapsedEmitter.fire(collapsed);
946 }
947 onPartMoved(newContainer) {
948 this.currentContainerId = newContainer.id;
949 this.onPartMovedEmitter.fire(newContainer);
950 }
951 setHidden(hidden) {
952 if (!this.canHide) {
953 return;
954 }
955 super.setHidden(hidden);
956 }
957 get canHide() {
958 return this.options.canHide === undefined || this.options.canHide;
959 }
960 get onCollapsed() {
961 return this.collapsedEmitter.event;
962 }
963 get onContextMenu() {
964 return this.contextMenuEmitter.event;
965 }
966 get minSize() {
967 const style = getComputedStyle(this.body);
968 if (ViewContainer.getOrientation(this.node) === 'horizontal') {
969 return (0, browser_1.parseCssMagnitude)(style.minWidth, 0);
970 }
971 else {
972 return (0, browser_1.parseCssMagnitude)(style.minHeight, 0);
973 }
974 }
975 showTitle() {
976 this.toShowHeader.dispose();
977 }
978 hideTitle() {
979 if (this.titleHidden) {
980 return;
981 }
982 const display = this.header.style.display;
983 const height = this.body.style.height;
984 this.body.style.height = '100%';
985 this.header.style.display = 'none';
986 this.toShowHeader.push(disposable_1.Disposable.create(() => {
987 this.header.style.display = display;
988 this.body.style.height = height;
989 }));
990 }
991 get titleHidden() {
992 return !this.toShowHeader.disposed || this.collapsed;
993 }
994 getScrollContainer() {
995 return this.body;
996 }
997 registerContextMenu() {
998 return new disposable_1.DisposableCollection((0, widgets_1.addEventListener)(this.header, 'contextmenu', event => {
999 this.contextMenuEmitter.fire(event);
1000 }));
1001 }
1002 createContent() {
1003 const disposable = new disposable_1.DisposableCollection();
1004 const { header, disposable: headerDisposable } = this.createHeader();
1005 const body = document.createElement('div');
1006 body.classList.add('body');
1007 this.node.appendChild(header);
1008 this.node.appendChild(body);
1009 disposable.push(headerDisposable);
1010 return {
1011 header,
1012 body,
1013 disposable,
1014 };
1015 }
1016 createHeader() {
1017 const disposable = new disposable_1.DisposableCollection();
1018 const header = document.createElement('div');
1019 header.tabIndex = 0;
1020 header.classList.add('theia-header', 'header', 'theia-view-container-part-header');
1021 disposable.push((0, widgets_1.addEventListener)(header, 'click', event => {
1022 if (this.toolbar && this.toolbar.shouldHandleMouseEvent(event)) {
1023 return;
1024 }
1025 this.collapsed = !this.collapsed;
1026 }));
1027 disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ARROW_LEFT, () => this.collapsed = true));
1028 disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ARROW_RIGHT, () => this.collapsed = false));
1029 disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ENTER, () => this.collapsed = !this.collapsed));
1030 const toggleIcon = document.createElement('span');
1031 toggleIcon.classList.add(widgets_1.EXPANSION_TOGGLE_CLASS, ...widgets_1.CODICON_TREE_ITEM_CLASSES);
1032 if (this.collapsed) {
1033 toggleIcon.classList.add(widgets_1.COLLAPSED_CLASS);
1034 }
1035 header.appendChild(toggleIcon);
1036 const title = document.createElement('span');
1037 title.classList.add('label', 'noselect');
1038 const description = document.createElement('span');
1039 description.classList.add('description');
1040 const badgeSpan = document.createElement('span');
1041 badgeSpan.classList.add('notification-count');
1042 const badgeContainer = document.createElement('div');
1043 badgeContainer.classList.add('notification-count-container');
1044 badgeContainer.appendChild(badgeSpan);
1045 const badgeContainerDisplay = badgeContainer.style.display;
1046 const updateTitle = () => {
1047 var _a;
1048 if (this.currentContainerId !== this.originalContainerId && ((_a = this.originalContainerTitle) === null || _a === void 0 ? void 0 : _a.label)) {
1049 // Creating a title in format: <original_container_title>: <part_title>.
1050 title.innerText = this.originalContainerTitle.label + ': ' + this.wrapped.title.label;
1051 }
1052 else {
1053 title.innerText = this.wrapped.title.label;
1054 }
1055 };
1056 const updateCaption = () => title.title = this.wrapped.title.caption || this.wrapped.title.label;
1057 const updateDescription = () => {
1058 description.innerText = DescriptionWidget.is(this.wrapped) && !this.collapsed && this.wrapped.description || '';
1059 };
1060 const updateBadge = () => {
1061 if (BadgeWidget.is(this.wrapped)) {
1062 const visibleToolBarItems = this.toolbarRegistry.visibleItems(this.wrapped).length > 0;
1063 const badge = this.wrapped.badge;
1064 if (badge && !visibleToolBarItems) {
1065 badgeSpan.innerText = badge.toString();
1066 badgeSpan.title = this.wrapped.badgeTooltip || '';
1067 badgeContainer.style.display = badgeContainerDisplay;
1068 return;
1069 }
1070 }
1071 badgeContainer.style.display = 'none';
1072 };
1073 updateTitle();
1074 updateCaption();
1075 updateDescription();
1076 updateBadge();
1077 disposable.pushAll([
1078 this.onTitleChanged(updateTitle),
1079 this.onTitleChanged(updateCaption),
1080 this.onDidMove(updateTitle),
1081 this.onDidChangeDescription(updateDescription),
1082 this.onDidChangeBadge(updateBadge),
1083 this.onDidChangeBadgeTooltip(updateBadge),
1084 this.onCollapsed(updateDescription)
1085 ]);
1086 header.appendChild(title);
1087 header.appendChild(description);
1088 header.appendChild(badgeContainer);
1089 return {
1090 header,
1091 disposable
1092 };
1093 }
1094 handleResize() {
1095 var _a;
1096 const handleMouseEnter = () => {
1097 var _a;
1098 (_a = this.node) === null || _a === void 0 ? void 0 : _a.classList.add('no-pointer-events');
1099 setTimeout(() => {
1100 var _a, _b;
1101 (_a = this.node) === null || _a === void 0 ? void 0 : _a.classList.remove('no-pointer-events');
1102 (_b = this.node) === null || _b === void 0 ? void 0 : _b.removeEventListener('mouseenter', handleMouseEnter);
1103 }, 100);
1104 };
1105 (_a = this.node) === null || _a === void 0 ? void 0 : _a.addEventListener('mouseenter', handleMouseEnter);
1106 }
1107 onResize(msg) {
1108 this.handleResize();
1109 if (this.wrapped.isAttached && !this.collapsed) {
1110 widgets_1.MessageLoop.sendMessage(this.wrapped, widgets_1.Widget.ResizeMessage.UnknownSize);
1111 }
1112 super.onResize(msg);
1113 }
1114 onUpdateRequest(msg) {
1115 if (this.wrapped.isAttached && !this.collapsed) {
1116 widgets_1.MessageLoop.sendMessage(this.wrapped, msg);
1117 }
1118 super.onUpdateRequest(msg);
1119 }
1120 onAfterAttach(msg) {
1121 if (!this.wrapped.isAttached) {
1122 widgets_1.UnsafeWidgetUtilities.attach(this.wrapped, this.body);
1123 }
1124 widgets_1.UnsafeWidgetUtilities.attach(this.toolbar, this.header);
1125 super.onAfterAttach(msg);
1126 }
1127 onBeforeDetach(msg) {
1128 super.onBeforeDetach(msg);
1129 if (this.toolbar.isAttached) {
1130 widgets_1.Widget.detach(this.toolbar);
1131 }
1132 if (this.wrapped.isAttached) {
1133 widgets_1.UnsafeWidgetUtilities.detach(this.wrapped);
1134 }
1135 }
1136 onBeforeShow(msg) {
1137 if (this.wrapped.isAttached && !this.collapsed) {
1138 widgets_1.MessageLoop.sendMessage(this.wrapped, msg);
1139 }
1140 super.onBeforeShow(msg);
1141 }
1142 onAfterShow(msg) {
1143 super.onAfterShow(msg);
1144 if (this.wrapped.isAttached && !this.collapsed) {
1145 widgets_1.MessageLoop.sendMessage(this.wrapped, msg);
1146 }
1147 }
1148 onBeforeHide(msg) {
1149 if (this.wrapped.isAttached && !this.collapsed) {
1150 widgets_1.MessageLoop.sendMessage(this.wrapped, msg);
1151 }
1152 super.onBeforeShow(msg);
1153 }
1154 onAfterHide(msg) {
1155 super.onAfterHide(msg);
1156 if (this.wrapped.isAttached && !this.collapsed) {
1157 widgets_1.MessageLoop.sendMessage(this.wrapped, msg);
1158 }
1159 }
1160 onChildRemoved(msg) {
1161 super.onChildRemoved(msg);
1162 // if wrapped is not disposed, but detached then we should not dispose it, but only get rid of this part
1163 this.toNoDisposeWrapped.dispose();
1164 this.dispose();
1165 }
1166 onActivateRequest(msg) {
1167 super.onActivateRequest(msg);
1168 if (this.collapsed) {
1169 this.header.focus();
1170 }
1171 else {
1172 this.wrapped.activate();
1173 }
1174 }
1175}
1176exports.ViewContainerPart = ViewContainerPart;
1177(function (ViewContainerPart) {
1178 /**
1179 * Make sure to adjust the `line-height` of the `.theia-view-container .part > .header` CSS class when modifying this, and vice versa.
1180 */
1181 ViewContainerPart.HEADER_HEIGHT = 22;
1182 function closestPart(element, selector = 'div.part') {
1183 if (element instanceof Element) {
1184 const part = element.closest(selector);
1185 if (part instanceof Element) {
1186 return part;
1187 }
1188 }
1189 return undefined;
1190 }
1191 ViewContainerPart.closestPart = closestPart;
1192})(ViewContainerPart = exports.ViewContainerPart || (exports.ViewContainerPart = {}));
1193class ViewContainerLayout extends widgets_1.SplitLayout {
1194 constructor(options, splitPositionHandler) {
1195 super(options);
1196 this.options = options;
1197 this.splitPositionHandler = splitPositionHandler;
1198 }
1199 get items() {
1200 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1201 return this._items;
1202 }
1203 iter() {
1204 return (0, algorithm_1.map)(this.items, item => item.widget);
1205 }
1206 // @ts-expect-error TS2611 `SplitLayout.widgets` is declared as `readonly widgets` but is implemented as a getter.
1207 get widgets() {
1208 return (0, algorithm_1.toArray)(this.iter());
1209 }
1210 attachWidget(index, widget) {
1211 var _a;
1212 super.attachWidget(index, widget);
1213 if (index > -1 && this.parent && this.parent.node.contains((_a = this.widgets[index + 1]) === null || _a === void 0 ? void 0 : _a.node)) {
1214 // Set the correct attach index to the DOM elements.
1215 const ref = this.widgets[index + 1].node;
1216 this.parent.node.insertBefore(widget.node, ref);
1217 this.parent.node.insertBefore(this.handles[index], ref);
1218 this.parent.fit();
1219 }
1220 }
1221 getPartSize(part) {
1222 if (part.collapsed || part.isHidden) {
1223 return part.uncollapsedSize;
1224 }
1225 if (this.orientation === 'horizontal') {
1226 return part.node.offsetWidth;
1227 }
1228 else {
1229 return part.node.offsetHeight;
1230 }
1231 }
1232 /**
1233 * Set the sizes of the view container parts according to the given weights
1234 * by moving the split handles. This is similar to `setRelativeSizes` defined
1235 * in `SplitLayout`, but here we properly consider the collapsed / expanded state.
1236 */
1237 setPartSizes(weights) {
1238 const parts = this.widgets;
1239 const availableSize = this.getAvailableSize();
1240 // Sum up the weights of visible parts
1241 let totalWeight = 0;
1242 let weightCount = 0;
1243 for (let index = 0; index < weights.length && index < parts.length; index++) {
1244 const part = parts[index];
1245 const weight = weights[index];
1246 if (weight && !part.isHidden && !part.collapsed) {
1247 totalWeight += weight;
1248 weightCount++;
1249 }
1250 }
1251 if (weightCount === 0 || availableSize === 0) {
1252 return;
1253 }
1254 // Add the average weight for visible parts without weight
1255 const averageWeight = totalWeight / weightCount;
1256 for (let index = 0; index < weights.length && index < parts.length; index++) {
1257 const part = parts[index];
1258 const weight = weights[index];
1259 if (!weight && !part.isHidden && !part.collapsed) {
1260 totalWeight += averageWeight;
1261 }
1262 }
1263 // Apply the weights to compute actual sizes
1264 let position = 0;
1265 for (let index = 0; index < weights.length && index < parts.length - 1; index++) {
1266 const part = parts[index];
1267 if (!part.isHidden) {
1268 if (this.orientation === 'vertical') {
1269 position += this.options.headerSize;
1270 }
1271 const weight = weights[index];
1272 if (part.collapsed) {
1273 if (weight) {
1274 part.uncollapsedSize = weight / totalWeight * availableSize;
1275 }
1276 }
1277 else {
1278 let contentSize = (weight || averageWeight) / totalWeight * availableSize;
1279 const minSize = part.minSize;
1280 if (contentSize < minSize) {
1281 contentSize = minSize;
1282 }
1283 position += contentSize;
1284 }
1285 this.setHandlePosition(index, position);
1286 position += this.spacing;
1287 }
1288 }
1289 }
1290 /**
1291 * Determine the size of the split panel area that is available for widget content,
1292 * i.e. excluding part headers and split handles.
1293 */
1294 getAvailableSize() {
1295 if (!this.parent || !this.parent.isAttached) {
1296 return 0;
1297 }
1298 const parts = this.widgets;
1299 const visiblePartCount = parts.filter(part => !part.isHidden).length;
1300 let availableSize;
1301 if (this.orientation === 'horizontal') {
1302 availableSize = this.parent.node.offsetWidth;
1303 }
1304 else {
1305 availableSize = this.parent.node.offsetHeight;
1306 availableSize -= visiblePartCount * this.options.headerSize;
1307 }
1308 availableSize -= (visiblePartCount - 1) * this.spacing;
1309 if (availableSize < 0) {
1310 return 0;
1311 }
1312 return availableSize;
1313 }
1314 /**
1315 * Update a view container part that has been collapsed or expanded. The transition
1316 * to the new state is animated.
1317 */
1318 updateCollapsed(part, enableAnimation) {
1319 const index = this.items.findIndex(item => item.widget === part);
1320 if (index < 0 || !this.parent || part.isHidden) {
1321 return;
1322 }
1323 // Do not store the height of the "stretched item". Otherwise, we mess up the "hint height".
1324 // Store the height only if there are other expanded items.
1325 const currentSize = this.orientation === 'horizontal' ? part.node.offsetWidth : part.node.offsetHeight;
1326 if (part.collapsed && this.items.some(item => !item.widget.collapsed && !item.widget.isHidden)) {
1327 part.uncollapsedSize = currentSize;
1328 }
1329 if (!enableAnimation || this.options.animationDuration <= 0) {
1330 widgets_1.MessageLoop.postMessage(this.parent, widgets_1.Widget.Msg.FitRequest);
1331 return;
1332 }
1333 let startTime = undefined;
1334 const duration = this.options.animationDuration;
1335 const direction = part.collapsed ? 'collapse' : 'expand';
1336 let fullSize;
1337 if (direction === 'collapse') {
1338 fullSize = currentSize - this.options.headerSize;
1339 }
1340 else {
1341 fullSize = Math.max((part.uncollapsedSize || 0) - this.options.headerSize, part.minSize);
1342 if (this.items.filter(item => !item.widget.collapsed && !item.widget.isHidden).length === 1) {
1343 // Expand to full available size
1344 fullSize = Math.max(fullSize, this.getAvailableSize());
1345 }
1346 }
1347 // The update function is called on every animation frame until the predefined duration has elapsed.
1348 const updateFunc = (time) => {
1349 if (!this.parent) {
1350 part.animatedSize = undefined;
1351 return;
1352 }
1353 if (startTime === undefined) {
1354 startTime = time;
1355 }
1356 if (time - startTime < duration) {
1357 // Render an intermediate state for the animation
1358 const t = this.tween((time - startTime) / duration);
1359 if (direction === 'collapse') {
1360 part.animatedSize = (1 - t) * fullSize;
1361 }
1362 else {
1363 part.animatedSize = t * fullSize;
1364 }
1365 requestAnimationFrame(updateFunc);
1366 }
1367 else {
1368 // The animation is finished
1369 if (direction === 'collapse') {
1370 part.animatedSize = undefined;
1371 }
1372 else {
1373 part.animatedSize = fullSize;
1374 // Request another frame to reset the part to variable size
1375 requestAnimationFrame(() => {
1376 part.animatedSize = undefined;
1377 if (this.parent) {
1378 widgets_1.MessageLoop.sendMessage(this.parent, widgets_1.Widget.Msg.FitRequest);
1379 }
1380 });
1381 }
1382 }
1383 widgets_1.MessageLoop.sendMessage(this.parent, widgets_1.Widget.Msg.FitRequest);
1384 };
1385 requestAnimationFrame(updateFunc);
1386 }
1387 updateSashes() {
1388 const { widgets, handles } = this;
1389 if (widgets.length !== handles.length) {
1390 console.warn('Unexpected mismatch between number of widgets and number of handles.');
1391 return;
1392 }
1393 const firstUncollapsed = this.getFirstUncollapsedWidgetIndex();
1394 const lastUncollapsed = firstUncollapsed === undefined ? undefined : this.getLastUncollapsedWidgetIndex();
1395 const allHidden = firstUncollapsed === lastUncollapsed;
1396 for (const [index, handle] of this.handles.entries()) {
1397 // The or clauses are added for type checking. If they're true, allHidden will also have been true.
1398 if (allHidden || firstUncollapsed === undefined || lastUncollapsed === undefined) {
1399 handle.classList.add('sash-hidden');
1400 }
1401 else if (index < lastUncollapsed && index >= firstUncollapsed) {
1402 handle.classList.remove('sash-hidden');
1403 }
1404 else {
1405 handle.classList.add('sash-hidden');
1406 }
1407 }
1408 }
1409 getFirstUncollapsedWidgetIndex() {
1410 const index = this.widgets.findIndex(widget => !widget.collapsed && !widget.isHidden);
1411 return index === -1 ? undefined : index;
1412 }
1413 getLastUncollapsedWidgetIndex() {
1414 for (let i = this.widgets.length - 1; i >= 0; i--) {
1415 if (!this.widgets[i].collapsed && !this.widgets[i].isHidden) {
1416 return i;
1417 }
1418 }
1419 }
1420 onFitRequest(msg) {
1421 for (const part of this.widgets) {
1422 const style = part.node.style;
1423 if (part.animatedSize !== undefined) {
1424 // The part size has been fixed for animating the transition to collapsed / expanded state
1425 const fixedSize = `${this.options.headerSize + part.animatedSize}px`;
1426 style.minHeight = fixedSize;
1427 style.maxHeight = fixedSize;
1428 }
1429 else if (part.collapsed) {
1430 // The part size is fixed to the header size
1431 const fixedSize = `${this.options.headerSize}px`;
1432 style.minHeight = fixedSize;
1433 style.maxHeight = fixedSize;
1434 }
1435 else {
1436 const minSize = `${this.options.headerSize + part.minSize}px`;
1437 style.minHeight = minSize;
1438 style.maxHeight = '';
1439 }
1440 }
1441 super.onFitRequest(msg);
1442 }
1443 /**
1444 * Sinusoidal tween function for smooth animation.
1445 */
1446 tween(t) {
1447 return 0.5 * (1 - Math.cos(Math.PI * t));
1448 }
1449 setHandlePosition(index, position) {
1450 const options = {
1451 referenceWidget: this.widgets[index],
1452 duration: 0
1453 };
1454 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1455 return this.splitPositionHandler.setSplitHandlePosition(this.parent, index, position, options);
1456 }
1457}
1458exports.ViewContainerLayout = ViewContainerLayout;
1459//# sourceMappingURL=view-container.js.map
\No newline at end of file