UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

1,150 lines (1,149 loc) • 62.8 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2018-2019 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 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; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var ViewContainer_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.ViewContainerLayout = exports.ViewContainerPart = exports.ViewContainer = exports.DynamicToolbarWidget = exports.BadgeWidget = exports.DescriptionWidget = exports.ViewContainerIdentifier = void 0; const inversify_1 = require("inversify"); const algorithm_1 = require("@phosphor/algorithm"); const widgets_1 = require("./widgets"); const event_1 = require("../common/event"); const disposable_1 = require("../common/disposable"); const command_1 = require("../common/command"); const menu_1 = require("../common/menu"); const shell_1 = require("./shell"); const theia_dock_panel_1 = require("./shell/theia-dock-panel"); const frontend_application_state_1 = require("./frontend-application-state"); const context_menu_renderer_1 = require("./context-menu-renderer"); const browser_1 = require("./browser"); const tab_bar_toolbar_1 = require("./shell/tab-bar-toolbar"); const common_1 = require("../common"); const widget_manager_1 = require("./widget-manager"); const keys_1 = require("./keys"); const progress_bar_factory_1 = require("./progress-bar-factory"); const dragdrop_1 = require("@phosphor/dragdrop"); const coreutils_1 = require("@phosphor/coreutils"); const domutils_1 = require("@phosphor/domutils"); const tab_bar_decorator_1 = require("./shell/tab-bar-decorator"); let ViewContainerIdentifier = class ViewContainerIdentifier { }; ViewContainerIdentifier = __decorate([ (0, inversify_1.injectable)() ], ViewContainerIdentifier); exports.ViewContainerIdentifier = ViewContainerIdentifier; var DescriptionWidget; (function (DescriptionWidget) { function is(arg) { return (0, common_1.isObject)(arg) && 'onDidChangeDescription' in arg; } DescriptionWidget.is = is; })(DescriptionWidget = exports.DescriptionWidget || (exports.DescriptionWidget = {})); var BadgeWidget; (function (BadgeWidget) { function is(arg) { return (0, common_1.isObject)(arg) && 'onDidChangeBadge' in arg && 'onDidChangeBadgeTooltip' in arg; } BadgeWidget.is = is; })(BadgeWidget = exports.BadgeWidget || (exports.BadgeWidget = {})); var DynamicToolbarWidget; (function (DynamicToolbarWidget) { function is(arg) { return (0, common_1.isObject)(arg) && 'onDidChangeToolbarItems' in arg; } DynamicToolbarWidget.is = is; })(DynamicToolbarWidget = exports.DynamicToolbarWidget || (exports.DynamicToolbarWidget = {})); /** * A view container holds an arbitrary number of widgets inside a split panel. * Each widget is wrapped in a _part_ that displays the widget title and toolbar * and allows to collapse / expand the widget content. */ let ViewContainer = ViewContainer_1 = class ViewContainer extends widgets_1.BaseWidget { constructor() { super(...arguments); /** * Disable dragging parts from/to this view container. */ this.disableDNDBetweenContainers = false; this.onDidChangeTrackableWidgetsEmitter = new event_1.Emitter(); this.onDidChangeTrackableWidgets = this.onDidChangeTrackableWidgetsEmitter.event; this.toDisposeOnCurrentPart = new disposable_1.DisposableCollection(); this.toDisposeOnUpdateTitle = new disposable_1.DisposableCollection(); this._tabBarDelegate = this; this.toRemoveWidgets = new Map(); this.toDisposeOnDragEnd = new disposable_1.DisposableCollection(); } init() { this.id = this.options.id; this.addClass('theia-view-container'); const layout = new widgets_1.PanelLayout(); this.layout = layout; this.panel = new widgets_1.SplitPanel({ layout: new ViewContainerLayout({ renderer: widgets_1.SplitPanel.defaultRenderer, orientation: this.orientation, spacing: 2, headerSize: ViewContainerPart.HEADER_HEIGHT, animationDuration: 200 }, this.splitPositionHandler) }); this.panel.node.tabIndex = -1; this.configureLayout(layout); const { commandRegistry, menuRegistry, contextMenuRenderer } = this; this.toDispose.pushAll([ (0, widgets_1.addEventListener)(this.node, 'contextmenu', event => { if (event.button === 2 && (0, algorithm_1.every)(this.containerLayout.iter(), part => !!part.isHidden)) { event.stopPropagation(); event.preventDefault(); contextMenuRenderer.render({ menuPath: this.contextMenuPath, anchor: event }); } }), commandRegistry.registerCommand({ id: this.globalHideCommandId }, { execute: (anchor) => { const toHide = this.findPartForAnchor(anchor); if (toHide && toHide.canHide) { toHide.hide(); } }, isVisible: (anchor) => { const toHide = this.findPartForAnchor(anchor); if (toHide) { return toHide.canHide && !toHide.isHidden; } else { return (0, algorithm_1.some)(this.containerLayout.iter(), part => !part.isHidden); } } }), menuRegistry.registerMenuAction([...this.contextMenuPath, '0_global'], { commandId: this.globalHideCommandId, label: common_1.nls.localizeByDefault('Hide') }), this.onDidChangeTrackableWidgetsEmitter, this.onDidChangeTrackableWidgets(() => this.decoratorService.fireDidChangeDecorations()) ]); if (this.options.progressLocationId) { this.toDispose.push(this.progressBarFactory({ container: this.node, insertMode: 'prepend', locationId: this.options.progressLocationId })); } } configureLayout(layout) { layout.addWidget(this.panel); } updateCurrentPart(part) { if (part && this.getParts().indexOf(part) !== -1) { this.currentPart = part; } if (this.currentPart && !this.currentPart.isDisposed) { return; } const visibleParts = this.getParts().filter(p => !p.isHidden); const expandedParts = visibleParts.filter(p => !p.collapsed); this.currentPart = expandedParts[0] || visibleParts[0]; } updateSplitterVisibility() { const className = 'p-first-visible'; let firstFound = false; for (const part of this.getParts()) { if (!part.isHidden && !firstFound) { part.addClass(className); firstFound = true; } else { part.removeClass(className); } } } setTitleOptions(titleOptions) { this.titleOptions = titleOptions; this.updateTitle(); } updateTabBarDelegate() { const visibleParts = this.getParts().filter(part => !part.isHidden); if (visibleParts.length === 1) { this._tabBarDelegate = visibleParts[0].wrapped; } else { this._tabBarDelegate = this; } } getTabBarDelegate() { return this._tabBarDelegate; } updateTitle() { var _a; this.toDisposeOnUpdateTitle.dispose(); this.toDispose.push(this.toDisposeOnUpdateTitle); this.updateTabBarDelegate(); let title = Object.assign({}, this.titleOptions); if ((0, common_1.isEmpty)(title)) { return; } const allParts = this.getParts(); const visibleParts = allParts.filter(part => !part.isHidden); this.title.label = title.label; // If there's only one visible part - inline it's title into the container title except in case the part // isn't originally belongs to this container but there are other **original** hidden parts. if (visibleParts.length === 1 && (visibleParts[0].originalContainerId === this.id || !this.findOriginalPart())) { const part = visibleParts[0]; this.toDisposeOnUpdateTitle.push(part.onTitleChanged(() => this.updateTitle())); const partLabel = part.wrapped.title.label; // Change the container title if it contains only one part that originally belongs to another container. if (allParts.length === 1 && part.originalContainerId !== this.id && !this.isCurrentTitle(part.originalContainerTitle)) { title = Object.assign({}, part.originalContainerTitle); this.setTitleOptions(title); return; } if (partLabel) { if (this.title.label && this.title.label !== partLabel) { this.title.label += ': ' + partLabel; } else { this.title.label = partLabel; } } part.collapsed = false; part.hideTitle(); } else { visibleParts.forEach(part => part.showTitle()); // If at least one part originally belongs to this container the title should return to its original value. const originalPart = this.findOriginalPart(); if (originalPart && !this.isCurrentTitle(originalPart.originalContainerTitle)) { title = Object.assign({}, originalPart.originalContainerTitle); this.setTitleOptions(title); return; } } this.updateToolbarItems(allParts); this.title.caption = (title === null || title === void 0 ? void 0 : title.caption) || (title === null || title === void 0 ? void 0 : title.label); if (title.iconClass) { this.title.iconClass = title.iconClass; } if (this.title.className.includes(widgets_1.PINNED_CLASS)) { (_a = this.title).closable && (_a.closable = false); } else if (title.closeable !== undefined) { this.title.closable = title.closeable; } } updateToolbarItems(allParts) { if (allParts.length > 1) { const group = this.getToggleVisibilityGroupLabel(); for (const part of allParts) { const existingId = this.toggleVisibilityCommandId(part); const { caption, label, dataset: { visibilityCommandLabel } } = part.wrapped.title; this.registerToolbarItem(existingId, { tooltip: visibilityCommandLabel || caption || label, group }); } } } getToggleVisibilityGroupLabel() { return 'view'; } registerToolbarItem(commandId, options) { const newId = `${this.id}-tabbar-toolbar-${commandId}`; const existingHandler = this.commandRegistry.getAllHandlers(commandId)[0]; const existingCommand = this.commandRegistry.getCommand(commandId); if (existingHandler && existingCommand) { this.toDisposeOnUpdateTitle.push(this.commandRegistry.registerCommand({ ...existingCommand, id: newId }, { execute: (_widget, ...args) => this.commandRegistry.executeCommand(commandId, ...args), isToggled: (_widget, ...args) => this.commandRegistry.isToggled(commandId, ...args), isEnabled: (_widget, ...args) => this.commandRegistry.isEnabled(commandId, ...args), isVisible: (widget, ...args) => widget === this.getTabBarDelegate() && this.commandRegistry.isVisible(commandId, ...args), })); this.toDisposeOnUpdateTitle.push(this.toolbarRegistry.registerItem({ ...options, id: newId, command: newId, })); } } findOriginalPart() { return this.getParts().find(part => part.originalContainerId === this.id); } isCurrentTitle(titleOptions) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (!!titleOptions && !!this.titleOptions && Object.keys(titleOptions).every(key => titleOptions[key] === this.titleOptions[key])) || (!titleOptions && !this.titleOptions); } findPartForAnchor(anchor) { const element = document.elementFromPoint(anchor.x, anchor.y); if (element instanceof Element) { const closestPart = ViewContainerPart.closestPart(element); if (closestPart && closestPart.id) { return (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === closestPart.id); } } return undefined; } createPartId(widget) { const description = this.widgetManager.getDescription(widget); return widget.id || JSON.stringify(description); } addWidget(widget, options, originalContainerId, originalContainerTitle) { const existing = this.toRemoveWidgets.get(widget.id); if (existing) { return existing; } const partId = this.createPartId(widget); const newPart = this.createPart(widget, partId, originalContainerId || this.id, originalContainerTitle || this.titleOptions, options); return this.attachNewPart(newPart); } attachNewPart(newPart, insertIndex) { const toRemoveWidget = new disposable_1.DisposableCollection(); this.toDispose.push(toRemoveWidget); this.toRemoveWidgets.set(newPart.wrapped.id, toRemoveWidget); toRemoveWidget.push(disposable_1.Disposable.create(() => this.toRemoveWidgets.delete(newPart.wrapped.id))); this.registerPart(newPart); if (insertIndex !== undefined || (newPart.options && newPart.options.order !== undefined)) { const index = insertIndex !== null && insertIndex !== void 0 ? insertIndex : this.getParts().findIndex(part => part.options.order === undefined || part.options.order > newPart.options.order); if (index >= 0) { this.containerLayout.insertWidget(index, newPart); } else { this.containerLayout.addWidget(newPart); } } else { this.containerLayout.addWidget(newPart); } this.refreshMenu(newPart); this.updateTitle(); this.updateCurrentPart(); this.updateSplitterVisibility(); this.update(); this.fireDidChangeTrackableWidgets(); toRemoveWidget.pushAll([ disposable_1.Disposable.create(() => { if (newPart.currentViewContainerId === this.id) { newPart.dispose(); } this.unregisterPart(newPart); if (!newPart.isDisposed && this.getPartIndex(newPart.id) > -1) { this.containerLayout.removeWidget(newPart); } if (!this.isDisposed) { this.update(); this.updateTitle(); this.updateCurrentPart(); this.updateSplitterVisibility(); this.fireDidChangeTrackableWidgets(); } }), this.registerDND(newPart), newPart.onDidChangeVisibility(() => { this.updateTitle(); this.updateCurrentPart(); this.updateSplitterVisibility(); this.containerLayout.updateSashes(); }), newPart.onCollapsed(() => { this.containerLayout.updateCollapsed(newPart, this.enableAnimation); this.containerLayout.updateSashes(); this.updateCurrentPart(); }), newPart.onContextMenu(event => { if (event.button === 2) { event.preventDefault(); event.stopPropagation(); this.contextMenuRenderer.render({ menuPath: this.contextMenuPath, anchor: event }); } }), newPart.onTitleChanged(() => this.refreshMenu(newPart)), newPart.onDidFocus(() => this.updateCurrentPart(newPart)) ]); newPart.disposed.connect(() => toRemoveWidget.dispose()); return toRemoveWidget; } createPart(widget, partId, originalContainerId, originalContainerTitle, options) { return new ViewContainerPart(widget, partId, this.id, originalContainerId, originalContainerTitle, this.toolbarRegistry, this.toolbarFactory, options); } removeWidget(widget) { const disposable = this.toRemoveWidgets.get(widget.id); if (disposable) { disposable.dispose(); return true; } return false; } getParts() { return this.containerLayout.widgets; } getPartIndex(partId) { if (partId) { return this.getParts().findIndex(part => part.id === partId); } return -1; } getPartFor(widget) { return this.getParts().find(p => p.wrapped.id === widget.id); } get containerLayout() { const layout = this.panel.layout; if (layout instanceof ViewContainerLayout) { return layout; } throw new Error('view container is disposed'); } get orientation() { return ViewContainer_1.getOrientation(this.node); } get enableAnimation() { return this.applicationStateService.state === 'ready'; } storeState() { if (!this.isVisible && this.lastVisibleState) { return this.lastVisibleState; } return this.doStoreState(); } doStoreState() { const parts = this.getParts(); const availableSize = this.containerLayout.getAvailableSize(); const orientation = this.orientation; const partStates = parts.map(part => { let size = this.containerLayout.getPartSize(part); if (size && size > ViewContainerPart.HEADER_HEIGHT && orientation === 'vertical') { size -= ViewContainerPart.HEADER_HEIGHT; } return { widget: part.wrapped, partId: part.partId, collapsed: part.collapsed, hidden: part.isHidden, relativeSize: size && availableSize ? size / availableSize : undefined, originalContainerId: part.originalContainerId, originalContainerTitle: part.originalContainerTitle }; }); return { parts: partStates, title: this.titleOptions }; } restoreState(state) { this.lastVisibleState = state; this.doRestoreState(state); } doRestoreState(state) { this.setTitleOptions(state.title); // restore widgets for (const part of state.parts) { if (part.widget) { this.addWidget(part.widget, undefined, part.originalContainerId, part.originalContainerTitle || {}); } } const partStates = state.parts.filter(partState => (0, algorithm_1.some)(this.containerLayout.iter(), p => p.partId === partState.partId)); // Reorder the parts according to the stored state for (let index = 0; index < partStates.length; index++) { const partState = partStates[index]; const widget = this.getParts().find(part => part.partId === partState.partId); if (widget) { this.containerLayout.insertWidget(index, widget); } } // Restore visibility and collapsed state const parts = this.getParts(); for (let index = 0; index < parts.length; index++) { const part = parts[index]; const partState = partStates.find(s => part.partId === s.partId); if (partState) { part.setHidden(partState.hidden); part.collapsed = partState.collapsed || !partState.relativeSize; } else if (part.canHide) { part.hide(); } this.refreshMenu(part); } // Restore part sizes (0, widgets_1.waitForRevealed)(this).then(() => { this.containerLayout.setPartSizes(partStates.map(partState => partState.relativeSize)); this.updateSplitterVisibility(); }); } /** * Register a command to toggle the visibility of the new part. */ registerPart(toRegister) { const commandId = this.toggleVisibilityCommandId(toRegister); this.commandRegistry.registerCommand({ id: commandId }, { execute: () => { const toHide = (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === toRegister.id); if (toHide) { toHide.setHidden(!toHide.isHidden); } }, isToggled: () => { if (!toRegister.canHide) { return true; } const widgetToToggle = (0, algorithm_1.find)(this.containerLayout.iter(), part => part.id === toRegister.id); if (widgetToToggle) { return !widgetToToggle.isHidden; } return false; }, isEnabled: arg => toRegister.canHide && (!this.titleOptions || !(arg instanceof widgets_1.Widget) || (arg instanceof ViewContainer_1 && arg.id === this.id)), isVisible: arg => !this.titleOptions || !(arg instanceof widgets_1.Widget) || (arg instanceof ViewContainer_1 && arg.id === this.id) }); } /** * Register a menu action to toggle the visibility of the new part. * The menu action is unregistered first to enable refreshing the order of menu actions. */ refreshMenu(part) { const commandId = this.toggleVisibilityCommandId(part); this.menuRegistry.unregisterMenuAction(commandId); if (!part.wrapped.title.label) { return; } const { dataset: { visibilityCommandLabel }, caption, label } = part.wrapped.title; const action = { commandId: commandId, label: visibilityCommandLabel || caption || label, order: this.getParts().indexOf(part).toString() }; this.menuRegistry.registerMenuAction([...this.contextMenuPath, '1_widgets'], action); if (this.titleOptions) { this.menuRegistry.registerMenuAction([...shell_1.SIDE_PANEL_TOOLBAR_CONTEXT_MENU, 'navigation'], action); } } unregisterPart(part) { const commandId = this.toggleVisibilityCommandId(part); this.commandRegistry.unregisterCommand(commandId); this.menuRegistry.unregisterMenuAction(commandId); } get contextMenuPath() { return [`${this.id}-context-menu`]; } toggleVisibilityCommandId(part) { return `${this.id}:toggle-visibility-${part.id}`; } get globalHideCommandId() { return `${this.id}:toggle-visibility`; } moveBefore(toMovedId, moveBeforeThisId) { const parts = this.getParts(); const indexToMove = parts.findIndex(part => part.id === toMovedId); const targetIndex = parts.findIndex(part => part.id === moveBeforeThisId); if (indexToMove >= 0 && targetIndex >= 0) { this.containerLayout.insertWidget(targetIndex, parts[indexToMove]); for (let index = Math.min(indexToMove, targetIndex); index < parts.length; index++) { this.refreshMenu(parts[index]); this.activate(); } } this.updateSplitterVisibility(); } getTrackableWidgets() { return this.getParts().map(w => w.wrapped); } fireDidChangeTrackableWidgets() { this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets()); } activateWidget(id) { const part = this.revealPart(id); if (!part) { return undefined; } this.updateCurrentPart(part); part.collapsed = false; return part.wrapped; } revealWidget(id) { const part = this.revealPart(id); return part && part.wrapped; } revealPart(id) { const part = this.getParts().find(p => p.wrapped.id === id); if (!part) { return undefined; } part.setHidden(false); return part; } onActivateRequest(msg) { super.onActivateRequest(msg); if (this.currentPart) { this.currentPart.activate(); } else { this.panel.node.focus({ preventScroll: true }); } } onAfterAttach(msg) { const orientation = this.orientation; this.containerLayout.orientation = orientation; if (orientation === 'horizontal') { for (const part of this.getParts()) { part.collapsed = false; } } super.onAfterAttach(msg); } onBeforeHide(msg) { super.onBeforeHide(msg); this.lastVisibleState = this.storeState(); } onAfterShow(msg) { super.onAfterShow(msg); this.updateTitle(); this.lastVisibleState = undefined; } onBeforeAttach(msg) { super.onBeforeAttach(msg); this.node.addEventListener('p-dragenter', this, true); this.node.addEventListener('p-dragover', this, true); this.node.addEventListener('p-dragleave', this, true); this.node.addEventListener('p-drop', this, true); } onAfterDetach(msg) { super.onAfterDetach(msg); this.node.removeEventListener('p-dragenter', this, true); this.node.removeEventListener('p-dragover', this, true); this.node.removeEventListener('p-dragleave', this, true); this.node.removeEventListener('p-drop', this, true); } handleEvent(event) { switch (event.type) { case 'p-dragenter': this.handleDragEnter(event); break; case 'p-dragover': this.handleDragOver(event); break; case 'p-dragleave': this.handleDragLeave(event); break; case 'p-drop': this.handleDrop(event); break; } } handleDragEnter(event) { if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) { event.preventDefault(); event.stopPropagation(); } } handleDragOver(event) { var _a; const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory'); const widget = factory && factory(); if (!(widget instanceof ViewContainerPart)) { return; } event.preventDefault(); event.stopPropagation(); const sameContainers = this.id === widget.currentViewContainerId; const targetPart = algorithm_1.ArrayExt.findFirstValue(this.getParts(), (p => domutils_1.ElementExt.hitTest(p.node, event.clientX, event.clientY))); if (!targetPart && sameContainers) { event.dropAction = 'none'; return; } if (targetPart) { // add overlay class style to the `targetPart` node. targetPart.node.classList.add('drop-target'); this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => targetPart.node.classList.remove('drop-target'))); } else { // show panel overlay. const dockPanel = this.getDockPanel(); if (dockPanel) { dockPanel.overlay.show({ top: 0, bottom: 0, right: 0, left: 0 }); this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => dockPanel.overlay.hide(100))); } } const isDraggingOutsideDisabled = this.disableDNDBetweenContainers || ((_a = widget.viewContainer) === null || _a === void 0 ? void 0 : _a.disableDNDBetweenContainers) || widget.options.disableDraggingToOtherContainers; if (isDraggingOutsideDisabled && !sameContainers) { const { target } = event; if (target instanceof HTMLElement) { target.classList.add('theia-cursor-no-drop'); this.toDisposeOnDragEnd.push(disposable_1.Disposable.create(() => { target.classList.remove('theia-cursor-no-drop'); })); } event.dropAction = 'none'; return; } ; event.dropAction = event.proposedAction; } ; handleDragLeave(event) { this.toDisposeOnDragEnd.dispose(); if (event.mimeData.hasData('application/vnd.phosphor.view-container-factory')) { event.preventDefault(); event.stopPropagation(); } } ; handleDrop(event) { this.toDisposeOnDragEnd.dispose(); const factory = event.mimeData.getData('application/vnd.phosphor.view-container-factory'); const draggedPart = factory && factory(); if (!(draggedPart instanceof ViewContainerPart)) { event.dropAction = 'none'; return; } event.preventDefault(); event.stopPropagation(); const parts = this.getParts(); const toIndex = algorithm_1.ArrayExt.findFirstIndex(parts, part => domutils_1.ElementExt.hitTest(part.node, event.clientX, event.clientY)); if (draggedPart.currentViewContainerId !== this.id) { this.attachNewPart(draggedPart, toIndex > -1 ? toIndex + 1 : toIndex); draggedPart.onPartMoved(this); } else { this.moveBefore(draggedPart.id, parts[toIndex].id); } event.dropAction = event.proposedAction; } registerDND(part) { part.headerElement.draggable = true; return new disposable_1.DisposableCollection((0, widgets_1.addEventListener)(part.headerElement, 'dragstart', event => { event.preventDefault(); const mimeData = new coreutils_1.MimeData(); mimeData.setData('application/vnd.phosphor.view-container-factory', () => part); const clonedHeader = part.headerElement.cloneNode(true); clonedHeader.style.width = part.node.style.width; clonedHeader.style.opacity = '0.6'; const drag = new dragdrop_1.Drag({ mimeData, dragImage: clonedHeader, proposedAction: 'move', supportedActions: 'move' }); part.node.classList.add('p-mod-hidden'); drag.start(event.clientX, event.clientY).then(dropAction => { // The promise is resolved when the drag has ended if (dropAction === 'move' && part.currentViewContainerId !== this.id) { this.removeWidget(part.wrapped); this.lastVisibleState = this.doStoreState(); } }); setTimeout(() => { part.node.classList.remove('p-mod-hidden'); }, 0); }, false)); } getDockPanel() { let panel; let parent = this.parent; while (!panel && parent) { if (this.isSideDockPanel(parent)) { panel = parent; } else { parent = parent.parent; } } return panel; } isSideDockPanel(widget) { const { leftPanelHandler, rightPanelHandler } = this.shell; if (widget instanceof widgets_1.DockPanel && (widget.id === rightPanelHandler.dockPanel.id || widget.id === leftPanelHandler.dockPanel.id)) { return true; } return false; } }; __decorate([ (0, inversify_1.inject)(frontend_application_state_1.FrontendApplicationStateService), __metadata("design:type", frontend_application_state_1.FrontendApplicationStateService) ], ViewContainer.prototype, "applicationStateService", void 0); __decorate([ (0, inversify_1.inject)(context_menu_renderer_1.ContextMenuRenderer), __metadata("design:type", context_menu_renderer_1.ContextMenuRenderer) ], ViewContainer.prototype, "contextMenuRenderer", void 0); __decorate([ (0, inversify_1.inject)(command_1.CommandRegistry), __metadata("design:type", command_1.CommandRegistry) ], ViewContainer.prototype, "commandRegistry", void 0); __decorate([ (0, inversify_1.inject)(menu_1.MenuModelRegistry), __metadata("design:type", menu_1.MenuModelRegistry) ], ViewContainer.prototype, "menuRegistry", void 0); __decorate([ (0, inversify_1.inject)(widget_manager_1.WidgetManager), __metadata("design:type", widget_manager_1.WidgetManager) ], ViewContainer.prototype, "widgetManager", void 0); __decorate([ (0, inversify_1.inject)(shell_1.SplitPositionHandler), __metadata("design:type", shell_1.SplitPositionHandler) ], ViewContainer.prototype, "splitPositionHandler", void 0); __decorate([ (0, inversify_1.inject)(ViewContainerIdentifier), __metadata("design:type", ViewContainerIdentifier) ], ViewContainer.prototype, "options", void 0); __decorate([ (0, inversify_1.inject)(tab_bar_toolbar_1.TabBarToolbarRegistry), __metadata("design:type", tab_bar_toolbar_1.TabBarToolbarRegistry) ], ViewContainer.prototype, "toolbarRegistry", void 0); __decorate([ (0, inversify_1.inject)(tab_bar_toolbar_1.TabBarToolbarFactory), __metadata("design:type", Function) ], ViewContainer.prototype, "toolbarFactory", void 0); __decorate([ (0, inversify_1.inject)(progress_bar_factory_1.ProgressBarFactory), __metadata("design:type", Function) ], ViewContainer.prototype, "progressBarFactory", void 0); __decorate([ (0, inversify_1.inject)(shell_1.ApplicationShell), __metadata("design:type", shell_1.ApplicationShell) ], ViewContainer.prototype, "shell", void 0); __decorate([ (0, inversify_1.inject)(tab_bar_decorator_1.TabBarDecoratorService), __metadata("design:type", tab_bar_decorator_1.TabBarDecoratorService) ], ViewContainer.prototype, "decoratorService", void 0); __decorate([ (0, inversify_1.postConstruct)(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], ViewContainer.prototype, "init", null); ViewContainer = ViewContainer_1 = __decorate([ (0, inversify_1.injectable)() ], ViewContainer); exports.ViewContainer = ViewContainer; (function (ViewContainer) { ViewContainer.Factory = Symbol('ViewContainerFactory'); function getOrientation(node) { if (node.closest(`#${theia_dock_panel_1.MAIN_AREA_ID}`) || node.closest(`#${theia_dock_panel_1.BOTTOM_AREA_ID}`)) { return 'horizontal'; } return 'vertical'; } ViewContainer.getOrientation = getOrientation; })(ViewContainer = exports.ViewContainer || (exports.ViewContainer = {})); exports.ViewContainer = ViewContainer; /** * Wrapper around a widget held by a view container. Adds a header to display the * title, toolbar, and collapse / expand handle. */ class ViewContainerPart extends widgets_1.BaseWidget { constructor(wrapped, partId, currentContainerId, originalContainerId, originalContainerTitle, toolbarRegistry, toolbarFactory, options = {}) { var _a; super(); this.wrapped = wrapped; this.partId = partId; this.currentContainerId = currentContainerId; this.originalContainerId = originalContainerId; this.originalContainerTitle = originalContainerTitle; this.toolbarRegistry = toolbarRegistry; this.toolbarFactory = toolbarFactory; this.options = options; this.collapsedEmitter = new event_1.Emitter(); this.contextMenuEmitter = new event_1.Emitter(); this.onTitleChangedEmitter = new event_1.Emitter(); this.onTitleChanged = this.onTitleChangedEmitter.event; this.onDidFocusEmitter = new event_1.Emitter(); this.onDidFocus = this.onDidFocusEmitter.event; this.onPartMovedEmitter = new event_1.Emitter(); this.onDidMove = this.onPartMovedEmitter.event; this.onDidChangeDescriptionEmitter = new event_1.Emitter(); this.onDidChangeDescription = this.onDidChangeDescriptionEmitter.event; this.onDidChangeBadgeEmitter = new event_1.Emitter(); this.onDidChangeBadge = this.onDidChangeBadgeEmitter.event; this.onDidChangeBadgeTooltipEmitter = new event_1.Emitter(); this.onDidChangeBadgeTooltip = this.onDidChangeBadgeTooltipEmitter.event; this.toShowHeader = new disposable_1.DisposableCollection(); wrapped.parent = this; wrapped.disposed.connect(() => this.dispose()); this.id = `${originalContainerId}--${wrapped.id}`; this.addClass('part'); const fireTitleChanged = () => this.onTitleChangedEmitter.fire(undefined); this.wrapped.title.changed.connect(fireTitleChanged); this.toDispose.push(disposable_1.Disposable.create(() => this.wrapped.title.changed.disconnect(fireTitleChanged))); if (DescriptionWidget.is(this.wrapped)) { (_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.onDidChangeDescription(() => this.onDidChangeDescriptionEmitter.fire(), undefined, this.toDispose); } if (BadgeWidget.is(this.wrapped)) { this.wrapped.onDidChangeBadge(() => this.onDidChangeBadgeEmitter.fire(), undefined, this.toDispose); this.wrapped.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire(), undefined, this.toDispose); } if (DynamicToolbarWidget.is(this.wrapped)) { this.wrapped.onDidChangeToolbarItems(() => { var _a; this.toolbar.updateTarget(this.wrapped); (_a = this.viewContainer) === null || _a === void 0 ? void 0 : _a.update(); }); } const { header, body, disposable } = this.createContent(); this.header = header; this.body = body; this.toNoDisposeWrapped = this.toDispose.push(wrapped); this.toolbar = this.toolbarFactory(); this.toolbar.addClass('theia-view-container-part-title'); this.toDispose.pushAll([ disposable, this.toolbar, this.toolbarRegistry.onDidChange(() => this.toolbar.updateTarget(this.wrapped)), this.collapsedEmitter, this.contextMenuEmitter, this.onTitleChangedEmitter, this.onDidChangeDescriptionEmitter, this.onDidChangeBadgeEmitter, this.onDidChangeBadgeTooltipEmitter, this.registerContextMenu(), this.onDidFocusEmitter, // focus event does not bubble, capture it (0, widgets_1.addEventListener)(this.node, 'focus', () => this.onDidFocusEmitter.fire(this), true) ]); this.scrollOptions = { suppressScrollX: true, minScrollbarLength: 35 }; this.collapsed = !!options.initiallyCollapsed; if (options.initiallyHidden && this.canHide) { this.hide(); } } get viewContainer() { return this.parent ? this.parent.parent : undefined; } get currentViewContainerId() { return this.currentContainerId; } get headerElement() { return this.header; } get collapsed() { return this._collapsed; } set collapsed(collapsed) { // Cannot collapse/expand if the orientation of the container is `horizontal`. const orientation = ViewContainer.getOrientation(this.node); if (this._collapsed === collapsed || (orientation === 'horizontal' && collapsed)) { return; } this._collapsed = collapsed; this.node.classList.toggle('collapsed', collapsed); if (collapsed && this.wrapped.node.contains(document.activeElement)) { this.header.focus(); } this.wrapped.setHidden(collapsed); const toggleIcon = this.header.querySelector(`span.${widgets_1.EXPANSION_TOGGLE_CLASS}`); if (toggleIcon) { if (collapsed) { toggleIcon.classList.add(widgets_1.COLLAPSED_CLASS); } else { toggleIcon.classList.remove(widgets_1.COLLAPSED_CLASS); } } this.update(); this.collapsedEmitter.fire(collapsed); } onPartMoved(newContainer) { this.currentContainerId = newContainer.id; this.onPartMovedEmitter.fire(newContainer); } setHidden(hidden) { if (!this.canHide) { return; } super.setHidden(hidden); } get canHide() { return this.options.canHide === undefined || this.options.canHide; } get onCollapsed() { return this.collapsedEmitter.event; } get onContextMenu() { return this.contextMenuEmitter.event; } get minSize() { const style = getComputedStyle(this.body); if (ViewContainer.getOrientation(this.node) === 'horizontal') { return (0, browser_1.parseCssMagnitude)(style.minWidth, 0); } else { return (0, browser_1.parseCssMagnitude)(style.minHeight, 0); } } showTitle() { this.toShowHeader.dispose(); } hideTitle() { if (this.titleHidden) { return; } const display = this.header.style.display; const height = this.body.style.height; this.body.style.height = '100%'; this.header.style.display = 'none'; this.toShowHeader.push(disposable_1.Disposable.create(() => { this.header.style.display = display; this.body.style.height = height; })); } get titleHidden() { return !this.toShowHeader.disposed || this.collapsed; } getScrollContainer() { return this.body; } registerContextMenu() { return new disposable_1.DisposableCollection((0, widgets_1.addEventListener)(this.header, 'contextmenu', event => { this.contextMenuEmitter.fire(event); })); } createContent() { const disposable = new disposable_1.DisposableCollection(); const { header, disposable: headerDisposable } = this.createHeader(); const body = document.createElement('div'); body.classList.add('body'); this.node.appendChild(header); this.node.appendChild(body); disposable.push(headerDisposable); return { header, body, disposable, }; } createHeader() { const disposable = new disposable_1.DisposableCollection(); const header = document.createElement('div'); header.tabIndex = 0; header.classList.add('theia-header', 'header', 'theia-view-container-part-header'); disposable.push((0, widgets_1.addEventListener)(header, 'click', event => { if (this.toolbar && this.toolbar.shouldHandleMouseEvent(event)) { return; } this.collapsed = !this.collapsed; })); disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ARROW_LEFT, () => this.collapsed = true)); disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ARROW_RIGHT, () => this.collapsed = false)); disposable.push((0, widgets_1.addKeyListener)(header, keys_1.Key.ENTER, () => this.collapsed = !this.collapsed)); const toggleIcon = document.createElement('span'); toggleIcon.classList.add(widgets_1.EXPANSION_TOGGLE_CLASS, ...widgets_1.CODICON_TREE_ITEM_CLASSES); if (this.collapsed) { toggleIcon.classList.add(widgets_1.COLLAPSED_CLASS); } header.appendChild(toggleIcon); const title = document.createElement('span'); title.classList.add('label', 'noselect'); const description = document.createElement('span'); description.classList.add('description'); const badgeSpan = document.createElement('span'); badgeSpan.classList.add('notification-count'); const badgeContainer = document.createElement('div'); badgeContainer.classList.add('notification-count-container'); badgeContainer.appendChild(badgeSpan); const badgeContainerDisplay = badgeContainer.style.display; const updateTitle = () => { var _a; if (this.currentContainerId !== this.originalContainerId && ((_a = this.originalContainerTitle) === null || _a === void 0 ? void 0 : _a.label)) { // Creating a title in format: <original_container_title>: <part_title>. title.innerText = this.originalContainerTitle.label + ': ' + this.wrapped.title.label; } else { title.innerText = this.wrapped.title.label; } }; const updateCaption = () => title.title = this.wrapped.title.caption || this.wrapped.title.label; const updateDescription = () => { description.innerText = DescriptionWidget.is(this.wrapped) && !this.collapsed && this.wrapped.description || ''; }; const updateBadge = () => { if (BadgeWidget.is(this.wrapped)) { const visibleToolBarItems = this.toolbarRegistry.visibleItems(this.wrapped).length > 0; const badge = this.wrapped.badge; if (badge && !visibleToolBarItems) { badgeSpan.innerText = badge.toString(); badgeSpan.title = this.wrapped.badgeTooltip || ''; badgeContainer.style.display = badgeContainerDisplay; return; } } badgeContainer.style.display = 'none'; }; updateTitle(); updateCaption(); updateDescription(); updateBadge(); disposable.pushAll([ this.onTitleChanged(updateTitle), this.onTitleChanged(updateCaption), this.onDidMove(updateTitle), this.onDidChangeDescription(updateDescription), this.onDidChangeBadge(updateBadge), this.onDidChangeBadgeTooltip(updateBadge), this.onCollapsed(updateDescription) ]); header.appendChild(title); header.appendChild(description); header.appendChild(badgeContainer); return { header, disposable }; } handleResize() { var _a; const handleMouseEnter = () => { var _a; (_a = this.node) === null || _a === void 0 ? void 0 : _a.classList.add('no-pointer-events'); setTimeout(() => { var _a, _b; (_a = this.node) === null || _a === void 0 ? void 0 : _a.classList.remove('no-pointer-events'); (_b = this.node) === null || _b === void 0 ? void 0 : _b.removeEventListener('mouseenter', handleMouseEnter); }, 100); }; (_a = this.node) === null || _a === void 0 ? void 0 : _a.addEventListener('mouseenter', handleMouseEnter); } onResize(msg) { this.handleResize(); if (this.wrapped.isAttached && !this.collapsed) { widgets_1.MessageLoop.sendMessage(this.wrapped, widgets_1.Widget.ResizeMessage.UnknownSize); } super.onResize(msg); } onUpdateRequest(msg) { if (this.wrapped.isAttached && !this.collapsed) { widgets_1.MessageLoop.sendMessage(this.wrapped, msg); } super.onUpdateRequest(msg); } onAfterAttach(msg) { if (!this.wrapped.isAttached) { widgets_1.UnsafeWidgetUtilities.attach(this.wrapped, this.body); } widgets_1.UnsafeWidgetUtilities.attach(this.toolbar, this.header); super.onAfterAttach(msg); } onBeforeDetach(msg) { super.onBeforeDetach(msg); if (this.toolbar.isAttached) { widgets_1.Widget.detach(this.toolbar); } if (this.wrapped.isAttached) { widgets_1.UnsafeWidgetUtilities.detach(this.wrapped); } } onBeforeShow(msg) { if (this.wrapped.isAttached && !this.collapsed) { widgets_1.MessageLoop.sendMessage(this.wrapped, msg); } super.onBeforeShow(msg); }