UNPKG

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