@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
1,150 lines (1,149 loc) • 62.8 kB
JavaScript
"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);
}