Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -6,7 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import types = require('vs/base/common/types');
import * as types from 'vs/base/common/types';
import { Action, IAction } from 'vs/base/common/actions';
import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
@@ -26,11 +26,11 @@ class ToggleCenteredLayout extends Action {
}
public run(): TPromise<any> {
this.partService.toggleCenteredEditorLayout();
this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered());
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';

View File

@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';

View File

@@ -6,7 +6,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';

View File

@@ -4,18 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Component } from 'vs/workbench/common/component';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IComposite } from 'vs/workbench/common/composite';
import { IEditorControl } from 'vs/platform/editor/common/editor';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import DOM = require('vs/base/browser/dom');
import { IDisposable } from 'vs/base/common/lifecycle';
import { IFocusTracker, trackFocus, Dimension } from 'vs/base/browser/dom';
/**
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
@@ -28,14 +27,14 @@ import { IDisposable } from 'vs/base/common/lifecycle';
* layout and focus call, but only one create and dispose call.
*/
export abstract class Composite extends Component implements IComposite {
private _onTitleAreaUpdate: Emitter<void>;
private _onDidFocus: Emitter<void>;
private readonly _onTitleAreaUpdate: Emitter<void>;
private readonly _onDidFocus: Emitter<void>;
private _focusTracker?: DOM.IFocusTracker;
private _focusTracker?: IFocusTracker;
private _focusListenerDisposable?: IDisposable;
private visible: boolean;
private parent: Builder;
private parent: HTMLElement;
protected actionRunner: IActionRunner;
@@ -75,7 +74,7 @@ export abstract class Composite extends Component implements IComposite {
* Note that DOM-dependent calculations should be performed from the setVisible()
* call. Only then the composite will be part of the DOM.
*/
public create(parent: Builder): TPromise<void> {
public create(parent: HTMLElement): TPromise<void> {
this.parent = parent;
return TPromise.as(null);
@@ -88,12 +87,12 @@ export abstract class Composite extends Component implements IComposite {
/**
* Returns the container this composite is being build in.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
public get onDidFocus(): Event<any> {
this._focusTracker = DOM.trackFocus(this.getContainer().getHTMLElement());
this._focusTracker = trackFocus(this.getContainer());
this._focusListenerDisposable = this._focusTracker.onDidFocus(() => {
this._onDidFocus.fire();
});
@@ -243,6 +242,10 @@ export abstract class CompositeDescriptor<T extends Composite> {
}
export abstract class CompositeRegistry<T extends Composite> {
private readonly _onDidRegister: Emitter<CompositeDescriptor<T>> = new Emitter<CompositeDescriptor<T>>();
readonly onDidRegister: Event<CompositeDescriptor<T>> = this._onDidRegister.event;
private composites: CompositeDescriptor<T>[];
constructor() {
@@ -255,6 +258,7 @@ export abstract class CompositeRegistry<T extends Composite> {
}
this.composites.push(descriptor);
this._onDidRegister.fire(descriptor);
}
public getComposite(id: string): CompositeDescriptor<T> {

View File

@@ -7,7 +7,7 @@
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { extname, basename } from 'vs/base/common/paths';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@@ -348,7 +348,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
}
}
export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: (URI | IFileStat)[], event: DragMouseEvent | DragEvent): void {
export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: (URI | { resource: URI, isDirectory: boolean })[], event: DragMouseEvent | DragEvent): void {
if (resources.length === 0) {
return;
}
@@ -455,4 +455,4 @@ export class LocalSelectionTransfer<T> {
this.proto = proto;
}
}
}
}

View File

@@ -6,7 +6,7 @@
'use strict';
import uri from 'vs/base/common/uri';
import resources = require('vs/base/common/resources');
import * as resources from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -25,6 +25,7 @@ import { Schemas } from 'vs/base/common/network';
import { FileKind, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { ITextModel } from 'vs/editor/common/model';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Event, Emitter } from 'vs/base/common/event';
export interface IResourceLabel {
name: string;
@@ -38,11 +39,16 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions {
}
export class ResourceLabel extends IconLabel {
private toDispose: IDisposable[];
private label: IResourceLabel;
private options: IResourceLabelOptions;
private computedIconClasses: string[];
private lastKnownConfiguredLangId: string;
private computedPathLabel: string;
private _onDidRender = new Emitter<void>();
readonly onDidRender: Event<void> = this._onDidRender.event;
constructor(
container: HTMLElement,
@@ -121,6 +127,10 @@ export class ResourceLabel extends IconLabel {
this.label = label;
this.options = options;
if (hasResourceChanged) {
this.computedPathLabel = void 0; // reset path label due to resource change
}
this.render(hasResourceChanged);
}
@@ -151,6 +161,7 @@ export class ResourceLabel extends IconLabel {
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
this.computedPathLabel = void 0;
this.setValue();
}
@@ -184,7 +195,11 @@ export class ResourceLabel extends IconLabel {
if (this.options && typeof this.options.title === 'string') {
iconLabelOptions.title = this.options.title;
} else if (resource && resource.scheme !== Schemas.data /* do not accidentally inline Data URIs */) {
iconLabelOptions.title = getPathLabel(resource, void 0, this.environmentService);
if (!this.computedPathLabel) {
this.computedPathLabel = getPathLabel(resource, void 0, this.environmentService);
}
iconLabelOptions.title = this.computedPathLabel;
}
if (!this.computedIconClasses) {
@@ -197,7 +212,7 @@ export class ResourceLabel extends IconLabel {
}
if (this.options && this.options.fileDecorations && resource) {
let deco = this.decorationsService.getDecoration(
const deco = this.decorationsService.getDecoration(
resource,
this.options.fileKind !== FileKind.FILE,
this.options.fileDecorations.data
@@ -207,9 +222,11 @@ export class ResourceLabel extends IconLabel {
if (deco.tooltip) {
iconLabelOptions.title = `${iconLabelOptions.title}${deco.tooltip}`;
}
if (this.options.fileDecorations.colors) {
iconLabelOptions.extraClasses.push(deco.labelClassName);
}
if (this.options.fileDecorations.badges) {
iconLabelOptions.extraClasses.push(deco.badgeClassName);
}
@@ -217,6 +234,8 @@ export class ResourceLabel extends IconLabel {
}
this.setValue(label, this.label.description, iconLabelOptions);
this._onDidRender.fire();
}
public dispose(): void {
@@ -227,6 +246,7 @@ export class ResourceLabel extends IconLabel {
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
this.computedPathLabel = void 0;
}
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { Part } from 'vs/workbench/browser/part';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IPartService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/part/common/partService';
@@ -22,11 +22,14 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { memoize } from 'vs/base/common/decorators';
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
import { Dimension, getClientArea, size, position, hide, show } from 'vs/base/browser/dom';
const MIN_SIDEBAR_PART_WIDTH = 170;
const DEFAULT_SIDEBAR_PART_WIDTH = 300;
const MIN_EDITOR_PART_HEIGHT = 70;
const MIN_EDITOR_PART_WIDTH = 220;
const MIN_PANEL_PART_HEIGHT = 77;
const DEFAULT_PANEL_PART_SIZE = 350;
const MIN_PANEL_PART_WIDTH = 300;
const DEFAULT_PANEL_SIZE_COEFFICIENT = 0.4;
const PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY = 0.7;
@@ -56,8 +59,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
private static readonly sashYHeightSettingsKey = 'workbench.panel.height';
private static readonly panelSizeBeforeMaximizedKey = 'workbench.panel.sizeBeforeMaximized';
private parent: Builder;
private workbenchContainer: Builder;
private parent: HTMLElement;
private workbenchContainer: HTMLElement;
private titlebar: Part;
private activitybar: Part;
private editor: Part;
@@ -65,6 +68,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
private panel: Part;
private statusbar: Part;
private quickopen: QuickOpenController;
private quickInput: QuickInputService;
private notificationsCenter: NotificationsCenter;
private notificationsToasts: NotificationsToasts;
private toUnbind: IDisposable[];
@@ -84,8 +88,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
// Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments
constructor(
parent: Builder,
workbenchContainer: Builder,
parent: HTMLElement,
workbenchContainer: HTMLElement,
parts: {
titlebar: Part,
activitybar: Part,
@@ -95,6 +99,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
statusbar: Part
},
quickopen: QuickOpenController,
quickInput: QuickInputService,
notificationsCenter: NotificationsCenter,
notificationsToasts: NotificationsToasts,
@IStorageService private storageService: IStorageService,
@@ -114,28 +119,29 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.panel = parts.panel;
this.statusbar = parts.statusbar;
this.quickopen = quickopen;
this.quickInput = quickInput;
this.notificationsCenter = notificationsCenter;
this.notificationsToasts = notificationsToasts;
this.toUnbind = [];
this.panelSizeBeforeMaximized = this.storageService.getInteger(WorkbenchLayout.panelSizeBeforeMaximizedKey, StorageScope.GLOBAL, 0);
this.panelMaximized = false;
this.sashXOne = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashXOne = new Sash(this.workbenchContainer, this, {
baseSize: 5
});
this.sashXTwo = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashXTwo = new Sash(this.workbenchContainer, this, {
baseSize: 5
});
this.sashY = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashY = new Sash(this.workbenchContainer, this, {
baseSize: 4,
orientation: Orientation.HORIZONTAL
});
this._sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, -1);
this._panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0);
this._panelWidth = this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, 0);
this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_SIDEBAR_PART_WIDTH));
this._panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
this._panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
@@ -397,7 +403,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.toUnbind.push(this.sashXOne.onDidReset(() => {
let activeViewlet = this.viewletService.getActiveViewlet();
let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
this.sidebarWidth = optimalWidth || 0;
this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH);
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError);
}));
@@ -441,7 +447,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
public layout(options?: ILayoutOptions): void {
this.workbenchSize = this.parent.getClientArea();
this.workbenchSize = getClientArea(this.parent);
const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART);
@@ -572,12 +578,11 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Workbench
this.workbenchContainer
.position(0, 0, 0, 0, 'relative')
.size(this.workbenchSize.width, this.workbenchSize.height);
position(this.workbenchContainer, 0, 0, 0, 0, 'relative');
size(this.workbenchContainer, this.workbenchSize.width, this.workbenchSize.height);
// Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case.
const workbenchContainer = this.workbenchContainer.getHTMLElement();
const workbenchContainer = this.workbenchContainer;
if (workbenchContainer.scrollTop > 0) {
workbenchContainer.scrollTop = 0;
}
@@ -586,69 +591,78 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Title Part
const titleContainer = this.titlebar.getContainer();
if (isTitlebarHidden) {
this.titlebar.getContainer().hide();
hide(titleContainer);
} else {
this.titlebar.getContainer().show();
show(titleContainer);
}
// Editor Part and Panel part
this.editor.getContainer().size(editorSize.width, editorSize.height);
this.panel.getContainer().size(panelDimension.width, panelDimension.height);
const editorContainer = this.editor.getContainer();
const panelContainer = this.panel.getContainer();
size(editorContainer, editorSize.width, editorSize.height);
size(panelContainer, panelDimension.width, panelDimension.height);
if (panelPosition === Position.BOTTOM) {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
}
} else {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
this.panel.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
}
}
// Activity Bar Part
this.activitybar.getContainer().size(null, activityBarSize.height);
const activitybarContainer = this.activitybar.getContainer();
size(activitybarContainer, null, activityBarSize.height);
if (sidebarPosition === Position.LEFT) {
this.activitybar.getContainer().getHTMLElement().style.right = '';
this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
this.activitybar.getContainer().style.right = '';
position(activitybarContainer, this.titlebarHeight, null, 0, 0);
} else {
this.activitybar.getContainer().getHTMLElement().style.left = '';
this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
this.activitybar.getContainer().style.left = '';
position(activitybarContainer, this.titlebarHeight, 0, 0, null);
}
if (isActivityBarHidden) {
this.activitybar.getContainer().hide();
hide(activitybarContainer);
} else {
this.activitybar.getContainer().show();
show(activitybarContainer);
}
// Sidebar Part
this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
const sidebarContainer = this.sidebar.getContainer();
size(sidebarContainer, sidebarSize.width, sidebarSize.height);
const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
if (sidebarPosition === Position.LEFT) {
this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
} else {
this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
}
// Statusbar Part
this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
const statusbarContainer = this.statusbar.getContainer();
position(statusbarContainer, this.workbenchSize.height - this.statusbarHeight);
if (isStatusbarHidden) {
this.statusbar.getContainer().hide();
hide(statusbarContainer);
} else {
this.statusbar.getContainer().show();
show(statusbarContainer);
}
// Quick open
this.quickopen.layout(this.workbenchSize);
// Quick input
this.quickInput.layout(this.workbenchSize);
// Notifications
this.notificationsCenter.layout(this.workbenchSize);
this.notificationsToasts.layout(this.workbenchSize);
@@ -786,4 +800,4 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.toUnbind = null;
}
}
}
}

View File

@@ -68,7 +68,7 @@
font-size: 13px;
}
.monaco-workbench > .part > .content .progress-container {
.monaco-workbench > .part > .content .monaco-progress-container {
position: absolute;
left: 0;
top: 33px; /* at the bottom of the 35px height title container */
@@ -76,6 +76,6 @@
height: 2px;
}
.monaco-workbench > .part > .content .progress-container .progress-bit {
.monaco-workbench > .part > .content .monaco-progress-container .progress-bit {
height: 2px;
}

View File

@@ -95,7 +95,7 @@ export abstract class TogglePanelAction extends Action {
const activePanel = this.panelService.getActivePanel();
const activeElement = document.activeElement;
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer().getHTMLElement());
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer());
}
}

View File

@@ -6,9 +6,9 @@
'use strict';
import 'vs/css!./media/part';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { Component } from 'vs/workbench/common/component';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { Dimension, size } from 'vs/base/browser/dom';
export interface IPartOptions {
hasTitle?: boolean;
@@ -20,9 +20,9 @@ export interface IPartOptions {
* and mandatory content area to show content.
*/
export abstract class Part extends Component {
private parent: Builder;
private titleArea: Builder;
private contentArea: Builder;
private parent: HTMLElement;
private titleArea: HTMLElement;
private contentArea: HTMLElement;
private partLayout: PartLayout;
constructor(
@@ -47,7 +47,7 @@ export abstract class Part extends Component {
*
* Called to create title and content area of the part.
*/
public create(parent: Builder): void {
public create(parent: HTMLElement): void {
this.parent = parent;
this.titleArea = this.createTitleArea(parent);
this.contentArea = this.createContentArea(parent);
@@ -60,35 +60,35 @@ export abstract class Part extends Component {
/**
* Returns the overall part container.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
/**
* Subclasses override to provide a title area implementation.
*/
protected createTitleArea(parent: Builder): Builder {
protected createTitleArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the title area container.
*/
protected getTitleArea(): Builder {
protected getTitleArea(): HTMLElement {
return this.titleArea;
}
/**
* Subclasses override to provide a content area implementation.
*/
protected createContentArea(parent: Builder): Builder {
protected createContentArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the content area container.
*/
protected getContentArea(): Builder {
protected getContentArea(): HTMLElement {
return this.contentArea;
}
@@ -104,8 +104,7 @@ const TITLE_HEIGHT = 35;
export class PartLayout {
constructor(container: Builder, private options: IPartOptions, titleArea: Builder, private contentArea: Builder) {
}
constructor(container: HTMLElement, private options: IPartOptions, titleArea: HTMLElement, private contentArea: HTMLElement) { }
public layout(dimension: Dimension): Dimension[] {
const { width, height } = dimension;
@@ -133,7 +132,7 @@ export class PartLayout {
// Content
if (this.contentArea) {
this.contentArea.size(contentSize.width, contentSize.height);
size(this.contentArea, contentSize.width, contentSize.height);
}
return sizes;

View File

@@ -6,7 +6,7 @@
'use strict';
import 'vs/css!./media/activityaction';
import DOM = require('vs/base/browser/dom');
import * as DOM from 'vs/base/browser/dom';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
@@ -22,6 +22,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActivityAction, ActivityActionItem, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class ViewletActivityAction extends ActivityAction {
@@ -32,7 +33,8 @@ export class ViewletActivityAction extends ActivityAction {
constructor(
activity: IActivity,
@IViewletService private viewletService: IViewletService,
@IPartService private partService: IPartService
@IPartService private partService: IPartService,
@ITelemetryService private telemetryService: ITelemetryService
) {
super(activity);
}
@@ -54,11 +56,23 @@ export class ViewletActivityAction extends ActivityAction {
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) {
this.logAction('hide');
return this.partService.setSideBarHidden(true);
}
this.logAction('show');
return this.viewletService.openViewlet(this.activity.id, true).then(() => this.activate());
}
private logAction(action: string) {
/* __GDPR__
"activityBarAction" : {
"viewletId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"action": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('activityBarAction', { viewletId: this.activity.id, action });
}
}
export class ToggleViewletAction extends Action {

View File

@@ -6,10 +6,10 @@
'use strict';
import 'vs/css!./media/activitybarpart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { illegalArgument } from 'vs/base/common/errors';
import { Builder, $, Dimension } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
@@ -30,8 +30,10 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar';
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
// {{SQL CARBON EDIT}}
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { Dimension } from 'vs/base/browser/dom';
export class ActivitybarPart extends Part {
@@ -66,6 +68,7 @@ export class ActivitybarPart extends Part {
super(id, { hasTitle: false }, themeService);
this.globalActivityIdToActions = Object.create(null);
this.compositeBar = this.instantiationService.createInstance(CompositeBar, {
icon: true,
storageId: ActivitybarPart.PINNED_VIEWLETS,
@@ -80,7 +83,9 @@ export class ActivitybarPart extends Part {
colors: ActivitybarPart.COLORS,
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
});
this.registerListeners();
this.updateCompositebar();
}
// {{SQL CARBON EDIT}}
@@ -97,6 +102,10 @@ export class ActivitybarPart extends Part {
private registerListeners(): void {
this.toUnbind.push(this.viewletService.onDidViewletRegister(() => this.updateCompositebar()));
this.toUnbind.push(ViewsRegistry.onViewsRegistered(() => this.updateCompositebar()));
this.toUnbind.push(ViewsRegistry.onViewsDeregistered(() => this.updateCompositebar()));
// Activate viewlet action on opening of a viewlet
this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId())));
@@ -105,7 +114,7 @@ export class ActivitybarPart extends Part {
this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e)));
this.toUnbind.push(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => {
if (enabled) {
this.compositeBar.addComposite(this.viewletService.getViewlet(id));
this.compositeBar.addComposite(this.viewletService.getViewlet(id), true);
} else {
this.compositeBar.removeComposite(id);
}
@@ -135,7 +144,7 @@ export class ActivitybarPart extends Part {
return toDisposable(() => action.setBadge(undefined));
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
const $el = $(parent);
const $result = $('.content').appendTo($el);
@@ -145,14 +154,14 @@ export class ActivitybarPart extends Part {
// Top Actionbar with action items for each viewlet action
this.createGlobalActivityActionBar($('.global-activity').appendTo($result).getHTMLElement());
return $result;
return $result.getHTMLElement();
}
public updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
const container = $(this.getContainer());
const background = this.getColor(ACTIVITY_BAR_BACKGROUND);
container.style('background-color', background);
@@ -170,7 +179,9 @@ export class ActivitybarPart extends Part {
private showContextMenu(e: MouseEvent): void {
const event = new StandardMouseEvent(e);
const actions: Action[] = this.viewletService.getViewlets().map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar));
const actions: Action[] = this.viewletService.getViewlets()
.filter(viewlet => this.canShow(viewlet))
.map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar));
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
@@ -202,6 +213,26 @@ export class ActivitybarPart extends Part {
});
}
private updateCompositebar(): void {
const viewlets = this.viewletService.getViewlets();
for (const viewlet of viewlets) {
const canShow = this.canShow(viewlet);
if (canShow) {
this.compositeBar.addComposite(viewlet, false);
} else {
this.compositeBar.removeComposite(viewlet.id);
}
}
}
private canShow(viewlet: ViewletDescriptor): boolean {
const viewLocation = ViewLocation.get(viewlet.id);
if (viewLocation) {
return ViewsRegistry.getViews(viewLocation).length > 0;
}
return true;
}
public getPinned(): string[] {
return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id));
}
@@ -229,6 +260,11 @@ export class ActivitybarPart extends Part {
return sizes;
}
public shutdown(): void {
this.compositeBar.shutdown();
super.shutdown();
}
public dispose(): void {
if (this.compositeBar) {
this.compositeBar.dispose();
@@ -242,4 +278,4 @@ export class ActivitybarPart extends Part {
super.dispose();
}
}
}

View File

@@ -19,7 +19,7 @@
background-color: inherit;
}
.monaco-workbench > .activitybar [tabindex="0"]:focus {
.monaco-workbench > .activitybar .action-item:focus {
outline: 0 !important; /* activity bar indicates focus custom */
}

View File

@@ -6,22 +6,20 @@
'use strict';
import 'vs/css!./media/compositepart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import strings = require('vs/base/common/strings');
import { Builder, $ } from 'vs/base/browser/builder';
import * as strings from 'vs/base/common/strings';
import { Emitter } from 'vs/base/common/event';
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as DOM from 'vs/base/browser/dom';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { prepareActions } from 'vs/workbench/browser/actions';
import { Action, IAction, IRunEvent } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { Part, IPartOptions } from 'vs/workbench/browser/part';
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
@@ -37,6 +35,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension } from 'vs/base/browser/dom';
export interface ICompositeTitleLabel {
@@ -74,10 +73,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
private telemetryService: ITelemetryService,
protected contextMenuService: IContextMenuService,
protected partService: IPartService,
private keybindingService: IKeybindingService,
protected keybindingService: IKeybindingService,
protected instantiationService: IInstantiationService,
themeService: IThemeService,
private registry: CompositeRegistry<T>,
protected readonly registry: CompositeRegistry<T>,
private activeCompositeSettingsKey: string,
private defaultCompositeId: string,
private nameForTelemetry: string,
@@ -223,8 +222,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
compositeContainer = $().div({
'class': ['composite', this.compositeCSSClass],
id: composite.getId()
}, (div: Builder) => {
createCompositePromise = composite.create(div).then(() => {
}, div => {
createCompositePromise = composite.create(div.getHTMLElement()).then(() => {
composite.updateStyles();
});
});
@@ -279,7 +278,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
}
// Action Run Handling
this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun((e: IRunEvent) => {
this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => {
// Check for Error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
@@ -311,7 +310,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
composite.layout(this.contentAreaSize);
}
});
}, (error: any) => this.onError(error));
}, error => this.onError(error));
}
protected onTitleAreaUpdate(compositeId: string): void {
@@ -391,7 +390,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
compositeContainer.hide();
// Clear any running Progress
this.progressBar.stop().getContainer().hide();
this.progressBar.stop().hide();
// Empty Actions
this.toolBar.setActions([])();
@@ -401,39 +400,37 @@ export abstract class CompositePart<T extends Composite> extends Part {
});
}
public createTitleArea(parent: Builder): Builder {
public createTitleArea(parent: HTMLElement): HTMLElement {
// Title Area Container
const titleArea = $(parent).div({
'class': ['composite', 'title']
});
$(titleArea).on(DOM.EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)));
// Left Title Label
this.titleLabel = this.createTitleLabel(titleArea);
this.titleLabel = this.createTitleLabel(titleArea.getHTMLElement());
// Right Actions Container
$(titleArea).div({
'class': 'title-actions'
}, (div) => {
}, div => {
// Toolbar
this.toolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
actionItemProvider: (action: Action) => this.actionItemProvider(action),
actionItemProvider: action => this.actionItemProvider(action as Action),
orientation: ActionsOrientation.HORIZONTAL,
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id)
});
});
return titleArea;
return titleArea.getHTMLElement();
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel {
let titleLabel: Builder;
$(parent).div({
'class': 'title-label'
}, (div) => {
}, div => {
titleLabel = div.span();
});
@@ -456,27 +453,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
this.titleLabel.updateStyles();
}
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
if (this.activeComposite) {
const contextMenuActions = this.getTitleAreaContextMenuActions();
if (contextMenuActions.length) {
const anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(contextMenuActions),
getActionItem: (action: Action) => this.actionItemProvider(action),
actionRunner: this.activeComposite.getActionRunner(),
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
});
}
}
}
protected actionItemProvider(action: Action): IActionItem {
protected getTitleAreaContextMenuActions(): IAction[] {
return this.activeComposite ? this.activeComposite.getContextMenuActions() : [];
}
private actionItemProvider(action: Action): IActionItem {
// Check Active Composite
if (this.activeComposite) {
return this.activeComposite.getActionItem(action);
@@ -485,14 +463,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
return undefined;
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
return $(parent).div({
'class': 'content'
}, (div: Builder) => {
this.progressBar = new ProgressBar(div);
}, div => {
this.progressBar = new ProgressBar(div.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.getContainer().hide();
});
this.progressBar.hide();
}).getHTMLElement();
}
private onError(error: any): void {

View File

@@ -5,26 +5,25 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { illegalArgument } from 'vs/base/common/errors';
import * as dom from 'vs/base/browser/dom';
import * as arrays from 'vs/base/common/arrays';
import { Dimension } from 'vs/base/browser/builder';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ActionBar, IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom';
export interface ICompositeBarOptions {
icon: boolean;
storageId: string;
orientation: ActionsOrientation;
composites: { id: string, name: string }[];
composites: { id: string, name: string, order: number }[];
colors: ICompositeBarColors;
overflowActionSize: number;
getActivityAction: (compositeId: string) => ActivityAction;
@@ -35,9 +34,14 @@ export interface ICompositeBarOptions {
hidePart: () => TPromise<any>;
}
interface CompositeState {
id: string;
pinned: boolean;
}
export class CompositeBar implements ICompositeBar {
private _onDidContextMenu: Emitter<MouseEvent>;
private readonly _onDidContextMenu: Emitter<MouseEvent>;
private dimension: Dimension;
private toDispose: IDisposable[];
@@ -51,6 +55,7 @@ export class CompositeBar implements ICompositeBar {
private compositeIdToActivityStack: { [compositeId: string]: ICompositeActivity[]; };
private compositeSizeInBar: Map<string, number>;
private initialCompositesStates: CompositeState[];
private pinnedComposites: string[];
private activeCompositeId: string;
private activeUnpinnedCompositeId: string;
@@ -67,27 +72,36 @@ export class CompositeBar implements ICompositeBar {
this.compositeSizeInBar = new Map<string, number>();
this._onDidContextMenu = new Emitter<MouseEvent>();
const pinnedComposites = JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, null)) as string[];
if (pinnedComposites) {
const compositeIds = this.options.composites.map(c => c.id);
this.pinnedComposites = pinnedComposites.filter(pcid => compositeIds.indexOf(pcid) >= 0);
} else {
this.pinnedComposites = this.options.composites.map(c => c.id);
}
this.initialCompositesStates = this.loadCompositesStates();
this.pinnedComposites = this.initialCompositesStates
.filter(c => c.pinned)
.map(c => c.id)
.filter(id => this.options.composites.some(c => c.id === id));
}
public get onDidContextMenu(): Event<MouseEvent> {
return this._onDidContextMenu.event;
}
public addComposite(compositeData: { id: string; name: string }): void {
public addComposite(compositeData: { id: string; name: string, order: number }, activate: boolean): void {
if (this.options.composites.filter(c => c.id === compositeData.id).length) {
return;
}
this.options.composites.push(compositeData);
this.pin(compositeData.id);
const compositeState = this.initialCompositesStates.filter(c => c.id === compositeData.id)[0];
if (!compositeState /* new composites are pinned by default */ || compositeState.pinned) {
let index;
if (compositeState) {
index = this.initialCompositesStates.indexOf(compositeState);
} else {
index = 0;
while (index < this.options.composites.length && this.options.composites[index].order < compositeData.order) {
index++;
}
}
this.pin(compositeData.id, true, index, activate);
}
}
public removeComposite(id: string): void {
@@ -97,6 +111,9 @@ export class CompositeBar implements ICompositeBar {
this.options.composites = this.options.composites.filter(c => c.id !== id);
this.unpin(id);
this.pullComposite(id);
// Only at the end deactivate composite so the unpin and pull properly finish
this.deactivateComposite(id);
}
public activateComposite(id: string): void {
@@ -119,6 +136,13 @@ export class CompositeBar implements ICompositeBar {
if (this.compositeIdToActions[id]) {
this.compositeIdToActions[id].deactivate();
}
if (this.activeCompositeId === id) {
this.activeCompositeId = undefined;
}
if (this.activeUnpinnedCompositeId === id) {
this.updateCompositeSwitcher();
this.activeUnpinnedCompositeId = undefined;
}
}
public showActivity(compositeId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
@@ -191,7 +215,7 @@ export class CompositeBar implements ICompositeBar {
}
public create(parent: HTMLElement): HTMLElement {
const actionBarDiv = parent.appendChild(dom.$('.composite-bar'));
const actionBarDiv = parent.appendChild($('.composite-bar'));
this.compositeSwitcherBar = new ActionBar(actionBarDiv, {
actionItemProvider: (action: Action) => action instanceof CompositeOverflowActivityAction ? this.compositeOverflowActionItem : this.compositeIdToActionItems[action.id],
orientation: this.options.orientation,
@@ -201,16 +225,16 @@ export class CompositeBar implements ICompositeBar {
this.toDispose.push(this.compositeSwitcherBar);
// Contextmenu for composites
this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.CONTEXT_MENU, (e: MouseEvent) => {
dom.EventHelper.stop(e, true);
this.toDispose.push(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => {
EventHelper.stop(e, true);
this._onDidContextMenu.fire(e);
}));
// Allow to drop at the end to move composites to the end
this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.DROP, (e: DragEvent) => {
this.toDispose.push(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
const draggedCompositeId = CompositeActionItem.getDraggedCompositeId();
if (draggedCompositeId) {
dom.EventHelper.stop(e, true);
EventHelper.stop(e, true);
CompositeActionItem.clearDraggedComposite();
const targetId = this.pinnedComposites[this.pinnedComposites.length - 1];
@@ -424,20 +448,21 @@ export class CompositeBar implements ICompositeBar {
});
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
}
public isPinned(compositeId: string): boolean {
return this.pinnedComposites.indexOf(compositeId) >= 0;
}
public pin(compositeId: string, update = true): void {
public pin(compositeId: string, update = true, index = this.pinnedComposites.length, activate: boolean = true): void {
if (this.isPinned(compositeId)) {
return;
}
this.options.openComposite(compositeId).then(() => {
this.pinnedComposites.push(compositeId);
const activatePromise = activate ? this.options.openComposite(compositeId) : TPromise.as(null);
activatePromise.then(() => {
this.pinnedComposites.splice(index, 0, compositeId);
this.pinnedComposites = arrays.distinct(this.pinnedComposites);
if (update) {
@@ -445,7 +470,7 @@ export class CompositeBar implements ICompositeBar {
}
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
});
}
@@ -477,7 +502,7 @@ export class CompositeBar implements ICompositeBar {
}, 0);
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
}
public layout(dimension: Dimension): void {
@@ -501,8 +526,33 @@ export class CompositeBar implements ICompositeBar {
this.updateCompositeSwitcher();
}
private savePinnedComposites(): void {
this.storageService.store(this.options.storageId, JSON.stringify(this.pinnedComposites), StorageScope.GLOBAL);
private loadCompositesStates(): CompositeState[] {
const storedStates = <Array<string | CompositeState>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]'));
const isOldData = storedStates && storedStates.length && typeof storedStates[0] === 'string';
const compositeStates = <CompositeState[]>storedStates.map(c =>
typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true } : c);
if (!isOldData) { /* Add new composites only if it is new data */
const newComposites = this.options.composites.filter(c => compositeStates.every(s => s.id !== c.id));
newComposites.sort((c1, c2) => c1.order < c2.order ? -1 : 1);
newComposites.forEach(c => compositeStates.push({ id: c.id, pinned: true /* new composites are pinned by default */ }));
}
return compositeStates;
}
private saveCompositesStates(): void {
const toSave = this.pinnedComposites.map(id => (<CompositeState>{ id, pinned: true }));
for (const composite of this.options.composites) {
if (this.pinnedComposites.indexOf(composite.id) === -1) { // Unpinned composites
toSave.push({ id: composite.id, pinned: false });
}
}
this.storageService.store(this.options.storageId, JSON.stringify(toSave), StorageScope.GLOBAL);
}
public shutdown(): void {
this.saveCompositesStates();
}
public dispose(): void {

View File

@@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom';
@@ -21,7 +21,7 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
import { IActivity } from 'vs/workbench/common/activity';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
export interface ICompositeActivity {
badge: IBadge;
@@ -193,7 +193,7 @@ export class ActivityActionItem extends BaseActionItem {
this.$label.text(this.getAction().label);
}
this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => {
this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => {
this.$badgeContent = badge.div({ 'class': 'badge-content' });
});
@@ -207,6 +207,10 @@ export class ActivityActionItem extends BaseActionItem {
}
protected updateBadge(badge: IBadge, clazz?: string): void {
if (!this.$badge || !this.$badgeContent) {
return;
}
this.badgeDisposable.dispose();
this.badgeDisposable = empty;

View File

@@ -5,7 +5,6 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
@@ -64,9 +63,9 @@ export abstract class BaseEditor extends Panel implements IEditor {
this._options = null;
}
public create(parent: Builder): void; // create is sync for editors
public create(parent: Builder): TPromise<void>;
public create(parent: Builder): TPromise<void> {
public create(parent: HTMLElement): void; // create is sync for editors
public create(parent: HTMLElement): TPromise<void>;
public create(parent: HTMLElement): TPromise<void> {
const res = super.create(parent);
// Create Editor
@@ -76,9 +75,16 @@ export abstract class BaseEditor extends Panel implements IEditor {
}
/**
* Called to create the editor in the parent builder.
* Called to create the editor in the parent HTMLElement.
*/
protected abstract createEditor(parent: Builder): void;
protected abstract createEditor(parent: HTMLElement): void;
/**
* Subclasses can set this to false if it does not make sense to center editor input.
*/
public supportsCenteredLayout(): boolean {
return true;
}
/**
* Overload this function to allow for passing in a position argument.
@@ -119,4 +125,4 @@ export abstract class BaseEditor extends Panel implements IEditor {
// Super Dispose
super.dispose();
}
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';

View File

@@ -5,41 +5,51 @@
'use strict';
import nls = require('vs/nls');
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import { EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { Builder, $ } from 'vs/base/browser/builder';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
import URI from 'vs/base/common/uri';
import { Dimension } from 'vs/base/browser/dom';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => void;
openExternal: (uri: URI) => void;
}
/*
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseBinaryResourceEditor extends BaseEditor {
private _onMetadataChanged: Emitter<void>;
private metadata: string;
private readonly _onMetadataChanged: Emitter<void>;
private callbacks: IOpenCallbacks;
private metadata: string;
private binaryContainer: Builder;
private scrollbar: DomScrollableElement;
private resourceViewerContext: ResourceViewerContext;
constructor(
id: string,
callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService,
private windowsService: IWindowsService
themeService: IThemeService
) {
super(id, telemetryService, themeService);
this._onMetadataChanged = new Emitter<void>();
this.toUnbind.push(this._onMetadataChanged);
this.callbacks = callbacks;
}
public get onMetadataChanged(): Event<void> {
@@ -50,7 +60,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Container for Binary
const binaryContainerElement = document.createElement('div');
@@ -61,7 +71,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
parent.getHTMLElement().appendChild(this.scrollbar.getDomNode());
parent.appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -74,10 +84,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
return input.resolve(true).then(model => {
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
if (!(model instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
@@ -87,21 +97,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
this.resourceViewerContext = ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
this.binaryContainer,
this.binaryContainer.getHTMLElement(),
this.scrollbar,
(resource: URI) => {
this.windowsService.openExternal(resource.toString()).then(didOpen => {
if (!didOpen) {
return this.windowsService.showItemInFolder(resource.fsPath);
}
return void 0;
});
},
(meta) => this.handleMetadataChanged(meta));
resource => this.callbacks.openInternal(input, options),
resource => this.callbacks.openExternal(resource),
meta => this.handleMetadataChanged(meta)
);
return TPromise.as<void>(null);
});
@@ -117,6 +120,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.metadata;
}
public supportsCenteredLayout(): boolean {
return false;
}
public clearInput(): void {
// Clear Meta

View File

@@ -5,7 +5,7 @@
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { Action, IAction } from 'vs/base/common/actions';
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
@@ -225,7 +225,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction,
export class QuickOpenActionContributor extends ActionBarContributor {
private openToSideActionInstance: OpenToSideAction;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
super();
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { mixin } from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
@@ -543,6 +543,50 @@ export class CloseEditorAction extends Action {
}
}
export class CloseOneEditorAction extends Action {
public static readonly ID = 'workbench.action.closeActiveEditor';
public static readonly LABEL = nls.localize('closeOneEditor', "Close");
constructor(
id: string,
label: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService
) {
super(id, label, 'close-editor-action');
}
public run(context?: IEditorCommandsContext): TPromise<any> {
const model = this.editorGroupService.getStacksModel();
const group = context ? model.getGroup(context.groupId) : null;
const position = group ? model.positionOfGroup(group) : null;
// Close Active Editor
if (typeof position !== 'number') {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
}
}
// Close Specific Editor
const editor = group && context && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : null;
if (editor) {
return this.editorService.closeEditor(position, editor);
}
// Close First Editor at Position
const visibleEditors = this.editorService.getVisibleEditors();
if (visibleEditors[position]) {
return this.editorService.closeEditor(position, visibleEditors[position].input);
}
return TPromise.as(false);
}
}
export class RevertAndCloseEditorAction extends Action {
public static readonly ID = 'workbench.action.revertAndCloseActiveEditor';

View File

@@ -70,7 +70,7 @@ function registerActiveEditorMoveCommand(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: EditorCommands.MoveActiveEditor,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: EditorContextKeys.textFocus,
when: EditorContextKeys.editorTextFocus,
primary: null,
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
description: {
@@ -269,7 +269,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const model = editorGroupService.getStacksModel();
const editorService = accessor.get(IWorkbenchEditorService);
@@ -299,7 +299,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
@@ -324,7 +324,7 @@ function registerEditorCommands() {
when: void 0,
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
@@ -373,7 +373,7 @@ function registerEditorCommands() {
when: void 0,
primary: void 0,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));

View File

@@ -6,16 +6,16 @@
'use strict';
import 'vs/css!./media/editorGroupsControl';
import arrays = require('vs/base/common/arrays');
import Event, { Emitter } from 'vs/base/common/event';
import * as arrays from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import types = require('vs/base/common/types');
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import * as types from 'vs/base/common/types';
import { Builder, $ } from 'vs/base/browser/builder';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import DOM = require('vs/base/browser/dom');
import errors = require('vs/base/common/errors');
import * as DOM from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -29,7 +29,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { ITitleAreaControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput, PREFERENCES_EDITOR_ID, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
@@ -40,6 +40,7 @@ import { ResourcesDropHandler, LocalSelectionTransfer, DraggedEditorIdentifier }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export enum Rochade {
NONE,
TWO_TO_ONE,
@@ -74,7 +75,7 @@ export interface IEditorGroupsControl {
updateProgress(position: Position, state: ProgressState): void;
updateTitleAreas(refreshActive?: boolean): void;
layout(dimension: Dimension): void;
layout(dimension: DOM.Dimension): void;
layout(position: Position): void;
arrangeGroups(arrangement: GroupArrangement): void;
@@ -121,8 +122,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private stacks: IEditorStacksModel;
private parent: Builder;
private dimension: Dimension;
private parent: HTMLElement;
private dimension: DOM.Dimension;
private dragging: boolean;
private layoutVertically: boolean;
@@ -151,7 +152,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private centeredEditorActive: boolean;
private centeredEditorSashLeft: Sash;
private centeredEditorSashRight: Sash;
private centeredEditorPreferedSize: number;
private centeredEditorPreferredSize: number;
private centeredEditorLeftMarginRatio: number;
private centeredEditorDragStartPosition: number;
private centeredEditorDragStartSize: number;
@@ -163,7 +164,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private visibleEditorFocusTrackerDisposable: IDisposable[];
private _onGroupFocusChanged: Emitter<void>;
private readonly _onGroupFocusChanged: Emitter<void>;
private onStacksChangeScheduler: RunOnceScheduler;
private stacksChangedBuffer: IStacksModelChangeEvent[];
@@ -171,12 +172,12 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
constructor(
parent: Builder,
parent: HTMLElement,
groupOrientation: GroupOrientation,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IPartService private partService: IPartService,
@IStorageService private storageServise: IStorageService,
@IStorageService private storageService: IStorageService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionService private extensionService: IExtensionService,
@IInstantiationService private instantiationService: IInstantiationService,
@@ -188,7 +189,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.stacks = editorGroupService.getStacksModel();
this.parent = parent;
this.dimension = new Dimension(0, 0);
this.dimension = new DOM.Dimension(0, 0);
this.silos = [];
this.silosSize = [];
@@ -291,6 +292,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
titleControl.dispose();
titleContainer.empty();
this.createTitleControl(this.stacks.groupAt(position), this.silos[position], titleContainer, this.getInstantiationService(position));
this.layoutTitleControl(position);
}
// Refresh title when layout options change
@@ -369,8 +371,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.trackFocus(editor, position);
// Find target container and build into
const target = this.silos[position].child();
editor.getContainer().build(target);
const target = this.silos[position].child().getHTMLElement();
target.appendChild(editor.getContainer());
// Adjust layout according to provided ratios (used when restoring multiple editors at once)
if (ratio && (ratio.length === 2 || ratio.length === 3)) {
@@ -442,7 +444,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
// Show editor container
editor.getContainer().show();
DOM.show(editor.getContainer());
}
private getVisibleEditorCount(): number {
@@ -554,7 +556,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.clearPosition(position);
// Take editor container offdom and hide
editor.getContainer().offDOM().hide();
const editorContainer = editor.getContainer();
if (editorContainer.parentNode) {
editorContainer.parentNode.removeChild(editorContainer);
}
DOM.hide(editorContainer);
// Adjust layout and rochade if instructed to do so
if (layoutAndRochade) {
@@ -780,10 +786,10 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.layoutVertically = (orientation !== 'horizontal');
// Editor Layout
const verticalLayouting = this.parent.hasClass('vertical-layout');
const verticalLayouting = DOM.hasClass(this.parent, 'vertical-layout');
if (verticalLayouting !== this.layoutVertically) {
this.parent.removeClass('vertical-layout', 'horizontal-layout');
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.removeClasses(this.parent, 'vertical-layout', 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
@@ -968,16 +974,16 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private create(): void {
// Store layout as class property
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
// Allow to drop into container to open
this.enableDropTarget(this.parent.getHTMLElement());
this.enableDropTarget(this.parent);
// Silo One
this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one' });
// Sash One
this.sashOne = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashOne = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashOne.onDidStart(() => this.onSashOneDragStart()));
this.toUnbind.push(this.sashOne.onDidChange((e: ISashEvent) => this.onSashOneDrag(e)));
this.toUnbind.push(this.sashOne.onDidEnd(() => this.onSashOneDragEnd()));
@@ -988,7 +994,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two' });
// Sash Two
this.sashTwo = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashTwo = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashTwo.onDidStart(() => this.onSashTwoDragStart()));
this.toUnbind.push(this.sashTwo.onDidChange((e: ISashEvent) => this.onSashTwoDrag(e)));
this.toUnbind.push(this.sashTwo.onDidEnd(() => this.onSashTwoDragEnd()));
@@ -1033,9 +1039,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);
// Progress Bar
const progressBar = new ProgressBar($(container));
const progressBar = new ProgressBar(container.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
progressBar.getContainer().hide();
progressBar.hide();
container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container
// Sash for first position to support centered editor layout
@@ -1059,11 +1065,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.centeredEditorLeftMarginRatio = 0.5;
// Restore centered layout position and size
const centeredLayoutDataString = this.storageServise.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
const centeredLayoutDataString = this.storageService.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
if (centeredLayoutDataString) {
const centeredLayout = <CenteredEditorLayoutData>JSON.parse(centeredLayoutDataString);
this.centeredEditorLeftMarginRatio = centeredLayout.leftMarginRatio;
this.centeredEditorPreferedSize = centeredLayout.size;
this.centeredEditorPreferredSize = centeredLayout.size;
}
}
}
@@ -1284,7 +1290,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
if (!overlay) {
const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer());
containers.forEach((container, index) => {
if (container && DOM.isAncestor(target, container.getHTMLElement())) {
if (container && DOM.isAncestor(target, container)) {
const activeContrastBorderColor = $this.getColor(activeContrastBorder);
overlay = $('div').style({
top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0,
@@ -1596,8 +1602,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// TODO@Ben remove me after a while
/* __GDPR__
"editorGroupMoved" : {
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
@@ -1627,11 +1633,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
let borderColor = null;
if (isDragging) {
this.parent.addClass('dragging');
DOM.addClass(this.parent, 'dragging');
silo.addClass('dragging');
borderColor = this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder);
} else {
this.parent.removeClass('dragging');
DOM.removeClass(this.parent, 'dragging');
silo.removeClass('dragging');
}
@@ -1930,11 +1936,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
private get centeredEditorAvailableSize(): number {
return this.silosSize[Position.ONE] - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
return this.dimension.width - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
}
private get centeredEditorSize(): number {
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferedSize);
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferredSize);
}
private get centeredEditorPosition(): number {
@@ -1956,7 +1962,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
if (size > 3 * this.minSize && size < this.centeredEditorAvailableSize) {
this.centeredEditorPreferedSize = size;
this.centeredEditorPreferredSize = size;
position -= EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN;
position = Math.min(position, this.centeredEditorAvailableSize - this.centeredEditorSize);
position = Math.max(0, position);
@@ -1971,7 +1977,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
leftMarginRatio: this.centeredEditorLeftMarginRatio,
size: this.centeredEditorSize
};
this.storageServise.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
this.storageService.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
}
public getVerticalSashTop(sash: Sash): number {
@@ -2013,17 +2019,17 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
return this.dragging;
}
public layout(dimension: Dimension): void;
public layout(dimension: DOM.Dimension): void;
public layout(position: Position): void;
public layout(arg: any): void {
if (arg instanceof Dimension) {
this.layoutControl(<Dimension>arg);
if (arg instanceof DOM.Dimension) {
this.layoutControl(<DOM.Dimension>arg);
} else {
this.layoutEditor(<Position>arg);
}
}
private layoutControl(dimension: Dimension): void {
private layoutControl(dimension: DOM.Dimension): void {
let oldDimension = this.dimension;
this.dimension = dimension;
@@ -2141,14 +2147,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
});
// Layout centered Editor (only in vertical layout when one group is opened)
const id = this.visibleEditors[Position.ONE] ? this.visibleEditors[Position.ONE].getId() : undefined;
const doCentering = this.layoutVertically && this.stacks.groups.length === 1 && this.partService.isEditorLayoutCentered() && id !== PREFERENCES_EDITOR_ID && id !== TEXT_DIFF_EDITOR_ID;
const doCentering = this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 &&
this.visibleEditors[Position.ONE] && this.visibleEditors[Position.ONE].supportsCenteredLayout();
if (doCentering && !this.centeredEditorActive) {
this.centeredEditorSashLeft.show();
this.centeredEditorSashRight.show();
// no size set yet. Calculate a default value
if (!this.centeredEditorPreferedSize) {
if (!this.centeredEditorPreferredSize) {
this.resetCenteredEditor(false);
}
} else if (!doCentering && this.centeredEditorActive) {
@@ -2169,16 +2175,18 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
});
// Layout title controls
POSITIONS.forEach(position => {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
});
POSITIONS.forEach(position => this.layoutTitleControl(position));
// Update minimized state
this.updateMinimizedState();
}
private layoutTitleControl(position: Position): void {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new DOM.Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
}
private layoutEditor(position: Position): void {
const editorSize = this.silosSize[position];
const editor = this.visibleEditors[position];
@@ -2201,20 +2209,20 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
const editorContainer = editor.getContainer();
editorContainer.style('margin-left', this.centeredEditorActive ? `${editorPosition}px` : null);
editorContainer.style('width', this.centeredEditorActive ? `${editorWidth}px` : null);
editorContainer.style('border-color', this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null);
editor.layout(new Dimension(editorWidth, editorHeight));
editorContainer.style.marginLeft = this.centeredEditorActive ? `${editorPosition}px` : null;
editorContainer.style.width = this.centeredEditorActive ? `${editorWidth}px` : null;
editorContainer.style.borderColor = this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null;
editor.layout(new DOM.Dimension(editorWidth, editorHeight));
}
}
private resetCenteredEditor(layout: boolean = true) {
this.centeredEditorLeftMarginRatio = 0.5;
this.centeredEditorPreferedSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
this.centeredEditorPreferredSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
if (layout) {
this.layoutContainers();
}
this.storageServise.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
this.storageService.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
}
public getInstantiationService(position: Position): IInstantiationService {
@@ -2269,13 +2277,13 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
switch (state) {
case ProgressState.INFINITE:
progressbar.infinite().getContainer().show();
progressbar.infinite().show();
break;
case ProgressState.DONE:
progressbar.done().getContainer().hide();
progressbar.done().hide();
break;
case ProgressState.STOP:
progressbar.stop().getContainer().hide();
progressbar.stop().hide();
break;
}
}

View File

@@ -9,12 +9,11 @@ import 'vs/css!./media/editorpart';
import 'vs/workbench/browser/parts/editor/editor.contribution';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import nls = require('vs/nls');
import strings = require('vs/base/common/strings');
import arrays = require('vs/base/common/arrays');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import * as objects from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { toErrorMessage } from 'vs/base/common/errorMessage';
@@ -35,20 +34,19 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { EditorStacksModel, EditorGroup, EditorIdentifier, EditorCloseEvent } from 'vs/workbench/common/editor/editorStacksModel';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter, once } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme';
import { createCSSRule } from 'vs/base/browser/dom';
import { createCSSRule, Dimension, addClass, removeClass } from 'vs/base/browser/dom';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join } from 'vs/base/common/paths';
import { IEditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { ThrottledEmitter } from 'vs/base/common/async';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification';
import { isErrorWithActions } from 'vs/base/common/errors';
import { dispose } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
@@ -107,16 +105,17 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private forceHideTabs: boolean;
private doNotFireTabOptionsChanged: boolean;
private revealIfOpen: boolean;
private _onEditorsChanged: ThrottledEmitter<void>;
private _onEditorOpening: Emitter<IEditorOpeningEvent>;
private _onEditorGroupMoved: Emitter<void>;
private _onEditorOpenFail: Emitter<EditorInput>;
private _onGroupOrientationChanged: Emitter<void>;
private _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private ignoreOpenEditorErrors: boolean;
private textCompareEditorVisible: IContextKey<boolean>;
private readonly _onEditorsChanged: ThrottledEmitter<void>;
private readonly _onEditorOpening: Emitter<IEditorOpeningEvent>;
private readonly _onEditorGroupMoved: Emitter<void>;
private readonly _onEditorOpenFail: Emitter<EditorInput>;
private readonly _onGroupOrientationChanged: Emitter<void>;
private readonly _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private readonly _onLayout: Emitter<Dimension>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
@@ -124,9 +123,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
private onLayoutEmitter = new Emitter<Dimension>();
public onLayout = this.onLayoutEmitter.event;
constructor(
id: string,
restoreFromStorage: boolean,
@@ -148,6 +144,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this._onLayout = new Emitter<Dimension>();
this.visibleEditors = [];
@@ -298,6 +295,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.editorGroupsControl.resizeGroup(position, groupSizeChange);
}
public get onLayout(): Event<Dimension> {
return this._onLayout.event;
}
public get onEditorsChanged(): Event<void> {
return this._onEditorsChanged.event;
}
@@ -479,11 +480,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
const editorContainer = document.createElement('div');
editorContainer.id = descriptor.getId();
addClass(editorContainer, 'editor-container');
editorContainer.setAttribute('role', 'tabpanel');
editor.create(editorContainer);
}
return editor;
@@ -555,18 +557,21 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Stop loading promise if any
monitor.cancel();
// Report error only if this was not us restoring previous error state
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error)) {
// Report error only if this was not us restoring previous error state or
// we are told to ignore errors that occur from opening an editor
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error) && !this.ignoreOpenEditorErrors) {
const actions: INotificationActions = { primary: [] };
if (isErrorWithActions(error)) {
actions.primary = error.actions;
if (errors.isErrorWithActions(error)) {
actions.primary = (error as errors.IErrorWithActions).actions;
}
this.notificationService.notify({
const handle = this.notificationService.notify({
severity: Severity.Error,
message: nls.localize('editorOpenError', "Unable to open '{0}': {1}.", input.getName(), toErrorMessage(error)),
actions
});
once(handle.onDidClose)(() => dispose(actions.primary));
}
this.editorGroupsControl.updateProgress(position, ProgressState.DONE);
@@ -576,7 +581,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Recover by closing the active editor (if the input is still the active one)
if (group.activeEditor === input) {
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */);
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */, true /* from error */);
}
}
@@ -610,7 +615,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
}
private doCloseActiveEditor(group: EditorGroup, focusNext = true): void {
private doCloseActiveEditor(group: EditorGroup, focusNext = true, fromError?: boolean): void {
const position = this.stacks.positionOfGroup(group);
// Update stacks model
@@ -623,7 +628,22 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Otherwise open next active
else {
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(null, errors.onUnexpectedError);
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
// repeated errors in this case to the user. As such, if we open the next editor and we are
// in a scope of a previous editor failing, we silence the input errors until the editor is
// opened.
if (fromError) {
this.ignoreOpenEditorErrors = true;
}
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(() => {
this.ignoreOpenEditorErrors = false;
}, error => {
errors.onUnexpectedError(error);
this.ignoreOpenEditorErrors = false;
});
}
}
@@ -710,7 +730,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
// Then check for array of positions to close
if (Array.isArray(positionsOrEditors) || isUndefinedOrNull(positionsOrEditors)) {
if (Array.isArray(positionsOrEditors) || types.isUndefinedOrNull(positionsOrEditors)) {
return this.doCloseAllEditorsAtPositions(positionsOrEditors as Position[]);
}
@@ -833,16 +853,18 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Close: By Filter or all
else {
editorsToClose = group.getEditors(true /* in MRU order */);
filter = filterOrEditors || Object.create(null);
const hasDirection = !types.isUndefinedOrNull(filter.direction);
editorsToClose = group.getEditors(!hasDirection /* in MRU order only if direction is not specified */);
// Filter: saved only
if (filter.savedOnly) {
editorsToClose = editorsToClose.filter(e => !e.isDirty());
}
// Filter: direction (left / right)
else if (!types.isUndefinedOrNull(filter.direction)) {
else if (hasDirection) {
editorsToClose = (filter.direction === Direction.LEFT) ? editorsToClose.slice(0, group.indexOf(filter.except)) : editorsToClose.slice(group.indexOf(filter.except) + 1);
}
@@ -1141,12 +1163,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.editorGroupsControl.getGroupOrientation();
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
// Content Container
const contentArea = $(parent)
.div()
.addClass('content');
const contentArea = document.createElement('div');
addClass(contentArea, 'content');
parent.appendChild(contentArea);
// get settings
this.memento = this.getMemento(this.storageService, MementoScope.WORKSPACE);
@@ -1164,19 +1186,19 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Part container
const container = this.getContainer();
container.style('background-color', this.getColor(editorBackground));
container.style.backgroundColor = this.getColor(editorBackground);
// Content area
const content = this.getContentArea();
const groupCount = this.stacks.groups.length;
if (groupCount > 1) {
content.addClass('multiple-groups');
addClass(content, 'multiple-groups');
} else {
content.removeClass('multiple-groups');
removeClass(content, 'multiple-groups');
}
content.style('background-color', groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null);
content.style.backgroundColor = groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null;
}
private onGroupFocusChanged(): void {
@@ -1474,7 +1496,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.dimension = sizes[1];
this.editorGroupsControl.layout(this.dimension);
this.onLayoutEmitter.fire(dimension);
this._onLayout.fire(dimension);
return sizes;
}
@@ -1512,6 +1534,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpening.dispose();
this._onEditorGroupMoved.dispose();
this._onEditorOpenFail.dispose();
this._onGroupOrientationChanged.dispose();
this._onTabOptionsChanged.dispose();
this._onLayout.dispose();
// Reset Tokens
this.editorOpenToken = [];

View File

@@ -6,9 +6,9 @@
import 'vs/css!./media/editorpicker';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';

View File

@@ -6,14 +6,14 @@
'use strict';
import 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener } from 'vs/base/browser/dom';
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import types = require('vs/base/common/types');
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener, getDomNodePagePosition } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import * as paths from 'vs/base/common/paths';
import * as types from 'vs/base/common/types';
import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
@@ -50,35 +50,58 @@ import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/con
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachStylerCallback, attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Button } from 'vs/base/browser/ui/button/button';
import { Schemas } from 'vs/base/common/network';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Themable } from 'vs/workbench/common/theme';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
// TODO@Sandeep layer breaker
// tslint:disable-next-line:import-patterns
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
public getEncoding(): string {
return this.master.getEncoding(); // always report from modified (right hand) side
}
public setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(s => s.setEncoding(encoding, mode));
}
}
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/parts/query/services/queryEditorService';
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
if (input instanceof SideBySideEditorInput) {
input = input.master;
}
// Untitled Editor
if (input instanceof UntitledEditorInput) {
return input;
}
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterEncodingSupport = toEditorWithEncodingSupport(input.master);
const detailsEncodingSupport = toEditorWithEncodingSupport(input.details);
if (masterEncodingSupport && detailsEncodingSupport) {
return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport);
}
return masterEncodingSupport;
}
// File or Resource Editor
let encodingSupport = input as IFileEditorInput;
if (types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
return encodingSupport;
}
// Unsupported for any other editor
return null;
}
@@ -246,16 +269,16 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
function _setDisplay(el: HTMLElement, desiredValue: string): void {
function setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
el.style.display = desiredValue;
}
}
function show(el: HTMLElement): void {
_setDisplay(el, '');
setDisplay(el, '');
}
function hide(el: HTMLElement): void {
_setDisplay(el, 'none');
setDisplay(el, 'none');
}
export class EditorStatus implements IStatusbarItem {
@@ -274,7 +297,7 @@ export class EditorStatus implements IStatusbarItem {
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private lastScreenReaderExplanation: ScreenReaderDetectedExplanation;
private screenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -289,7 +312,6 @@ export class EditorStatus implements IStatusbarItem {
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
this.lastScreenReaderExplanation = null;
}
public render(container: HTMLElement): IDisposable {
@@ -313,7 +335,7 @@ export class EditorStatus implements IStatusbarItem {
hide(this.selectionElement);
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
this.indentationElement.title = nls.localize('indentation', "Indentation");
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
this.indentationElement.onclick = () => this.onIndentationClick();
hide(this.indentationElement);
@@ -487,7 +509,18 @@ export class EditorStatus implements IStatusbarItem {
}
private onScreenReaderModeClick(): void {
this.lastScreenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
const showExplanation = !this.screenReaderExplanation || !this.screenReaderExplanation.visible;
if (!this.screenReaderExplanation) {
this.screenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation);
this.toDispose.push(this.screenReaderExplanation);
}
if (showExplanation) {
this.screenReaderExplanation.show(this.screenRedearModeElement);
} else {
this.screenReaderExplanation.hide();
}
}
private onSelectionClick(): void {
@@ -652,9 +685,8 @@ export class EditorStatus implements IStatusbarItem {
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
}
if (screenReaderMode === false && this.lastScreenReaderExplanation) {
this.lastScreenReaderExplanation.hide();
this.lastScreenReaderExplanation = null;
if (screenReaderMode === false && this.screenReaderExplanation && this.screenReaderExplanation.visible) {
this.screenReaderExplanation.hide();
}
this.updateState({ screenReaderMode: screenReaderMode });
@@ -1229,113 +1261,148 @@ export class ChangeEncodingAction extends Action {
}
}
class ScreenReaderDetectedExplanation {
private _isDisposed: boolean;
private _toDispose: IDisposable[];
class ScreenReaderDetectedExplanation extends Themable {
private container: HTMLElement;
private hrElement: HTMLHRElement;
private _visible: boolean;
constructor(
anchorElement: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IThemeService themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this._isDisposed = false;
this._toDispose = [];
super(themeService);
}
public get visible(): boolean {
return this._visible;
}
protected updateStyles(): void {
if (this.container) {
const background = this.getColor(editorWidgetBackground);
this.container.style.backgroundColor = background ? background.toString() : null;
const widgetShadowColor = this.getColor(widgetShadow);
this.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
const contrastBorderColor = this.getColor(contrastBorder);
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
const foregroundColor = this.getColor(foreground);
this.hrElement.style.backgroundColor = foregroundColor ? foregroundColor.toString() : null;
}
}
public show(anchorElement: HTMLElement): void {
this._visible = true;
this.contextViewService.showContextView({
getAnchor: () => anchorElement,
getAnchor: () => {
const res = getDomNodePagePosition(anchorElement);
return {
x: res.left,
y: res.top - 9, /* above the status bar */
width: res.width,
height: res.height
} as IAnchor;
},
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
},
onDOMEvent: (e, activeElement) => { },
onHide: () => {
this.dispose();
this._visible = false;
}
});
}
public dispose(): void {
this._isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
public hide(): void {
if (this._isDisposed) {
return;
}
this.contextViewService.hideContextView();
}
protected renderContents(container: HTMLElement): IDisposable {
const domNode = $('div.screen-reader-detected-explanation', {
protected renderContents(parent: HTMLElement): IDisposable {
const toDispose: IDisposable[] = [];
this.container = $('div.screen-reader-detected-explanation', {
'aria-hidden': 'true'
});
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
domNode.appendChild(title);
this.container.appendChild(title);
const closeBtn = $('div.cancel');
this._toDispose.push(addDisposableListener(closeBtn, 'click', () => {
toDispose.push(addDisposableListener(closeBtn, 'click', () => {
this.contextViewService.hideContextView();
}));
domNode.appendChild(closeBtn);
toDispose.push(addDisposableListener(closeBtn, 'mouseover', () => {
const theme = this.themeService.getTheme();
let darkenFactor: number;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
closeBtn.style.backgroundColor = this.getColor(editorWidgetBackground, (color, theme) => darken(color, darkenFactor)(theme));
}
}));
toDispose.push(addDisposableListener(closeBtn, 'mouseout', () => {
closeBtn.style.backgroundColor = null;
}));
this.container.appendChild(closeBtn);
// {{SQL CARBON EDIT}}
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate SQL Operations Studio?"));
domNode.appendChild(question);
this.container.appendChild(question);
const buttonContainer = $('div.buttons');
domNode.appendChild(buttonContainer);
this.container.appendChild(buttonContainer);
const yesBtn = new Button(buttonContainer);
yesBtn.label = nls.localize('screenReaderDetectedExplanation.answerYes', "Yes");
this._toDispose.push(attachButtonStyler(yesBtn, this.themeService));
this._toDispose.push(yesBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(yesBtn, this.themeService));
toDispose.push(yesBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const noBtn = new Button(buttonContainer);
noBtn.label = nls.localize('screenReaderDetectedExplanation.answerNo', "No");
this._toDispose.push(attachButtonStyler(noBtn, this.themeService));
this._toDispose.push(noBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(noBtn, this.themeService));
toDispose.push(noBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const clear = $('div');
clear.style.clear = 'both';
domNode.appendChild(clear);
this.container.appendChild(clear);
const br = $('br');
domNode.appendChild(br);
this.container.appendChild(br);
const hr = $('hr');
domNode.appendChild(hr);
this.hrElement = $('hr');
this.container.appendChild(this.hrElement);
// {{SQL CARBON EDIT}}
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "SQL Operations Studio is now optimized for usage with a screen reader."));
domNode.appendChild(explanation1);
this.container.appendChild(explanation1);
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Some editor features will have different behaviour: e.g. word wrapping, folding, etc."));
domNode.appendChild(explanation2);
this.container.appendChild(explanation2);
container.appendChild(domNode);
parent.appendChild(this.container);
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
domNode.style.backgroundColor = colors.editorWidgetBackground;
if (colors.widgetShadow) {
domNode.style.boxShadow = `0 5px 8px ${colors.widgetShadow}`;
}
}));
this.updateStyles();
return {
dispose: () => { this.dispose(); }
dispose: () => dispose(toDispose)
};
}
}

View File

@@ -3,6 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
font-style: italic;
}

View File

@@ -22,7 +22,6 @@
cursor: default !important;
}
.monaco-shell .screen-reader-detected-explanation {
width: 420px;
top: 30px;
@@ -53,8 +52,9 @@
font-size: 1.2em;
}
.monaco-shell .screen-reader-detected-explanation p.question {
font-size: 1.4em;
.monaco-shell .screen-reader-detected-explanation hr {
border: 0;
height: 2px;
}
.monaco-shell .screen-reader-detected-explanation .buttons {
@@ -72,21 +72,8 @@
.monaco-shell.vs .screen-reader-detected-explanation .cancel {
background: url('close-big.svg') center center no-repeat;
}
.monaco-shell.vs .screen-reader-detected-explanation .cancel:hover {
background-color: #eaeaea;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel,
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
background: url('close-big-dark.svg') center center no-repeat;
}
.monaco-shell.vs-dark .screen-reader-detected-explanation .cancel:hover {
background-color: rgba(30,30,30,0.8);
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel {
opacity: 0.6;
}
.monaco-shell.hc-black .screen-reader-detected-explanation .cancel:hover {
opacity: 1;
}
}

View File

@@ -54,8 +54,9 @@
cursor: zoom-out;
}
.monaco-resource-viewer .open-external,
.monaco-resource-viewer .open-external:hover {
.monaco-resource-viewer .embedded-link,
.monaco-resource-viewer .embedded-link:hover {
cursor: pointer;
text-decoration: underline;
margin-left: 5px;
}

View File

@@ -62,15 +62,24 @@
max-width: fit-content;
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left::after,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off::after {
content: "";
display: flex;
flex: 0;
width: 5px; /* Reserve space to hide tab fade when close button is left or off (fixes https://github.com/Microsoft/vscode/issues/45728) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-left {
min-width: 80px; /* make more room for close button when it shows to the left */
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged {
will-change: transform; /* forces tab to be drawn on a separate layer (fixes https://github.com/Microsoft/vscode/issues/18733) */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over * {
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.dragged-over div {
pointer-events: none; /* prevents cursor flickering (fixes https://github.com/Microsoft/vscode/issues/38753) */
}
@@ -190,6 +199,10 @@
padding-right: 10px; /* give a little bit more room if close button is off */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.sizing-shrink.close-button-off {
padding-right: 5px; /* we need less room when sizing is shrink */
}
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title .tabs-container > .tab.close-button-off.dirty {
background-repeat: no-repeat;
background-position-y: center;

View File

@@ -6,9 +6,9 @@
'use strict';
import 'vs/css!./media/notabstitle';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { toResource } from 'vs/workbench/common/editor';
import DOM = require('vs/base/browser/dom');
import * as DOM from 'vs/base/browser/dom';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { Verbosity } from 'vs/platform/editor/common/editor';
@@ -81,14 +81,14 @@ export class NoTabsTitleControl extends TitleControl {
// Close editor on middle mouse click
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
this.closeEditorAction.run({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
this.closeOneEditorAction.run({ groupId: group.id, editorIndex: group.indexOf(group.activeEditor) }).done(null, errors.onUnexpectedError);
}
// Focus editor group unless:
// - click on toolbar: should trigger actions within
// - mouse click: do not focus group if there are more than one as it otherwise makes group DND funky
// - touch: always focus
else if ((this.stacks.groups.length === 1 || !(e instanceof MouseEvent)) && !DOM.isAncestor(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer().getHTMLElement())) {
else if ((this.stacks.groups.length === 1 || !(e instanceof MouseEvent)) && !DOM.isAncestor(((e as GestureEvent).initialTarget || e.target || e.srcElement) as HTMLElement, this.editorActionsToolbar.getContainer())) {
this.editorGroupService.focusGroup(group);
}
}

View File

@@ -5,7 +5,7 @@
import { IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IRange } from 'vs/editor/common/core/range';
import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -25,10 +25,10 @@ export class RangeHighlightDecorations implements IDisposable {
private editor: ICodeEditor = null;
private editorDisposables: IDisposable[] = [];
private _onHighlightRemoved: Emitter<void> = new Emitter<void>();
private readonly _onHighlightRemoved: Emitter<void> = new Emitter<void>();
public readonly onHighlghtRemoved: Event<void> = this._onHighlightRemoved.event;
constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
constructor(@IWorkbenchEditorService private editorService: IWorkbenchEditorService) {
}
public removeHighlightRange() {

View File

@@ -6,12 +6,12 @@
'use strict';
import 'vs/css!./media/resourceviewer';
import nls = require('vs/nls');
import mimes = require('vs/base/common/mime');
import * as nls from 'vs/nls';
import * as mimes from 'vs/base/common/mime';
import URI from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
import { Builder, $, Dimension } from 'vs/base/browser/builder';
import DOM = require('vs/base/browser/dom');
import * as paths from 'vs/base/common/paths';
import { Builder, $ } from 'vs/base/browser/builder';
import * as DOM from 'vs/base/browser/dom';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { LRUCache } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
@@ -19,7 +19,6 @@ import { clamp } from 'vs/base/common/numbers';
import { Themable } from 'vs/workbench/common/theme';
import { IStatusbarItem, StatusbarItemDescriptor, IStatusbarRegistry, Extensions, StatusbarAlignment } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { } from 'vs/platform/workspace/common/workspace';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -119,7 +118,7 @@ class BinarySize {
}
export interface ResourceViewerContext {
layout(dimension: Dimension): void;
layout(dimension: DOM.Dimension): void;
}
/**
@@ -127,26 +126,42 @@ export interface ResourceViewerContext {
* progress of the binary resource.
*/
export class ResourceViewer {
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
public static show(
descriptor: IResourceDescriptor,
container: Builder,
container: HTMLElement,
scrollbar: DomScrollableElement,
openExternal: (uri: URI) => void,
openInternalClb: (uri: URI) => void,
openExternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
): ResourceViewerContext {
): ResourceViewerContext | null {
// Ensure CSS class
$(container).setClass('monaco-resource-viewer');
// Images
if (ResourceViewer.isImageResource(descriptor)) {
return ImageView.create(container, descriptor, scrollbar, openExternal, metadataClb);
return ImageView.create(container, descriptor, scrollbar, openExternalClb, metadataClb);
}
// Large Files
if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
FileTooLargeFileView.create(container, descriptor, scrollbar, metadataClb);
}
// Seemingly Binary Files
else {
FileSeemsBinaryFileView.create(container, descriptor, scrollbar, openInternalClb, metadataClb);
}
GenericBinaryFileView.create(container, metadataClb, descriptor, scrollbar);
return null;
}
private static isImageResource(descriptor: IResourceDescriptor) {
const mime = ResourceViewer.getMime(descriptor);
return mime.indexOf('image/') >= 0;
}
@@ -158,6 +173,7 @@ export class ResourceViewer {
mime = mapExtToMediaMimes[ext.toLowerCase()];
}
}
return mime || mimes.MIME_BINARY;
}
}
@@ -167,17 +183,18 @@ class ImageView {
private static readonly BASE64_MARKER = 'base64,';
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openExternal: (uri: URI) => void,
openExternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
): ResourceViewerContext | null {
if (ImageView.shouldShowImageInline(descriptor)) {
return InlineImageView.create(container, descriptor, scrollbar, metadataClb);
}
LargeImageView.create(container, descriptor, openExternal);
LargeImageView.create(container, descriptor, openExternalClb);
return null;
}
@@ -203,43 +220,81 @@ class ImageView {
class LargeImageView {
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
openExternal: (uri: URI) => void
openExternalClb: (uri: URI) => void
) {
const size = BinarySize.formatSize(descriptor.size);
const imageContainer = $(container)
.empty()
.p({
text: nls.localize('largeImageError', "The file size of the image is too large (>1MB) to display in the editor. ")
text: nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size)
});
if (descriptor.resource.scheme !== Schemas.data) {
imageContainer.append($('a', {
role: 'button',
class: 'open-external',
class: 'embedded-link',
text: nls.localize('resourceOpenExternalButton', "Open image using external program?")
}).on(DOM.EventType.CLICK, (e) => {
openExternal(descriptor.resource);
openExternalClb(descriptor.resource);
}));
}
}
}
class GenericBinaryFileView {
class FileTooLargeFileView {
public static create(
container: Builder,
metadataClb: (meta: string) => void,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
) {
const size = BinarySize.formatSize(descriptor.size);
$(container)
.empty()
.span({
text: nls.localize('nativeBinaryError', "The file will not be displayed in the editor because it is either binary, very large or uses an unsupported text encoding.")
text: nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size)
});
if (metadataClb) {
metadataClb(size);
}
scrollbar.scanDomNode();
}
}
class FileSeemsBinaryFileView {
public static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void
) {
const binaryContainer = $(container)
.empty()
.p({
text: nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.")
});
if (descriptor.resource.scheme !== Schemas.data) {
binaryContainer.append($('a', {
role: 'button',
class: 'embedded-link',
text: nls.localize('openAsText', "Do you want to open it anyway?")
}).on(DOM.EventType.CLICK, (e) => {
openInternalClb(descriptor.resource);
}));
}
if (metadataClb) {
metadataClb(BinarySize.formatSize(descriptor.size));
}
scrollbar.scanDomNode();
}
}
@@ -266,7 +321,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
private onEditorsChanged(): void {
this.hide();
this.onSelectScale = undefined;
this.onSelectScale = void 0;
}
public show(scale: Scale, onSelectScale: (scale: number) => void) {
@@ -295,6 +350,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
.getHTMLElement();
this.statusBarItem.style.display = 'none';
}
return this;
}
@@ -306,10 +362,11 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem {
private get zoomActions(): Action[] {
const scales: Scale[] = [10, 5, 2, 1, 0.5, 0.2, 'fit'];
return scales.map(scale =>
new Action('zoom.' + scale, ZoomStatusbarItem.zoomLabel(scale), undefined, undefined, () => {
new Action(`zoom.${scale}`, ZoomStatusbarItem.zoomLabel(scale), void 0, void 0, () => {
if (this.onSelectScale) {
this.onSelectScale(scale);
}
return null;
}));
}
@@ -376,13 +433,13 @@ class InlineImageView {
private static readonly imageStateCache = new LRUCache<string, ImageState>(100);
public static create(
container: Builder,
container: HTMLElement,
descriptor: IResourceDescriptor,
scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void
) {
const context = {
layout(dimension: Dimension) { }
layout(dimension: DOM.Dimension) { }
};
const cacheKey = descriptor.resource.toString();
@@ -585,6 +642,4 @@ class InlineImageView {
return cached.src;
}
}
}

View File

@@ -5,14 +5,11 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorControl, Position, IEditor } from 'vs/platform/editor/common/editor';
import { VSash } from 'vs/base/browser/ui/sash/sash';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -23,7 +20,7 @@ export class SideBySideEditor extends BaseEditor {
public static readonly ID: string = 'workbench.editor.sidebysideEditor';
private dimension: Dimension;
private dimension: DOM.Dimension;
protected masterEditor: BaseEditor;
private masterEditorContainer: HTMLElement;
@@ -41,10 +38,9 @@ export class SideBySideEditor extends BaseEditor {
super(SideBySideEditor.ID, telemetryService, themeService);
}
protected createEditor(parent: Builder): void {
const parentElement = parent.getHTMLElement();
DOM.addClass(parentElement, 'side-by-side-editor');
this.createSash(parentElement);
protected createEditor(parent: HTMLElement): void {
DOM.addClass(parent, 'side-by-side-editor');
this.createSash(parent);
}
public setInput(newInput: SideBySideEditorInput, options?: EditorOptions): TPromise<void> {
@@ -90,7 +86,7 @@ export class SideBySideEditor extends BaseEditor {
}
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
this.dimension = dimension;
this.sash.setDimenesion(this.dimension);
}
@@ -110,6 +106,10 @@ export class SideBySideEditor extends BaseEditor {
return this.detailsEditor;
}
public supportsCenteredLayout(): boolean {
return false;
}
private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options?: EditorOptions): void {
if (!newInput.matches(oldInput)) {
if (oldInput) {
@@ -137,7 +137,7 @@ export class SideBySideEditor extends BaseEditor {
const descriptor = Registry.as<IEditorRegistry>(EditorExtensions.Editors).getEditor(editorInput);
const editor = descriptor.instantiate(this.instantiationService);
editor.create(new Builder(container));
editor.create(container);
editor.setVisible(this.isVisible(), this.position);
return editor;
@@ -151,7 +151,7 @@ export class SideBySideEditor extends BaseEditor {
}
private createEditorContainers(): void {
const parentElement = this.getContainer().getHTMLElement();
const parentElement = this.getContainer();
this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
this.detailsEditorContainer.style.position = 'absolute';
this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container'));
@@ -188,12 +188,12 @@ export class SideBySideEditor extends BaseEditor {
this.masterEditorContainer.style.height = `${this.dimension.height}px`;
this.masterEditorContainer.style.left = `${splitPoint}px`;
this.detailsEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height));
this.masterEditor.layout(new Dimension(masterEditorWidth, this.dimension.height));
this.detailsEditor.layout(new DOM.Dimension(detailsEditorWidth, this.dimension.height));
this.masterEditor.layout(new DOM.Dimension(masterEditorWidth, this.dimension.height));
}
private disposeEditors(): void {
const parentContainer = this.getContainer().getHTMLElement();
const parentContainer = this.getContainer();
if (this.detailsEditor) {
this.detailsEditor.dispose();
this.detailsEditor = null;
@@ -216,4 +216,4 @@ export class SideBySideEditor extends BaseEditor {
this.disposeEditors();
super.dispose();
}
}
}

View File

@@ -6,10 +6,10 @@
'use strict';
import 'vs/css!./media/tabstitle';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { ActionRunner, IAction } from 'vs/base/common/actions';
@@ -38,8 +38,6 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_BACKGROUND, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { Dimension } from 'vs/base/browser/builder';
import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { ResourcesDropHandler, fillResourceDataTransfers, LocalSelectionTransfer, DraggedEditorIdentifier } from 'vs/workbench/browser/dnd';
import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -72,7 +70,7 @@ export class TabsTitleControl extends TitleControl {
private scrollbar: ScrollableElement;
private tabDisposeables: IDisposable[];
private blockRevealActiveTab: boolean;
private dimension: Dimension;
private dimension: DOM.Dimension;
private layoutScheduled: IDisposable;
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
@@ -576,7 +574,7 @@ export class TabsTitleControl extends TitleControl {
this.tabDisposeables.push(actionRunner);
const bar = new ActionBar(tabCloseContainer, { ariaLabel: nls.localize('araLabelTabActions', "Tab actions"), actionRunner });
bar.push(this.closeEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeEditorAction) });
bar.push(this.closeOneEditorAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.closeOneEditorAction) });
// Eventing
const disposable = this.hookTabListeners(tabContainer, index);
@@ -586,7 +584,7 @@ export class TabsTitleControl extends TitleControl {
return tabContainer;
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
if (!this.activeTab || !dimension) {
return;
}
@@ -597,14 +595,14 @@ export class TabsTitleControl extends TitleControl {
// that can result in the browser doing a full page layout to validate them. To buffer
// this a little bit we try at least to schedule this work on the next animation frame.
if (!this.layoutScheduled) {
this.layoutScheduled = scheduleAtNextAnimationFrame(() => {
this.layoutScheduled = DOM.scheduleAtNextAnimationFrame(() => {
this.doLayout(this.dimension);
this.layoutScheduled = void 0;
});
}
}
private doLayout(dimension: Dimension): void {
private doLayout(dimension: DOM.Dimension): void {
const visibleContainerWidth = this.tabsContainer.offsetWidth;
const totalContainerWidth = this.tabsContainer.scrollWidth;
@@ -695,7 +693,7 @@ export class TabsTitleControl extends TitleControl {
tab.blur();
if (e.button === 1 /* Middle Button*/ && !this.isTabActionBar((e.target || e.srcElement) as HTMLElement)) {
this.closeEditorAction.run({ groupId: this.context.id, editorIndex: index }).done(null, errors.onUnexpectedError);
this.closeOneEditorAction.run({ groupId: this.context.id, editorIndex: index }).done(null, errors.onUnexpectedError);
}
}));
@@ -789,7 +787,7 @@ export class TabsTitleControl extends TitleControl {
// Fixes https://github.com/Microsoft/vscode/issues/18733
DOM.addClass(tab, 'dragged');
scheduleAtNextAnimationFrame(() => DOM.removeClass(tab, 'dragged'));
DOM.scheduleAtNextAnimationFrame(() => DOM.removeClass(tab, 'dragged'));
}));
// We need to keep track of DRAG_ENTER and DRAG_LEAVE events because a tab is not just a div without children,
@@ -977,7 +975,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (tabHoverBackground) {
collector.addRule(`
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.active .tabs-container > .tab:hover {
background: ${tabHoverBackground} !important;
background-color: ${tabHoverBackground} !important;
}
`);
}
@@ -986,7 +984,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
if (tabUnfocusedHoverBackground) {
collector.addRule(`
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title.inactive .tabs-container > .tab:hover {
background: ${tabUnfocusedHoverBackground} !important;
background-color: ${tabUnfocusedHoverBackground} !important;
}
`);
}

View File

@@ -7,12 +7,11 @@
import 'vs/css!./media/textdiffeditor';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import objects = require('vs/base/common/objects');
import { Builder } from 'vs/base/browser/builder';
import * as nls from 'vs/nls';
import * as objects from 'vs/base/common/objects';
import { Action, IAction } from 'vs/base/common/actions';
import { onUnexpectedError } from 'vs/base/common/errors';
import types = require('vs/base/common/types');
import * as types from 'vs/base/common/types';
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
@@ -32,10 +31,13 @@ import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/wo
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import URI from 'vs/base/common/uri';
import { getCodeOrDiffEditor } from 'vs/editor/browser/services/codeEditorService';
import { once } from 'vs/base/common/event';
/**
* The text editor that leverages the diff text editor for the editing experience.
@@ -45,10 +47,10 @@ export class TextDiffEditor extends BaseTextEditor {
public static readonly ID = TEXT_DIFF_EDITOR_ID;
private diffNavigator: DiffNavigator;
private diffNavigatorDisposables: IDisposable[];
private nextDiffAction: NavigateAction;
private previousDiffAction: NavigateAction;
private toggleIgnoreTrimWhitespaceAction: ToggleIgnoreTrimWhitespaceAction;
private _configurationListener: IDisposable;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -63,11 +65,12 @@ export class TextDiffEditor extends BaseTextEditor {
) {
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
this._configurationListener = this._actualConfigurationService.onDidChangeConfiguration((e) => {
this.diffNavigatorDisposables = [];
this.toUnbind.push(this._actualConfigurationService.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('diffEditor.ignoreTrimWhitespace')) {
this.updateIgnoreTrimWhitespaceAction();
}
});
}));
}
public getTitle(): string {
@@ -78,7 +81,7 @@ export class TextDiffEditor extends BaseTextEditor {
return nls.localize('textDiffEditor', "Text Diff Editor");
}
public createEditorControl(parent: Builder, configuration: IEditorOptions): IDiffEditor {
public createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IDiffEditor {
// Actions
this.nextDiffAction = new NavigateAction(this, true);
@@ -118,7 +121,7 @@ export class TextDiffEditor extends BaseTextEditor {
// Create a special child of instantiator that will delegate all calls to openEditor() to the same diff editor if the input matches with the modified one
const diffEditorInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent.getHTMLElement(), configuration);
return diffEditorInstantiator.createInstance(DiffEditorWidget, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -137,9 +140,10 @@ export class TextDiffEditor extends BaseTextEditor {
}
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
}
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
// Remember view settings if input changes
this.saveTextDiffEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
@@ -155,27 +159,32 @@ export class TextDiffEditor extends BaseTextEditor {
return null;
}
// Editor
// Set Editor Model
const diffEditor = <IDiffEditor>this.getControl();
diffEditor.setModel((<TextDiffEditorModel>resolvedModel).textDiffEditorModel);
// Handle TextOptions
let alwaysRevealFirst = true;
// Apply Options from TextOptions
let optionsGotApplied = false;
if (options && types.isFunction((<TextEditorOptions>options).apply)) {
const hadOptions = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
if (hadOptions) {
alwaysRevealFirst = false; // Do not reveal if we are instructed to open specific line/col
}
optionsGotApplied = (<TextEditorOptions>options).apply(<IDiffEditor>diffEditor, ScrollType.Immediate);
}
// Otherwise restore View State
let hasPreviousViewState = false;
if (!optionsGotApplied) {
hasPreviousViewState = this.restoreTextDiffEditorViewState(input);
}
// Listen on diff updated changes to reveal the first change
this.diffNavigator = new DiffNavigator(diffEditor, {
alwaysRevealFirst
alwaysRevealFirst: !optionsGotApplied && !hasPreviousViewState // only reveal first change if we had no options or viewstate
});
this.diffNavigator.onDidUpdate(() => {
this.diffNavigatorDisposables.push(this.diffNavigator);
this.diffNavigatorDisposables.push(this.diffNavigator.onDidUpdate(() => {
this.nextDiffAction.updateEnablement();
this.previousDiffAction.updateEnablement();
});
}));
this.updateIgnoreTrimWhitespaceAction();
}, error => {
@@ -190,6 +199,26 @@ export class TextDiffEditor extends BaseTextEditor {
});
}
public supportsCenteredLayout(): boolean {
return false;
}
private restoreTextDiffEditorViewState(input: EditorInput): boolean {
if (input instanceof DiffEditorInput) {
const resource = this.toDiffEditorViewStateResource(input);
if (resource) {
const viewState = this.loadTextEditorViewState(resource);
if (viewState) {
this.getControl().restoreViewState(viewState);
return true;
}
}
}
return false;
}
private updateIgnoreTrimWhitespaceAction(): void {
const ignoreTrimWhitespace = this.configurationService.getValue<boolean>(this.getResource(), 'diffEditor.ignoreTrimWhitespace');
if (this.toggleIgnoreTrimWhitespaceAction) {
@@ -278,9 +307,10 @@ export class TextDiffEditor extends BaseTextEditor {
public clearInput(): void {
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
}
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
// Keep editor view state in settings to restore when coming back
this.saveTextDiffEditorViewState(this.input);
// Clear Model
this.getControl().setModel(null);
@@ -305,14 +335,85 @@ export class TextDiffEditor extends BaseTextEditor {
return super.getControl() as IDiffEditor;
}
public dispose(): void {
protected loadTextEditorViewState(resource: URI): IDiffEditorViewState {
return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support
}
// Dispose previous diff navigator
if (this.diffNavigator) {
this.diffNavigator.dispose();
private saveTextDiffEditorViewState(input: EditorInput): void {
if (!(input instanceof DiffEditorInput)) {
return; // only supported for diff editor inputs
}
this._configurationListener.dispose();
const resource = this.toDiffEditorViewStateResource(input);
if (!resource) {
return; // unable to retrieve input resource
}
// Clear view state if input is disposed
if (input.isDisposed()) {
super.clearTextEditorViewState([resource]);
}
// Otherwise save it
else {
super.saveTextEditorViewState(resource);
// Make sure to clean up when the input gets disposed
once(input.onDispose)(() => {
super.clearTextEditorViewState([resource]);
});
}
}
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState {
return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support
}
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
const editor = getCodeOrDiffEditor(this).diffEditor;
if (!editor) {
return null; // not supported for non-diff editors
}
const model = editor.getModel();
if (!model || !model.modified || !model.original) {
return null; // view state always needs a model
}
const modelUri = this.toDiffEditorViewStateResource(model);
if (!modelUri) {
return null; // model URI is needed to make sure we save the view state correctly
}
if (modelUri.toString() !== resource.toString()) {
return null; // prevent saving view state for a model that is not the expected one
}
return editor.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
let original: URI;
let modified: URI;
if (modelOrInput instanceof DiffEditorInput) {
original = modelOrInput.originalInput.getResource();
modified = modelOrInput.modifiedInput.getResource();
} else {
original = modelOrInput.original.uri;
modified = modelOrInput.modified.uri;
}
if (!original || !modified) {
return null;
}
// create a URI that is the Base64 concatenation of original + modified resource
return URI.from({ scheme: 'diff', path: `${btoa(original.toString())}${btoa(modified.toString())}` });
}
public dispose(): void {
this.diffNavigatorDisposables = dispose(this.diffNavigatorDisposables);
super.dispose();
}
@@ -372,4 +473,4 @@ class ToggleIgnoreTrimWhitespaceAction extends Action {
this._configurationService.updateValue(`diffEditor.ignoreTrimWhitespace`, !this._isChecked);
return null;
}
}
}

View File

@@ -5,16 +5,15 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { Dimension, Builder } from 'vs/base/browser/builder';
import objects = require('vs/base/common/objects');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import * as objects from 'vs/base/common/objects';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions, EditorViewStateMemento } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorViewState, IEditor } from 'vs/editor/common/editorCommon';
import { Position } from 'vs/platform/editor/common/editor';
@@ -43,15 +42,16 @@ export interface IEditorConfiguration {
*/
export abstract class BaseTextEditor extends BaseEditor {
private editorControl: IEditor;
private _editorContainer: Builder;
private _editorContainer: HTMLElement;
private hasPendingConfigurationChange: boolean;
private lastAppliedEditorOptions: IEditorOptions;
private editorViewStateMemento: EditorViewStateMemento<IEditorViewState>;
constructor(
id: string,
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService private readonly _configurationService: ITextResourceConfigurationService,
@IThemeService protected themeService: IThemeService,
@ITextFileService private readonly _textFileService: ITextFileService,
@@ -59,6 +59,8 @@ export abstract class BaseTextEditor extends BaseEditor {
) {
super(id, telemetryService, themeService);
this.editorViewStateMemento = new EditorViewStateMemento<IEditorViewState>(this.getMemento(storageService, Scope.WORKSPACE), TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.handleConfigurationChangeEvent(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
}
@@ -123,7 +125,7 @@ export abstract class BaseTextEditor extends BaseEditor {
return overrides;
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Editor for Text
this._editorContainer = parent;
@@ -177,10 +179,10 @@ export abstract class BaseTextEditor extends BaseEditor {
*
* The passed in configuration object should be passed to the editor control when creating it.
*/
protected createEditorControl(parent: Builder, configuration: IEditorOptions): IEditor {
protected createEditorControl(parent: HTMLElement, configuration: IEditorOptions): IEditor {
// Use a getter for the instantiation service since some subclasses might use scoped instantiation services
return this.instantiationService.createInstance(CodeEditor, parent.getHTMLElement(), configuration);
return this.instantiationService.createInstance(CodeEditor, parent, configuration);
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -189,7 +191,7 @@ export abstract class BaseTextEditor extends BaseEditor {
// Update editor options after having set the input. We do this because there can be
// editor input specific options (e.g. an ARIA label depending on the input showing)
this.updateEditorConfiguration();
this._editorContainer.getHTMLElement().setAttribute('aria-label', this.computeAriaLabel());
this._editorContainer.setAttribute('aria-label', this.computeAriaLabel());
});
}
@@ -219,7 +221,7 @@ export abstract class BaseTextEditor extends BaseEditor {
this.editorControl.focus();
}
public layout(dimension: Dimension): void {
public layout(dimension: DOM.Dimension): void {
// Pass on to Editor
this.editorControl.layout(dimension);
@@ -233,69 +235,51 @@ export abstract class BaseTextEditor extends BaseEditor {
* Saves the text editor view state for the given resource.
*/
protected saveTextEditorViewState(resource: URI): void {
const editorViewState = this.retrieveTextEditorViewState(resource);
if (!editorViewState) {
return;
}
this.editorViewStateMemento.saveState(resource, this.position, editorViewState);
}
protected retrieveTextEditorViewState(resource: URI): IEditorViewState {
const editor = getCodeOrDiffEditor(this).codeEditor;
if (!editor) {
return; // not supported for diff editors
return null; // not supported for diff editors
}
const model = editor.getModel();
if (!model) {
return; // view state always needs a model
return null; // view state always needs a model
}
const modelUri = model.uri;
if (!modelUri) {
return; // model URI is needed to make sure we save the view state correctly
return null; // model URI is needed to make sure we save the view state correctly
}
if (modelUri.toString() !== resource.toString()) {
return; // prevent saving view state for a model that is not the expected one
return null; // prevent saving view state for a model that is not the expected one
}
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
let textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
if (!textEditorViewStateMemento) {
textEditorViewStateMemento = Object.create(null);
memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY] = textEditorViewStateMemento;
}
let lastKnownViewState = textEditorViewStateMemento[resource.toString()];
if (!lastKnownViewState) {
lastKnownViewState = Object.create(null);
textEditorViewStateMemento[resource.toString()] = lastKnownViewState;
}
if (typeof this.position === 'number') {
lastKnownViewState[this.position] = editor.saveViewState();
}
return editor.saveViewState();
}
/**
* Clears the text editor view state for the given resources.
*/
protected clearTextEditorViewState(resources: URI[]): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
if (textEditorViewStateMemento) {
resources.forEach(resource => delete textEditorViewStateMemento[resource.toString()]);
}
resources.forEach(resource => {
this.editorViewStateMemento.clearState(resource);
});
}
/**
* Loads the text editor view state for the given resource and returns it.
*/
protected loadTextEditorViewState(resource: URI): IEditorViewState {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const textEditorViewStateMemento: { [key: string]: { [position: number]: IEditorViewState } } = memento[TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY];
if (textEditorViewStateMemento) {
const viewState = textEditorViewStateMemento[resource.toString()];
if (viewState) {
return viewState[this.position];
}
}
return null;
return this.editorViewStateMemento.loadState(resource, this.position);
}
private updateEditorConfiguration(configuration = this.configurationService.getValue<IEditorConfiguration>(this.getResource())): void {
@@ -337,6 +321,14 @@ export abstract class BaseTextEditor extends BaseEditor {
protected abstract getAriaLabel(): string;
protected saveMemento(): void {
// ensure to first save our view state memento
this.editorViewStateMemento.save();
super.saveMemento();
}
public dispose(): void {
this.lastAppliedEditorOptions = void 0;
this.editorControl.dispose();

View File

@@ -5,8 +5,8 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import types = require('vs/base/common/types');
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { TextEditorOptions, EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
@@ -67,7 +67,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
}
// Remember view settings if input changes
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
// Set input and resolve
return super.setInput(input, options).then(() => {
@@ -97,7 +97,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Otherwise restore View State
if (!optionsGotApplied) {
this.restoreViewState(input);
this.restoreTextResourceEditorViewState(input);
}
return void 0;
@@ -105,7 +105,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
});
}
protected restoreViewState(input: EditorInput) {
private restoreTextResourceEditorViewState(input: EditorInput) {
if (input instanceof UntitledEditorInput || input instanceof ResourceEditorInput) {
const viewState = this.loadTextEditorViewState(input.getResource());
if (viewState) {
@@ -153,7 +153,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
public clearInput(): void {
// Keep editor view state in settings to restore when coming back
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
// Clear Model
this.getControl().setModel(null);
@@ -165,14 +165,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Save View State (only for untitled)
if (this.input instanceof UntitledEditorInput) {
this.saveTextEditorViewStateForInput(this.input);
this.saveTextResourceEditorViewState(this.input);
}
// Call Super
super.shutdown();
}
protected saveTextEditorViewStateForInput(input: EditorInput): void {
private saveTextResourceEditorViewState(input: EditorInput): void {
if (!(input instanceof UntitledEditorInput) && !(input instanceof ResourceEditorInput)) {
return; // only enabled for untitled and resource inputs
}

View File

@@ -6,15 +6,14 @@
'use strict';
import 'vs/css!./media/titlecontrol';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { prepareActions } from 'vs/workbench/browser/actions';
import { IAction, Action, IRunEvent } from 'vs/base/common/actions';
import errors = require('vs/base/common/errors');
import DOM = require('vs/base/browser/dom');
import * as errors from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { RunOnceScheduler } from 'vs/base/common/async';
import arrays = require('vs/base/common/arrays');
import * as arrays from 'vs/base/common/arrays';
import { IEditorStacksModel, IEditorGroup, IEditorIdentifier, EditorInput, IStacksModelChangeEvent, toResource, IEditorCommandsContext } from 'vs/workbench/common/editor';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
@@ -28,7 +27,7 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { SplitEditorAction, CloseEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { SplitEditorAction, CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { createActionItem, fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IMenuService, MenuId, IMenu, ExecuteCommandAction } from 'vs/platform/actions/common/actions';
@@ -36,8 +35,8 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Themable } from 'vs/workbench/common/theme';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Dimension } from 'vs/base/browser/builder';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, findParentWithClass } from 'vs/base/browser/dom';
export interface IToolbarActions {
primary: IAction[];
@@ -65,7 +64,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
protected dragged: boolean;
protected closeEditorAction: CloseEditorAction;
protected closeOneEditorAction: CloseOneEditorAction;
protected splitEditorAction: SplitEditorAction;
private parent: HTMLElement;
@@ -75,7 +74,8 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
protected editorActionsToolbar: ToolBar;
private mapActionsToEditors: { [editorId: string]: IToolbarActions; };
private scheduler: RunOnceScheduler;
private titleAreaUpdateScheduler: RunOnceScheduler;
private titleAreaToolbarUpdateScheduler: RunOnceScheduler;
private refreshScheduled: boolean;
private resourceContext: ResourceContextKey;
@@ -101,8 +101,11 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.stacks = editorGroupService.getStacksModel();
this.mapActionsToEditors = Object.create(null);
this.scheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
this.toUnbind.push(this.scheduler);
this.titleAreaUpdateScheduler = new RunOnceScheduler(() => this.onSchedule(), 0);
this.toUnbind.push(this.titleAreaUpdateScheduler);
this.titleAreaToolbarUpdateScheduler = new RunOnceScheduler(() => this.updateEditorActionsToolbar(), 0);
this.toUnbind.push(this.titleAreaToolbarUpdateScheduler);
this.resourceContext = instantiationService.createInstance(ResourceContextKey);
@@ -166,22 +169,26 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
public update(instant?: boolean): void {
if (instant) {
this.scheduler.cancel();
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.scheduler.schedule();
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public refresh(instant?: boolean) {
this.refreshScheduled = true;
if (instant) {
this.scheduler.cancel();
this.titleAreaUpdateScheduler.cancel();
this.onSchedule();
} else {
this.scheduler.schedule();
this.titleAreaUpdateScheduler.schedule();
}
this.titleAreaToolbarUpdateScheduler.cancel(); // a title area update will always refresh the toolbar too
}
public create(parent: HTMLElement): void {
@@ -203,11 +210,11 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
}
public allowDragging(element: HTMLElement): boolean {
return !DOM.findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
return !findParentWithClass(element, 'monaco-action-bar', 'one-editor-silo');
}
protected initActions(services: IInstantiationService): void {
this.closeEditorAction = services.createInstance(CloseEditorAction, CloseEditorAction.ID, nls.localize('close', "Close"));
this.closeOneEditorAction = services.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL);
this.splitEditorAction = services.createInstance(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL);
}
@@ -296,7 +303,14 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
const codeEditor = isCodeEditor(widget) && widget || isDiffEditor(widget) && widget.getModifiedEditor();
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => this.update()));
this.disposeOnEditorActions.push(titleBarMenu, titleBarMenu.onDidChange(_ => {
// schedule the update for the title area toolbar only if no other
// update to the title area is scheduled which will always also
// update the toolbar
if (!this.titleAreaUpdateScheduler.isScheduled()) {
this.titleAreaToolbarUpdateScheduler.schedule();
}
}));
fillInActions(titleBarMenu, { arg: this.resourceContext.get(), shouldForwardArgs: true }, { primary, secondary }, this.contextMenuService);
}
@@ -334,11 +348,10 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
const primaryEditorActionIds = primaryEditorActions.map(a => a.id);
if (!tabOptions.showTabs) {
primaryEditorActionIds.push(this.closeEditorAction.id); // always show "Close" when tabs are disabled
primaryEditorActionIds.push(this.closeOneEditorAction.id); // always show "Close" when tabs are disabled
}
const secondaryEditorActionIds = secondaryEditorActions.map(a => a.id);
if (
!arrays.equals(primaryEditorActionIds, this.currentPrimaryEditorActionIds) ||
!arrays.equals(secondaryEditorActionIds, this.currentSecondaryEditorActionIds) ||
@@ -348,7 +361,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
if (!tabOptions.showTabs) {
this.editorActionsToolbar.addPrimaryAction(this.closeEditorAction)();
this.editorActionsToolbar.addPrimaryAction(this.closeOneEditorAction)();
}
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
@@ -416,7 +429,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
// Actions
[
this.splitEditorAction,
this.closeEditorAction
this.closeOneEditorAction
].forEach((action) => {
action.dispose();
});

View File

@@ -1,66 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import URI from 'vs/base/common/uri';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope } from 'vs/workbench/common/memento';
export interface HtmlPreviewEditorViewState {
scrollYPercentage: number;
}
/**
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseWebviewEditor extends BaseEditor {
constructor(
id: string,
telemetryService: ITelemetryService,
themeService: IThemeService,
private storageService: IStorageService
) {
super(id, telemetryService, themeService);
}
private get viewStateStorageKey(): string {
return this.getId() + '.editorViewState';
}
protected saveViewState(resource: URI | string, editorViewState: HtmlPreviewEditorViewState): void {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
let editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (!editorViewStateMemento) {
editorViewStateMemento = Object.create(null);
memento[this.viewStateStorageKey] = editorViewStateMemento;
}
let fileViewState = editorViewStateMemento[resource.toString()];
if (!fileViewState) {
fileViewState = Object.create(null);
editorViewStateMemento[resource.toString()] = fileViewState;
}
if (typeof this.position === 'number') {
fileViewState[this.position] = editorViewState;
}
}
protected loadViewState(resource: URI | string): HtmlPreviewEditorViewState | null {
const memento = this.getMemento(this.storageService, Scope.WORKSPACE);
const editorViewStateMemento: { [key: string]: { [position: number]: HtmlPreviewEditorViewState } } = memento[this.viewStateStorageKey];
if (editorViewStateMemento) {
const fileViewState = editorViewStateMemento[resource.toString()];
if (fileViewState) {
return fileViewState[this.position];
}
}
return null;
}
}

View File

@@ -24,21 +24,21 @@
}
.vs .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .expand-notification-action {
background-image: url('down.svg');
background-image: url('up.svg');
}
.vs-dark .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .expand-notification-action,
.hc-black .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .expand-notification-action {
background-image: url('down-inverse.svg');
background-image: url('up-inverse.svg');
}
.vs .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .collapse-notification-action {
background-image: url('up.svg');
background-image: url('down.svg');
}
.vs-dark .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .collapse-notification-action,
.hc-black .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .collapse-notification-action {
background-image: url('up-inverse.svg');
background-image: url('down-inverse.svg');
}
.vs .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-toolbar-container .configure-notification-action {

View File

@@ -10,14 +10,13 @@ import 'vs/css!./media/notificationsActions';
import { Themable, NOTIFICATIONS_BORDER, NOTIFICATIONS_CENTER_HEADER_FOREGROUND, NOTIFICATIONS_CENTER_HEADER_BACKGROUND, NOTIFICATIONS_CENTER_BORDER } from 'vs/workbench/common/theme';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType } from 'vs/workbench/common/notifications';
import { Dimension } from 'vs/base/browser/builder';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { NotificationsCenterVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { addClass, removeClass, isAncestor } from 'vs/base/browser/dom';
import { addClass, removeClass, isAncestor, Dimension } from 'vs/base/browser/dom';
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { localize } from 'vs/nls';
@@ -32,10 +31,11 @@ export class NotificationsCenter extends Themable {
private notificationsCenterContainer: HTMLElement;
private notificationsCenterHeader: HTMLElement;
private notificationsCenterTitle: HTMLSpanElement;
private notificationsList: NotificationsList;
private _isVisible: boolean;
private workbenchDimensions: Dimension;
private _onDidChangeVisibility: Emitter<void>;
private readonly _onDidChangeVisibility: Emitter<void>;
private notificationsCenterVisibleContextKey: IContextKey<boolean>;
constructor(
@@ -71,10 +71,6 @@ export class NotificationsCenter extends Themable {
}
public show(): void {
if (this.model.notifications.length === 0) {
return; // currently not supporting to show empty (https://github.com/Microsoft/vscode/issues/44509)
}
if (this._isVisible) {
this.notificationsList.show(true /* focus */);
@@ -86,6 +82,9 @@ export class NotificationsCenter extends Themable {
this.create();
}
// Title
this.updateTitle();
// Make visible
this._isVisible = true;
addClass(this.notificationsCenterContainer, 'visible');
@@ -110,6 +109,14 @@ export class NotificationsCenter extends Themable {
this._onDidChangeVisibility.fire();
}
private updateTitle(): void {
if (this.model.notifications.length === 0) {
this.notificationsCenterTitle.innerText = localize('notificationsEmpty', "No new notifications");
} else {
this.notificationsCenterTitle.innerText = localize('notifications', "Notifications");
}
}
private create(): void {
// Container
@@ -122,10 +129,9 @@ export class NotificationsCenter extends Themable {
this.notificationsCenterContainer.appendChild(this.notificationsCenterHeader);
// Header Title
const title = document.createElement('span');
addClass(title, 'notifications-center-header-title');
title.innerText = localize('notifications', "Notifications");
this.notificationsCenterHeader.appendChild(title);
this.notificationsCenterTitle = document.createElement('span');
addClass(this.notificationsCenterTitle, 'notifications-center-header-title');
this.notificationsCenterHeader.appendChild(this.notificationsCenterTitle);
// Header Toolbar
const toolbarContainer = document.createElement('div');
@@ -184,6 +190,9 @@ export class NotificationsCenter extends Themable {
break;
}
// Update title
this.updateTitle();
// Hide if no more notifications to show
if (this.model.notifications.length === 0) {
this.hide();
@@ -280,7 +289,7 @@ export class NotificationsCenter extends Themable {
// Hide notifications center first
this.hide();
// Dispose all
// Close all
while (this.model.notifications.length) {
this.model.notifications[0].close();
}

View File

@@ -74,7 +74,7 @@ export class NotificationsList extends Themable {
const renderer = this.instantiationService.createInstance(NotificationRenderer, actionRunner);
// List
this.list = this.instantiationService.createInstance(
this.list = <WorkbenchList<INotificationViewItem>>this.instantiationService.createInstance(
WorkbenchList,
this.listContainer,
new NotificationsListDelegate(this.listContainer),

View File

@@ -75,7 +75,7 @@ export class NotificationsStatus {
// Create new
this.statusItem = this.statusbarService.addEntry({
text: this.count === 0 ? '$(bell)' : `$(bell) ${this.count}`,
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : this.model.notifications.length > 0 ? SHOW_NOTIFICATIONS_CENTER : void 0,
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
tooltip: this.getTooltip(),
showBeak: this.isNotificationsCenterVisible
}, StatusbarAlignment.RIGHT, -1000 /* towards the far end of the right hand side */);

View File

@@ -8,10 +8,9 @@
import 'vs/css!./media/notificationsToasts';
import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem, NotificationViewItemLabelKind } from 'vs/workbench/common/notifications';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { addClass, removeClass, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { addClass, removeClass, isAncestor, addDisposableListener, EventType, Dimension } from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList';
import { Dimension } from 'vs/base/browser/builder';
import { once } from 'vs/base/common/event';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { Themable, NOTIFICATIONS_TOAST_BORDER } from 'vs/workbench/common/theme';
@@ -122,8 +121,6 @@ export class NotificationsToasts extends Themable {
this.notificationsToastsContainer.appendChild(notificationToastContainer);
}
itemDisposeables.push(toDisposable(() => this.notificationsToastsContainer.removeChild(notificationToastContainer)));
// Toast
const notificationToast = document.createElement('div');
addClass(notificationToast, 'notification-toast');
@@ -135,7 +132,15 @@ export class NotificationsToasts extends Themable {
verticalScrollMode: ScrollbarVisibility.Hidden
});
itemDisposeables.push(notificationList);
this.mapNotificationToToast.set(item, { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, disposeables: itemDisposeables });
const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, disposeables: itemDisposeables };
this.mapNotificationToToast.set(item, toast);
itemDisposeables.push(toDisposable(() => {
if (this.isVisible(toast)) {
this.notificationsToastsContainer.removeChild(toast.container);
}
}));
// Make visible
notificationList.show();
@@ -163,7 +168,7 @@ export class NotificationsToasts extends Themable {
}
}));
// Remove when item gets disposed
// Remove when item gets closed
once(item.onDidClose)(() => {
this.removeToast(item);
});
@@ -431,6 +436,8 @@ export class NotificationsToasts extends Themable {
availableHeight -= (2 * 12); // adjust for paddings top and bottom
}
availableHeight = Math.round(availableHeight * 0.618); // try to not cover the full height for stacked toasts
return new Dimension(Math.min(maxWidth, availableWidth), availableHeight);
}
@@ -466,10 +473,18 @@ export class NotificationsToasts extends Themable {
}
private setVisibility(toast: INotificationToast, visible: boolean): void {
toast.container.style.display = visible ? 'block' : 'none';
if (this.isVisible(toast) === visible) {
return;
}
if (visible) {
this.notificationsToastsContainer.appendChild(toast.container);
} else {
this.notificationsToastsContainer.removeChild(toast.container);
}
}
private isVisible(toast: INotificationToast): boolean {
return toast.container.style.display === 'block';
return !!toast.container.parentElement;
}
}

View File

@@ -434,7 +434,9 @@ export class NotificationTemplateRenderer {
const action = notification.actions.primary[index];
button.label = action.label;
this.inputDisposeables.push(button.onDidClick(() => {
this.inputDisposeables.push(button.onDidClick(e => {
e.preventDefault();
e.stopPropagation();
// Run action
this.actionRunner.run(action, notification);
@@ -454,7 +456,7 @@ export class NotificationTemplateRenderer {
// Return early if the item has no progress
if (!notification.hasProgress()) {
this.template.progress.stop().getContainer().hide();
this.template.progress.stop().hide();
return;
}
@@ -462,23 +464,23 @@ export class NotificationTemplateRenderer {
// Infinite
const state = notification.progress.state;
if (state.infinite) {
this.template.progress.infinite().getContainer().show();
this.template.progress.infinite().show();
}
// Total / Worked
else if (state.total || state.worked) {
if (state.total) {
else if (typeof state.total === 'number' || typeof state.worked === 'number') {
if (typeof state.total === 'number' && !this.template.progress.hasTotal()) {
this.template.progress.total(state.total);
}
if (state.worked) {
this.template.progress.worked(state.worked).getContainer().show();
if (typeof state.worked === 'number') {
this.template.progress.worked(state.worked).show();
}
}
// Done
else {
this.template.progress.done().getContainer().hide();
this.template.progress.done().hide();
}
}

View File

@@ -30,10 +30,6 @@
border-left-style: solid;
}
.monaco-workbench > .part.panel > .composite.title > .title-actions {
flex-grow: 0;
}
.monaco-workbench > .part.panel > .title > .title-actions .monaco-action-bar .action-item .action-label {
outline-offset: -2px;
}
@@ -90,7 +86,7 @@
cursor: default;
}
.monaco-workbench .panel .monaco-action-bar .action-item .select-box {
.monaco-workbench .panel .monaco-action-bar .action-item .monaco-select-box {
cursor: pointer;
min-width: 110px;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/panelpart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';

View File

@@ -6,13 +6,13 @@
import 'vs/css!./media/panelpart';
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction, Action } from 'vs/base/common/actions';
import Event from 'vs/base/common/event';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { Event } from 'vs/base/common/event';
import { $ } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IPanel } from 'vs/workbench/common/panel';
import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart';
import { Panel, PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -30,6 +30,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension } from 'vs/base/browser/dom';
export class PanelPart extends CompositePart<Panel> implements IPanelService {
@@ -42,7 +43,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
private blockOpeningPanel: boolean;
private compositeBar: CompositeBar;
private dimension: Dimension;
private toolbarWidth = new Map<string, number>();
constructor(
id: string,
@@ -100,6 +100,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
private registerListeners(): void {
this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor, false)));
// Activate panel action on opening of a panel
this.toUnbind.push(this.onDidPanelOpen(panel => {
this.compositeBar.activateComposite(panel.getId());
@@ -123,11 +125,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
public updateStyles(): void {
super.updateStyles();
const container = this.getContainer();
const container = $(this.getContainer());
container.style('background-color', this.getColor(PANEL_BACKGROUND));
container.style('border-left-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder));
const title = this.getTitleArea();
const title = $(this.getTitleArea());
title.style('border-top-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder));
}
@@ -169,7 +171,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
});
}
public getPanels(): IPanelIdentifier[] {
public getPanels(): PanelDescriptor[] {
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels()
.filter(p => p.enabled)
.sort((v1, v2) => v1.order - v2.order);
@@ -180,7 +182,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
if (descriptor && descriptor.enabled !== enabled) {
descriptor.enabled = enabled;
if (enabled) {
this.compositeBar.addComposite(descriptor);
this.compositeBar.addComposite(descriptor, true);
} else {
this.compositeBar.removeComposite(id);
}
@@ -207,8 +209,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return this.hideActiveComposite().then(composite => void 0);
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
const titleArea = this.compositeBar.create(parent.getHTMLElement());
protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel {
const titleArea = this.compositeBar.create(parent);
titleArea.classList.add('panel-switcher-container');
return {
@@ -241,6 +243,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return sizes;
}
public shutdown(): void {
this.compositeBar.shutdown();
super.shutdown();
}
private layoutCompositeBar(): void {
if (this.dimension) {
let availableWidth = this.dimension.width - 40; // take padding into account
@@ -257,11 +264,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
if (!activePanel) {
return 0;
}
if (!this.toolbarWidth.has(activePanel.getId())) {
this.toolbarWidth.set(activePanel.getId(), this.toolBar.getContainer().getHTMLElement().offsetWidth);
}
return this.toolbarWidth.get(activePanel.getId());
return this.toolBar.getItemsWidth();
}
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.quick-input-widget {
position: absolute;
width: 600px;
z-index: 2000;
padding-bottom: 6px;
left: 50%;
margin-left: -300px;
}
.quick-input-header {
display: flex;
padding: 6px 6px 4px 6px;
}
.quick-input-check-all {
align-self: center;
margin: 0;
}
.quick-input-filter {
flex-grow: 1;
display: flex;
position: relative;
}
.quick-input-box {
flex-grow: 1;
}
.quick-input-widget[data-type=selectMany] .quick-input-box {
margin-left: 5px;
}
.quick-input-count {
align-self: center;
position: absolute;
right: 4px;
}
.quick-input-count .monaco-count-badge {
vertical-align: middle;
}
.quick-input-action {
margin-left: 6px;
}
.quick-input-action .monaco-text-button {
font-size: 85%;
padding: 7px 6px 6px 6px;
line-height: initial;
}
.quick-input-message {
margin: 0px 11px;
}
.quick-input-progress.monaco-progress-container,
.quick-input-progress.monaco-progress-container .progress-bit {
height: 2px;
}
.quick-input-checkbox-list {
line-height: 22px;
}
.quick-input-checkbox-list .monaco-list {
overflow: hidden;
max-height: calc(20 * 22px);
}
.quick-input-checkbox-list .quick-input-checkbox-list-entry {
overflow: hidden;
display: flex;
height: 100%;
padding: 0 6px;
}
.quick-input-checkbox-list .quick-input-checkbox-list-label {
overflow: hidden;
display: flex;
height: 100%;
flex: 1;
}
.quick-input-checkbox-list .quick-input-checkbox-list-checkbox {
align-self: center;
margin: 0;
/* TODO */
/* margin-top: 5px; */
}
.quick-input-checkbox-list .quick-input-checkbox-list-rows {
overflow: hidden;
text-overflow: ellipsis;
display: flex;
flex-direction: column;
height: 100%;
flex: 1;
margin-left: 10px;
}
.quick-input-checkbox-list .quick-input-checkbox-list-rows > .quick-input-checkbox-list-row {
display: flex;
align-items: center;
}
.quick-input-checkbox-list .quick-input-checkbox-list-rows .monaco-highlighted-label span {
opacity: 1;
}
.quick-input-checkbox-list .quick-input-checkbox-list-label-meta {
opacity: 0.7;
line-height: normal;
}

View File

@@ -0,0 +1,526 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./quickInput';
import { Component } from 'vs/workbench/common/component';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import * as dom from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IQuickOpenService, IPickOpenEntry, IPickOptions, IInputOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickInputCheckboxList } from './quickInputCheckboxList';
import { QuickInputBox } from './quickInputBox';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { attachBadgeStyler, attachProgressBarStyler, attachButtonStyler } from 'vs/platform/theme/common/styler';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { chain, debounceEvent } from 'vs/base/common/event';
import { Button } from 'vs/base/browser/ui/button/button';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError, canceled } from 'vs/base/common/errors';
import Severity from 'vs/base/common/severity';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
const $ = dom.$;
type InputParameters = SelectManyParameters | TextInputParameters;
export interface BaseInputParameters {
readonly type: 'selectMany' | 'textInput';
readonly ignoreFocusLost?: boolean;
}
export interface SelectManyParameters<T extends IPickOpenEntry = IPickOpenEntry> extends BaseInputParameters {
readonly type: 'selectMany';
readonly picks: TPromise<T[]>;
readonly matchOnDescription?: boolean;
readonly matchOnDetail?: boolean;
readonly placeHolder?: string;
}
export interface TextInputParameters extends BaseInputParameters {
readonly type: 'textInput';
readonly value?: string;
readonly valueSelection?: [number, number];
readonly prompt?: string;
readonly placeHolder?: string;
readonly password?: boolean;
readonly validateInput?: (input: string) => TPromise<string>;
}
interface QuickInputUI {
checkAll: HTMLInputElement;
inputBox: QuickInputBox;
count: CountBadge;
message: HTMLElement;
checkboxList: QuickInputCheckboxList;
}
interface InputController<R> {
readonly showUI: { [k in keyof QuickInputUI]?: boolean; } & { ok?: boolean; };
readonly result: TPromise<R>;
readonly ready: TPromise<void>;
readonly resolve: (ok?: true | Thenable<never>) => void | TPromise<void>;
}
class SelectManyController<T extends IPickOpenEntry> implements InputController<T[]> {
public showUI = { checkAll: true, inputBox: true, count: true, ok: true, checkboxList: true };
public result: TPromise<T[]>;
public ready: TPromise<void>;
public resolve: (ok?: true | Thenable<never>) => void;
public progress: (value: T) => void;
private closed = false;
constructor(ui: QuickInputUI, parameters: SelectManyParameters<T>) {
this.result = new TPromise<T[]>((resolve, reject, progress) => {
this.resolve = ok => resolve(ok === true ? <T[]>ui.checkboxList.getCheckedElements() : ok);
this.progress = progress;
});
this.result.then(() => this.closed = true, () => this.closed = true);
ui.inputBox.value = '';
ui.inputBox.setPlaceholder(parameters.placeHolder || '');
ui.checkboxList.matchOnDescription = parameters.matchOnDescription;
ui.checkboxList.matchOnDetail = parameters.matchOnDetail;
ui.checkboxList.setElements([]);
ui.checkAll.checked = ui.checkboxList.getAllVisibleChecked();
ui.count.setCount(ui.checkboxList.getCheckedCount());
this.ready = parameters.picks.then(elements => {
if (this.closed) {
return;
}
ui.checkboxList.setElements(elements);
ui.checkboxList.filter(ui.inputBox.value);
ui.checkAll.checked = ui.checkboxList.getAllVisibleChecked();
ui.count.setCount(ui.checkboxList.getCheckedCount());
});
}
}
class TextInputController implements InputController<string> {
public showUI = { inputBox: true, message: true };
public result: TPromise<string>;
public ready = TPromise.as(null);
public resolveResult: (string) => void;
private validationValue: string;
private validation: TPromise<string>;
private defaultMessage: string;
private disposables: IDisposable[] = [];
constructor(private ui: QuickInputUI, private parameters: TextInputParameters) {
this.result = new TPromise<string>((resolve, reject, progress) => {
this.resolveResult = resolve;
});
this.result.then(() => this.dispose());
ui.inputBox.value = parameters.value || '';
const selection = parameters.valueSelection;
ui.inputBox.select(selection && { start: selection[0], end: selection[1] });
ui.inputBox.setPlaceholder(parameters.placeHolder || '');
this.defaultMessage = parameters.prompt
? localize('inputModeEntryDescription', "{0} (Press 'Enter' to confirm or 'Escape' to cancel)", parameters.prompt)
: localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel");
ui.message.textContent = this.defaultMessage;
ui.inputBox.setPassword(parameters.password);
if (parameters.validateInput) {
const onDidChange = debounceEvent(ui.inputBox.onDidChange, (last, cur) => cur, 100);
this.disposables.push(onDidChange(() => this.didChange()));
if (ui.inputBox.value) {
// Replicating old behavior: only fire if value is not empty.
this.didChange();
}
}
}
didChange() {
this.updatedValidation()
.then(validationError => {
this.ui.message.textContent = validationError || this.defaultMessage;
this.ui.inputBox.showDecoration(validationError ? Severity.Error : Severity.Ignore);
})
.then(null, onUnexpectedError);
}
resolve(ok?: true | Thenable<never>) {
if (ok === true) {
return this.updatedValidation()
.then(validationError => {
if (validationError) {
throw canceled();
}
this.resolveResult(this.ui.inputBox.value);
});
} else {
this.resolveResult(ok);
}
return null;
}
private updatedValidation() {
if (this.parameters.validateInput) {
const value = this.ui.inputBox.value;
if (value !== this.validationValue) {
this.validationValue = value;
this.validation = this.parameters.validateInput(value)
.then(validationError => {
if (this.validationValue !== value) {
throw canceled();
}
return validationError;
});
}
} else if (!this.validation) {
this.validation = TPromise.as(null);
}
return this.validation;
}
private dispose() {
this.disposables = dispose(this.disposables);
}
}
export class QuickInputService extends Component implements IQuickInputService {
public _serviceBrand: any;
private static readonly ID = 'workbench.component.quickinput';
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
private layoutDimensions: dom.Dimension;
private container: HTMLElement;
private filterContainer: HTMLElement;
private countContainer: HTMLElement;
private okContainer: HTMLElement;
private ui: QuickInputUI;
private ready = false;
private progressBar: ProgressBar;
private ignoreFocusLost = false;
private controller: InputController<any>;
constructor(
@IEnvironmentService private environmentService: IEnvironmentService,
@IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService private instantiationService: IInstantiationService,
@IPartService private partService: IPartService,
@IQuickOpenService private quickOpenService: IQuickOpenService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IThemeService themeService: IThemeService
) {
super(QuickInputService.ID, themeService);
}
private create() {
if (this.container) {
return;
}
const workbench = document.getElementById(this.partService.getWorkbenchElementId());
this.container = dom.append(workbench, $('.quick-input-widget'));
this.container.tabIndex = -1;
this.container.style.display = 'none';
const headerContainer = dom.append(this.container, $('.quick-input-header'));
const checkAll = <HTMLInputElement>dom.append(headerContainer, $('input.quick-input-check-all'));
checkAll.type = 'checkbox';
this.toUnbind.push(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => {
const checked = checkAll.checked;
checkboxList.setAllVisibleChecked(checked);
}));
this.toUnbind.push(dom.addDisposableListener(checkAll, dom.EventType.CLICK, e => {
if (e.x || e.y) { // Avoid 'click' triggered by 'space'...
inputBox.setFocus();
}
}));
this.filterContainer = dom.append(headerContainer, $('.quick-input-filter'));
const inputBox = new QuickInputBox(this.filterContainer);
this.toUnbind.push(inputBox);
inputBox.onDidChange(value => {
checkboxList.filter(value);
});
this.toUnbind.push(inputBox.onKeyDown(event => {
if (!checkboxList.isDisplayed()) {
return;
}
switch (event.keyCode) {
case KeyCode.DownArrow:
checkboxList.focus('First');
checkboxList.domFocus();
break;
case KeyCode.UpArrow:
checkboxList.focus('Last');
checkboxList.domFocus();
break;
}
}));
this.countContainer = dom.append(this.filterContainer, $('.quick-input-count'));
const count = new CountBadge(this.countContainer, { countFormat: localize('quickInput.countSelected', "{0} Selected") });
this.toUnbind.push(attachBadgeStyler(count, this.themeService));
this.okContainer = dom.append(headerContainer, $('.quick-input-action'));
const ok = new Button(this.okContainer);
attachButtonStyler(ok, this.themeService);
ok.label = localize('ok', "OK");
this.toUnbind.push(ok.onDidClick(e => {
if (this.ready) {
this.close(true);
}
}));
const message = dom.append(this.container, $('.quick-input-message'));
this.progressBar = new ProgressBar(this.container);
dom.addClass(this.progressBar.getContainer(), 'quick-input-progress');
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
const checkboxList = this.instantiationService.createInstance(QuickInputCheckboxList, this.container);
this.toUnbind.push(checkboxList);
this.toUnbind.push(checkboxList.onAllVisibleCheckedChanged(checked => {
checkAll.checked = checked;
}));
this.toUnbind.push(checkboxList.onCheckedCountChanged(c => {
count.setCount(c);
}));
this.toUnbind.push(checkboxList.onLeave(() => {
// Defer to avoid the input field reacting to the triggering key.
setTimeout(() => {
inputBox.setFocus();
checkboxList.clearFocus();
}, 0);
}));
this.toUnbind.push(
chain(checkboxList.onFocusChange)
.map(e => e[0])
.filter(e => !!e)
.latch()
.on(e => this.controller instanceof SelectManyController && this.controller.progress(e)) // TODO
);
this.toUnbind.push(dom.addDisposableListener(this.container, 'focusout', (e: FocusEvent) => {
if (e.relatedTarget === this.container) {
(<HTMLElement>e.target).focus();
return;
}
for (let element = <Element>e.relatedTarget; element; element = element.parentElement) {
if (element === this.container) {
return;
}
}
if (!this.ignoreFocusLost && !this.environmentService.args['sticky-quickopen'] && this.configurationService.getValue(CLOSE_ON_FOCUS_LOST_CONFIG)) {
this.close(undefined, true);
}
}));
this.toUnbind.push(dom.addDisposableListener(this.container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {
case KeyCode.Enter:
if (this.ready) {
dom.EventHelper.stop(e, true);
this.close(true);
}
break;
case KeyCode.Escape:
dom.EventHelper.stop(e, true);
this.close();
break;
case KeyCode.Tab:
if (!event.altKey && !event.ctrlKey && !event.metaKey) {
const inputs = this.container.querySelectorAll('input');
if (event.shiftKey && event.target === inputs[0]) {
dom.EventHelper.stop(e, true);
inputs[inputs.length - 1].focus();
} else if (!event.shiftKey && event.target === inputs[inputs.length - 1]) {
dom.EventHelper.stop(e, true);
inputs[0].focus();
}
}
break;
}
}));
this.toUnbind.push(this.quickOpenService.onShow(() => this.close()));
this.ui = { checkAll, inputBox, count, message, checkboxList };
this.updateStyles();
}
private close(ok?: true | Thenable<never>, focusLost?: boolean) {
if (!this.container || this.container.style.display === 'none') {
return TPromise.as(undefined);
}
if (this.controller) {
const resolved = this.controller.resolve(ok);
if (resolved) {
const result = resolved
.then(() => {
this.container.style.display = 'none';
if (!focusLost) {
this.restoreFocus();
}
});
result.then(null, onUnexpectedError);
return result;
}
}
this.container.style.display = 'none';
if (!focusLost) {
this.restoreFocus();
}
return TPromise.as(undefined);
}
private restoreFocus(): void {
const editor = this.editorService.getActiveEditor();
if (editor) {
editor.focus();
}
}
pick<T extends IPickOpenEntry>(picks: TPromise<T[]>, options: IPickOptions = {}, token?: CancellationToken): TPromise<T[]> {
return this.show({
type: 'selectMany',
picks,
placeHolder: options.placeHolder,
matchOnDescription: options.matchOnDescription,
matchOnDetail: options.matchOnDetail,
ignoreFocusLost: options.ignoreFocusLost
}, token);
}
input(options: IInputOptions = {}, token?: CancellationToken): TPromise<string> {
return this.show({
type: 'textInput',
value: options.value,
valueSelection: options.valueSelection,
prompt: options.prompt,
placeHolder: options.placeHolder,
password: options.password,
ignoreFocusLost: options.ignoreFocusLost,
validateInput: options.validateInput,
}, token);
}
show<T extends IPickOpenEntry>(parameters: SelectManyParameters<T>, token?: CancellationToken): TPromise<T[]>;
show(parameters: TextInputParameters, token?: CancellationToken): TPromise<string>;
show<R>(parameters: InputParameters, token: CancellationToken = CancellationToken.None): TPromise<R> {
this.create();
this.quickOpenService.close();
if (this.controller) {
this.controller.resolve();
}
this.container.setAttribute('data-type', parameters.type);
this.ignoreFocusLost = parameters.ignoreFocusLost;
this.progressBar.stop();
this.ready = false;
this.controller = parameters.type === 'selectMany' ? new SelectManyController(this.ui, parameters) : new TextInputController(this.ui, parameters);
this.ui.checkAll.style.display = this.controller.showUI.checkAll ? null : 'none';
this.filterContainer.style.display = this.controller.showUI.inputBox ? null : 'none';
this.ui.inputBox.showDecoration(Severity.Ignore);
this.countContainer.style.display = this.controller.showUI.count ? null : 'none';
this.okContainer.style.display = this.controller.showUI.ok ? null : 'none';
this.ui.message.style.display = this.controller.showUI.message ? null : 'none';
this.ui.checkboxList.display(this.controller.showUI.checkboxList);
this.container.style.display = null;
this.updateLayout();
this.ui.inputBox.setFocus();
const d = token.onCancellationRequested(() => this.close());
this.controller.result.then(() => d.dispose(), () => d.dispose());
const delay = TPromise.timeout(800);
delay.then(() => this.progressBar.infinite(), () => { /* ignore */ });
const wasController = this.controller;
this.controller.ready.then(() => {
delay.cancel();
if (this.controller !== wasController) {
return;
}
this.progressBar.stop();
this.ready = true;
this.updateLayout();
}).then(null, reason => this.close(TPromise.wrapError(reason)));
return this.controller.result;
}
focus() {
if (this.ui) {
this.ui.inputBox.setFocus();
}
}
accept() {
return this.close(true);
}
cancel() {
return this.close();
}
layout(dimension: dom.Dimension): void {
this.layoutDimensions = dimension;
this.updateLayout();
}
private updateLayout() {
if (this.layoutDimensions && this.container) {
const titlebarOffset = this.partService.getTitleBarOffset();
this.container.style.top = `${titlebarOffset}px`;
const style = this.container.style;
const width = Math.min(this.layoutDimensions.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH);
style.width = width + 'px';
style.marginLeft = '-' + (width / 2) + 'px';
this.ui.inputBox.layout();
this.ui.checkboxList.layout();
}
}
protected updateStyles() {
const theme = this.themeService.getTheme();
if (this.ui) {
this.ui.inputBox.style(theme);
}
if (this.container) {
const sideBarBackground = theme.getColor(SIDE_BAR_BACKGROUND);
this.container.style.backgroundColor = sideBarBackground ? sideBarBackground.toString() : undefined;
const sideBarForeground = theme.getColor(SIDE_BAR_FOREGROUND);
this.container.style.color = sideBarForeground ? sideBarForeground.toString() : undefined;
const contrastBorderColor = theme.getColor(contrastBorder);
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : undefined;
const widgetShadowColor = theme.getColor(widgetShadow);
this.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : undefined;
}
}
}

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./quickInput';
import * as dom from 'vs/base/browser/dom';
import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { localize } from 'vs/nls';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Severity from 'vs/base/common/severity';
const $ = dom.$;
const DEFAULT_INPUT_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results.");
export class QuickInputBox {
private container: HTMLElement;
private inputBox: InputBox;
private disposables: IDisposable[] = [];
constructor(
private parent: HTMLElement
) {
this.container = dom.append(this.parent, $('.quick-input-box'));
this.inputBox = new InputBox(this.container, null, {
ariaLabel: DEFAULT_INPUT_ARIA_LABEL
});
this.disposables.push(this.inputBox);
// ARIA
const inputElement = this.inputBox.inputElement;
inputElement.setAttribute('role', 'combobox');
inputElement.setAttribute('aria-haspopup', 'false');
inputElement.setAttribute('aria-autocomplete', 'list');
}
onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => {
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
handler(new StandardKeyboardEvent(e));
});
}
onDidChange = (handler: (event: string) => void): IDisposable => {
return this.inputBox.onDidChange(handler);
}
get value() {
return this.inputBox.value;
}
set value(value: string) {
this.inputBox.value = value;
}
select(range: IRange = null): void {
this.inputBox.select(range);
}
setPlaceholder(placeholder: string) {
this.inputBox.setPlaceHolder(placeholder);
}
setPassword(isPassword: boolean): void {
this.inputBox.inputElement.type = isPassword ? 'password' : 'text';
}
showDecoration(decoration: Severity): void {
if (decoration === Severity.Ignore) {
this.inputBox.hideMessage();
} else {
this.inputBox.showMessage({ type: decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR, content: '' });
}
}
setFocus(): void {
this.inputBox.focus();
}
layout(): void {
this.inputBox.layout();
}
style(theme: ITheme) {
this.inputBox.style({
inputForeground: theme.getColor(inputForeground),
inputBackground: theme.getColor(inputBackground),
inputBorder: theme.getColor(inputBorder),
inputValidationInfoBackground: theme.getColor(inputValidationInfoBackground),
inputValidationInfoBorder: theme.getColor(inputValidationInfoBorder),
inputValidationWarningBackground: theme.getColor(inputValidationWarningBackground),
inputValidationWarningBorder: theme.getColor(inputValidationWarningBorder),
inputValidationErrorBackground: theme.getColor(inputValidationErrorBackground),
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
});
}
dispose() {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -0,0 +1,389 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./quickInput';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon';
import { compareAnything } from 'vs/base/common/comparers';
import { Emitter, Event, mapEvent } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { memoize } from 'vs/base/common/decorators';
import { range } from 'vs/base/common/arrays';
import * as platform from 'vs/base/common/platform';
const $ = dom.$;
interface ICheckableElement {
index: number;
item: IPickOpenEntry;
checked: boolean;
}
class CheckableElement implements ICheckableElement {
index: number;
item: IPickOpenEntry;
shouldAlwaysShow = false;
hidden = false;
private _onChecked = new Emitter<boolean>();
onChecked = this._onChecked.event;
_checked: boolean;
get checked() {
return this._checked;
}
set checked(value: boolean) {
if (value !== this._checked) {
this._checked = value;
this._onChecked.fire(value);
}
}
labelHighlights?: IMatch[];
descriptionHighlights?: IMatch[];
detailHighlights?: IMatch[];
constructor(init: ICheckableElement) {
assign(this, init);
}
}
interface ICheckableElementTemplateData {
checkbox: HTMLInputElement;
label: IconLabel;
detail: HighlightedLabel;
element: CheckableElement;
toDisposeElement: IDisposable[];
toDisposeTemplate: IDisposable[];
}
class CheckableElementRenderer implements IRenderer<CheckableElement, ICheckableElementTemplateData> {
static readonly ID = 'checkableelement';
get templateId() {
return CheckableElementRenderer.ID;
}
renderTemplate(container: HTMLElement): ICheckableElementTemplateData {
const data: ICheckableElementTemplateData = Object.create(null);
const entry = dom.append(container, $('.quick-input-checkbox-list-entry'));
const label = dom.append(entry, $('label.quick-input-checkbox-list-label'));
// Entry
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-checkbox-list-checkbox'));
data.checkbox.type = 'checkbox';
data.toDisposeElement = [];
data.toDisposeTemplate = [];
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
data.element.checked = data.checkbox.checked;
}));
const rows = dom.append(label, $('.quick-input-checkbox-list-rows'));
const row1 = dom.append(rows, $('.quick-input-checkbox-list-row'));
const row2 = dom.append(rows, $('.quick-input-checkbox-list-row'));
// Label
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true });
// Detail
const detailContainer = dom.append(row2, $('.quick-input-checkbox-list-label-meta'));
data.detail = new HighlightedLabel(detailContainer);
return data;
}
renderElement(element: CheckableElement, index: number, data: ICheckableElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
data.element = element;
data.checkbox.checked = element.checked;
data.toDisposeElement.push(element.onChecked(checked => data.checkbox.checked = checked));
const { labelHighlights, descriptionHighlights, detailHighlights } = element;
// Label
const options: IIconLabelValueOptions = Object.create(null);
options.matches = labelHighlights || [];
options.descriptionTitle = element.item.description;
options.descriptionMatches = descriptionHighlights || [];
data.label.setValue(element.item.label, element.item.description, options);
// Meta
data.detail.set(element.item.detail, detailHighlights);
}
disposeTemplate(data: ICheckableElementTemplateData): void {
data.toDisposeElement = dispose(data.toDisposeElement);
data.toDisposeTemplate = dispose(data.toDisposeTemplate);
}
}
class CheckableElementDelegate implements IDelegate<CheckableElement> {
getHeight(element: CheckableElement): number {
return element.item.detail ? 44 : 22;
}
getTemplateId(element: CheckableElement): string {
return CheckableElementRenderer.ID;
}
}
export class QuickInputCheckboxList {
private container: HTMLElement;
private list: WorkbenchList<CheckableElement>;
private elements: CheckableElement[] = [];
matchOnDescription = false;
matchOnDetail = false;
private _onAllVisibleCheckedChanged = new Emitter<boolean>(); // TODO: Debounce
onAllVisibleCheckedChanged: Event<boolean> = this._onAllVisibleCheckedChanged.event;
private _onCheckedCountChanged = new Emitter<number>(); // TODO: Debounce
onCheckedCountChanged: Event<number> = this._onCheckedCountChanged.event;
private _onLeave = new Emitter<void>();
onLeave: Event<void> = this._onLeave.event;
private _fireCheckedEvents = true;
private elementDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
constructor(
private parent: HTMLElement,
@IInstantiationService private instantiationService: IInstantiationService
) {
this.container = dom.append(this.parent, $('.quick-input-checkbox-list'));
const delegate = new CheckableElementDelegate();
this.list = this.instantiationService.createInstance(WorkbenchList, this.container, delegate, [new CheckableElementRenderer()], {
identityProvider: element => element.label,
multipleSelectionSupport: false
}) as WorkbenchList<CheckableElement>;
this.disposables.push(this.list);
this.disposables.push(this.list.onKeyDown(e => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {
case KeyCode.Space:
this.toggleCheckbox();
break;
case KeyCode.KEY_A:
if (platform.isMacintosh ? e.metaKey : e.ctrlKey) {
this.list.setFocus(range(this.list.length));
}
break;
case KeyCode.UpArrow:
const focus1 = this.list.getFocus();
if (focus1.length === 1 && focus1[0] === 0) {
this._onLeave.fire();
}
break;
case KeyCode.DownArrow:
const focus2 = this.list.getFocus();
if (focus2.length === 1 && focus2[0] === this.list.length - 1) {
this._onLeave.fire();
}
break;
}
}));
this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => {
if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox.
this._onLeave.fire();
}
}));
this.disposables.push(this.list.onSelectionChange(e => {
if (e.elements.length) {
this.list.setSelection([]);
}
}));
}
@memoize
get onFocusChange() {
return mapEvent(this.list.onFocusChange, e => e.elements.map(e => e.item));
}
getAllVisibleChecked() {
return this.allVisibleChecked(this.elements, false);
}
private allVisibleChecked(elements: CheckableElement[], whenNoneVisible = true) {
for (let i = 0, n = elements.length; i < n; i++) {
const element = elements[i];
if (!element.hidden) {
if (!element.checked) {
return false;
} else {
whenNoneVisible = true;
}
}
}
return whenNoneVisible;
}
getCheckedCount() {
let count = 0;
const elements = this.elements;
for (let i = 0, n = elements.length; i < n; i++) {
if (elements[i].checked) {
count++;
}
}
return count;
}
setAllVisibleChecked(checked: boolean) {
try {
this._fireCheckedEvents = false;
this.elements.forEach(element => {
if (!element.hidden) {
element.checked = checked;
}
});
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
setElements(elements: IPickOpenEntry[]): void {
this.elementDisposables = dispose(this.elementDisposables);
this.elements = elements.map((item, index) => new CheckableElement({
index,
item,
checked: !!item.picked
}));
this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents())));
this.list.splice(0, this.list.length, this.elements);
this.list.setFocus([]);
}
getCheckedElements() {
return this.elements.filter(e => e.checked)
.map(e => e.item);
}
focus(what: 'First' | 'Last' | 'Next' | 'Previous' | 'NextPage' | 'PreviousPage'): void {
this.list['focus' + what]();
this.list.reveal(this.list.getFocus()[0]);
}
clearFocus() {
this.list.setFocus([]);
}
domFocus() {
this.list.domFocus();
}
layout(): void {
this.list.layout();
}
filter(query: string) {
query = query.trim();
// Reset filtering
if (!query) {
this.elements.forEach(element => {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = false;
});
}
// Filter by value (since we support octicons, use octicon aware fuzzy matching)
else {
this.elements.forEach(element => {
const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.item.label));
const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.item.description || '')) : undefined;
const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.item.detail || '')) : undefined;
if (element.shouldAlwaysShow || labelHighlights || descriptionHighlights || detailHighlights) {
element.labelHighlights = labelHighlights;
element.descriptionHighlights = descriptionHighlights;
element.detailHighlights = detailHighlights;
element.hidden = false;
} else {
element.labelHighlights = undefined;
element.descriptionHighlights = undefined;
element.detailHighlights = undefined;
element.hidden = true;
}
});
}
// Sort by value
const normalizedSearchValue = query.toLowerCase();
this.elements.sort((a, b) => {
if (!query) {
return a.index - b.index; // restore natural order
}
return compareEntries(a, b, normalizedSearchValue);
});
this.list.splice(0, this.list.length, this.elements.filter(element => !element.hidden));
this.list.setFocus([]);
this.list.layout();
this._onAllVisibleCheckedChanged.fire(this.getAllVisibleChecked());
}
toggleCheckbox() {
try {
this._fireCheckedEvents = false;
const elements = this.list.getFocusedElements();
const allChecked = this.allVisibleChecked(elements);
for (const element of elements) {
element.checked = !allChecked;
}
} finally {
this._fireCheckedEvents = true;
this.fireCheckedEvents();
}
}
display(display: boolean) {
this.container.style.display = display ? null : 'none';
}
isDisplayed() {
return this.container.style.display !== 'none';
}
dispose() {
this.elementDisposables = dispose(this.elementDisposables);
this.disposables = dispose(this.disposables);
}
private fireCheckedEvents() {
if (this._fireCheckedEvents) {
this._onAllVisibleCheckedChanged.fire(this.getAllVisibleChecked());
this._onCheckedCountChanged.fire(this.getCheckedCount());
}
}
}
function compareEntries(elementA: CheckableElement, elementB: CheckableElement, lookFor: string): number {
const labelHighlightsA = elementA.labelHighlights || [];
const labelHighlightsB = elementB.labelHighlights || [];
if (labelHighlightsA.length && !labelHighlightsB.length) {
return -1;
}
if (!labelHighlightsA.length && labelHighlightsB.length) {
return 1;
}
return compareAnything(elementA.item.label, elementB.item.label, lookFor);
}

View File

@@ -3,22 +3,22 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.none,
.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.none {
.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.none,
.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.none {
width: 16px;
background: none;
}
.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
width: 14px;
height: 18px;
}
.vs .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
background-image: url('dirty.svg');
}
.hc-black .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty,
.vs-dark .monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty,
.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.dirty {
background-image: url('dirty-inverse.svg');
}

View File

@@ -7,15 +7,13 @@
import 'vs/css!./media/quickopen';
import { TPromise, ValueCallback } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import * as browser from 'vs/base/browser/browser';
import { Dimension, withElementById } from 'vs/base/browser/builder';
import strings = require('vs/base/common/strings');
import DOM = require('vs/base/browser/dom');
import * as strings from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import types = require('vs/base/common/types');
import * as types from 'vs/base/common/types';
import { Action, IAction } from 'vs/base/common/actions';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -23,7 +21,7 @@ import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel
import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { QuickOpenWidget, HideReason } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import labels = require('vs/base/common/labels');
import * as labels from 'vs/base/common/labels';
import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
import { Registry } from 'vs/platform/registry/common/platform';
import { IResourceInput, IEditorInput } from 'vs/platform/editor/common/editor';
@@ -32,10 +30,10 @@ import { getIconClasses } from 'vs/workbench/browser/labels';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { Component } from 'vs/workbench/common/component';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG } from 'vs/workbench/browser/quickopen';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IPickOpenEntry, IFilePickOpenEntry, IInputOptions, IQuickOpenService, IPickOptions, IShowOptions, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -51,13 +49,13 @@ import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { FileKind, IFileService } from 'vs/platform/files/common/files';
import { scoreItem, ScorerCache, compareItemsByScore, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { getBaseLabel } from 'vs/base/common/labels';
import { WorkbenchTree } from 'vs/platform/list/browser/listService';
import { matchesFuzzyOcticonAware, parseOcticons, IParsedOcticons } from 'vs/base/common/octicon';
import { IMatch } from 'vs/base/common/filters';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, addClass } from 'vs/base/browser/dom';
const HELP_PREFIX = '?';
@@ -84,8 +82,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
private static readonly ID = 'workbench.component.quickopen';
private _onShow: Emitter<void>;
private _onHide: Emitter<void>;
private readonly _onShow: Emitter<void>;
private readonly _onHide: Emitter<void>;
private quickOpenWidget: QuickOpenWidget;
private pickOpenWidget: QuickOpenWidget;
@@ -206,7 +204,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
currentDecoration = !!message ? Severity.Error : void 0;
const newPick = message || defaultMessage;
if (newPick !== currentPick) {
options.valueSelection = [lastValue.length, lastValue.length];
options.valueSelection = null;
currentPick = newPick;
resolve(new TPromise<any>(init));
}
@@ -303,7 +301,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Create upon first open
if (!this.pickOpenWidget) {
this.pickOpenWidget = new QuickOpenWidget(
withElementById(this.partService.getWorkbenchElementId()).getHTMLElement(),
document.getElementById(this.partService.getWorkbenchElementId()),
{
onOk: () => { /* ignore, handle later */ },
onCancel: () => { /* ignore, handle later */ },
@@ -319,7 +317,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
this.toUnbind.push(attachQuickOpenStyler(this.pickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
const pickOpenContainer = this.pickOpenWidget.create();
DOM.addClass(pickOpenContainer, 'show-file-icons');
addClass(pickOpenContainer, 'show-file-icons');
this.positionQuickOpenWidget();
}
@@ -375,7 +373,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
picksPromiseDone = true;
// Reset Progress
this.pickOpenWidget.getProgressBar().stop().getContainer().hide();
this.pickOpenWidget.getProgressBar().stop().hide();
// Model
const model = new QuickOpenModel([], new PickOpenActionProvider());
@@ -488,7 +486,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Progress if task takes a long time
TPromise.timeout(800).then(() => {
if (!picksPromiseDone && this.currentPickerToken === currentPickerToken) {
this.pickOpenWidget.getProgressBar().infinite().getContainer().show();
this.pickOpenWidget.getProgressBar().infinite().show();
}
});
@@ -558,7 +556,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Create upon first open
if (!this.quickOpenWidget) {
this.quickOpenWidget = new QuickOpenWidget(
withElementById(this.partService.getWorkbenchElementId()).getHTMLElement(),
document.getElementById(this.partService.getWorkbenchElementId()),
{
onOk: () => { /* ignore */ },
onCancel: () => { /* ignore */ },
@@ -575,7 +573,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
this.toUnbind.push(attachQuickOpenStyler(this.quickOpenWidget, this.themeService, { background: SIDE_BAR_BACKGROUND, foreground: SIDE_BAR_FOREGROUND }));
const quickOpenContainer = this.quickOpenWidget.create();
DOM.addClass(quickOpenContainer, 'show-file-icons');
addClass(quickOpenContainer, 'show-file-icons');
this.positionQuickOpenWidget();
}
@@ -624,11 +622,11 @@ export class QuickOpenController extends Component implements IQuickOpenService
const titlebarOffset = this.partService.getTitleBarOffset();
if (this.quickOpenWidget) {
this.quickOpenWidget.getElement().style('top', `${titlebarOffset}px`);
this.quickOpenWidget.getElement().style.top = `${titlebarOffset}px`;
}
if (this.pickOpenWidget) {
this.pickOpenWidget.getElement().style('top', `${titlebarOffset}px`);
this.pickOpenWidget.getElement().style.top = `${titlebarOffset}px`;
}
}
@@ -743,7 +741,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Reset Progress
if (!instantProgress) {
this.quickOpenWidget.getProgressBar().stop().getContainer().hide();
this.quickOpenWidget.getProgressBar().stop().hide();
}
// Reset Extra Class
@@ -785,7 +783,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Progress if task takes a long time
TPromise.timeout(instantProgress ? 0 : 800).then(() => {
if (!resultPromiseDone && currentResultToken === this.currentResultToken) {
this.quickOpenWidget.getProgressBar().infinite().getContainer().show();
this.quickOpenWidget.getProgressBar().infinite().show();
}
});
@@ -794,7 +792,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
resultPromiseDone = true;
if (currentResultToken === this.currentResultToken) {
this.quickOpenWidget.getProgressBar().getContainer().hide();
this.quickOpenWidget.getProgressBar().hide();
}
}, (error: any) => {
resultPromiseDone = true;
@@ -1292,7 +1290,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
} else {
const resourceInput = input as IResourceInput;
this.resource = resourceInput.resource;
this.label = getBaseLabel(resourceInput.resource);
this.label = labels.getBaseLabel(resourceInput.resource);
this.description = labels.getPathLabel(resources.dirname(this.resource), contextService, environmentService);
this.dirty = this.resource && this.textFileService.isDirty(this.resource);

View File

@@ -12,6 +12,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { RemoveFromEditorHistoryAction } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { QuickOpenSelectNextAction, QuickOpenSelectPreviousAction, inQuickOpenContext, getQuickNavigateHandler, QuickOpenNavigateNextAction, QuickOpenNavigatePreviousAction, defaultQuickOpenContext, QUICKOPEN_ACTION_ID, QUICKOPEN_ACION_LABEL } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.closeQuickOpen',
@@ -21,6 +22,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: accessor => {
const quickOpenService = accessor.get(IQuickOpenService);
quickOpenService.close();
const quickInputService = accessor.get(IQuickInputService);
return quickInputService.cancel();
}
});
@@ -32,6 +35,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: accessor => {
const quickOpenService = accessor.get(IQuickOpenService);
quickOpenService.accept();
const quickInputService = accessor.get(IQuickInputService);
return quickInputService.accept();
}
});
@@ -43,10 +48,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: accessor => {
const quickOpenService = accessor.get(IQuickOpenService);
quickOpenService.focus();
const quickInputService = accessor.get(IQuickInputService);
quickInputService.focus();
}
});
const registry = <IWorkbenchActionRegistry>Registry.as(ActionExtensions.WorkbenchActions);
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: null } };

View File

@@ -6,7 +6,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';

View File

@@ -5,7 +5,7 @@
import 'vs/css!./media/sidebarpart';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { CompositePart } from 'vs/workbench/browser/parts/compositePart';
@@ -21,12 +21,14 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Event from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
import { Dimension } from 'vs/base/browser/builder';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, EventType } from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
export class SidebarPart extends CompositePart<Viewlet> {
@@ -75,11 +77,18 @@ export class SidebarPart extends CompositePart<Viewlet> {
return this._onDidCompositeClose.event as Event<IViewlet>;
}
public createTitleArea(parent: HTMLElement): HTMLElement {
const titleArea = super.createTitleArea(parent);
$(titleArea).on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)));
return titleArea;
}
public updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
const container = $(this.getContainer());
container.style('background-color', this.getColor(SIDE_BAR_BACKGROUND));
container.style('color', this.getColor(SIDE_BAR_FOREGROUND));
@@ -132,6 +141,23 @@ export class SidebarPart extends CompositePart<Viewlet> {
return super.layout(dimension);
}
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
const activeViewlet = this.getActiveViewlet() as Viewlet;
if (activeViewlet) {
const contextMenuActions = activeViewlet ? activeViewlet.getContextMenuActions() : [];
if (contextMenuActions.length) {
const anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(contextMenuActions),
getActionItem: action => this.actionItemProvider(action as Action),
actionRunner: activeViewlet.getActionRunner(),
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id)
});
}
}
}
}
class FocusSideBarAction extends Action {

View File

@@ -45,7 +45,7 @@
margin-left: 5px;
}
.monaco-workbench > .part.statusbar > .statusbar-item a:not([disabled]):not(.disabled) {
.monaco-workbench > .part.statusbar > .statusbar-item a {
cursor: pointer;
display: inline-block;
height: 100%;
@@ -57,7 +57,7 @@
}
.monaco-workbench > .part.statusbar > .statusbar-entry > span,
.monaco-workbench > .part.statusbar > .statusbar-entry > a:not([disabled]) {
.monaco-workbench > .part.statusbar > .statusbar-entry > a {
padding: 0 5px 0 5px;
white-space: pre; /* gives some degree of styling */
}
@@ -67,6 +67,6 @@
font-size: 14px;
}
.monaco-workbench > .part.statusbar > .statusbar-item a:hover:not([disabled]):not(.disabled) {
.monaco-workbench > .part.statusbar > .statusbar-item a:hover {
text-decoration: none;
}

View File

@@ -6,11 +6,11 @@
'use strict';
import 'vs/css!./media/statusbarpart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { TPromise } from 'vs/base/common/winjs.base';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Builder, $ } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { Registry } from 'vs/platform/registry/common/platform';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -39,7 +39,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
private static readonly PRIORITY_PROP = 'priority';
private static readonly ALIGNMENT_PROP = 'alignment';
private statusItemsContainer: Builder;
private statusItemsContainer: HTMLElement;
private statusMsgDispose: IDisposable;
private styleElement: HTMLStyleElement;
@@ -67,7 +67,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
const toDispose = item.render(el);
// Insert according to priority
const container = this.statusItemsContainer.getHTMLElement();
const container = this.statusItemsContainer;
const neighbours = this.getEntries(alignment);
let inserted = false;
for (let i = 0; i < neighbours.length; i++) {
@@ -101,7 +101,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
private getEntries(alignment: StatusbarAlignment): HTMLElement[] {
const entries: HTMLElement[] = [];
const container = this.statusItemsContainer.getHTMLElement();
const container = this.statusItemsContainer;
const children = container.children;
for (let i = 0; i < children.length; i++) {
const childElement = <HTMLElement>children.item(i);
@@ -113,8 +113,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
return entries;
}
public createContentArea(parent: Builder): Builder {
this.statusItemsContainer = $(parent);
public createContentArea(parent: HTMLElement): HTMLElement {
this.statusItemsContainer = parent;
// Fill in initial items that were contributed from the registry
const registry = Registry.as<IStatusbarRegistry>(Extensions.Statusbar);
@@ -129,7 +129,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
const el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority);
const dispose = item.render(el);
this.statusItemsContainer.append(el);
this.statusItemsContainer.appendChild(el);
return dispose;
}));
@@ -140,7 +140,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
protected updateStyles(): void {
super.updateStyles();
const container = this.getContainer();
const container = $(this.getContainer());
// Background colors
const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND);
@@ -334,12 +334,12 @@ class ManageExtensionAction extends Action {
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND);
if (statusBarItemHoverBackground) {
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:hover:not([disabled]):not(.disabled) { background-color: ${statusBarItemHoverBackground}; }`);
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`);
}
const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND);
if (statusBarItemActiveBackground) {
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:active:not([disabled]):not(.disabled) { background-color: ${statusBarItemActiveBackground}; }`);
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`);
}
const statusBarProminentItemBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND);
@@ -349,6 +349,6 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const statusBarProminentItemHoverBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND);
if (statusBarProminentItemHoverBackground) {
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a.status-bar-info:hover:not([disabled]):not(.disabled) { background-color: ${statusBarProminentItemHoverBackground}; }`);
collector.addRule(`.monaco-workbench > .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`);
}
});

View File

@@ -12,7 +12,7 @@
flex-shrink: 0;
align-items: center;
justify-content: center;
-webkit-user-select: none;
user-select: none;
-webkit-app-region: drag;
zoom: 1; /* prevent zooming */
line-height: 22px;

View File

@@ -7,8 +7,7 @@
import 'vs/css!./media/titlebarpart';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder, $, Dimension } from 'vs/base/browser/builder';
import * as DOM from 'vs/base/browser/dom';
import { Builder, $ } from 'vs/base/browser/builder';
import * as paths from 'vs/base/common/paths';
import { Part } from 'vs/workbench/browser/part';
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
@@ -22,7 +21,7 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import * as labels from 'vs/base/common/labels';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@@ -34,6 +33,7 @@ import { isMacintosh, isWindows } from 'vs/base/common/platform';
import URI from 'vs/base/common/uri';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { trim } from 'vs/base/common/strings';
import { addDisposableListener, EventType, EventHelper, Dimension } from 'vs/base/browser/dom';
export class TitlebarPart extends Part implements ITitleService {
@@ -86,8 +86,8 @@ export class TitlebarPart extends Part implements ITitleService {
}
private registerListeners(): void {
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.BLUR, () => this.onBlur()));
this.toUnbind.push(DOM.addDisposableListener(window, DOM.EventType.FOCUS, () => this.onFocus()));
this.toUnbind.push(addDisposableListener(window, EventType.BLUR, () => this.onBlur()));
this.toUnbind.push(addDisposableListener(window, EventType.FOCUS, () => this.onFocus()));
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.setTitle(this.getWindowTitle())));
@@ -226,7 +226,7 @@ export class TitlebarPart extends Part implements ITitleService {
});
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
this.titleContainer = $(parent);
// Title
@@ -236,16 +236,16 @@ export class TitlebarPart extends Part implements ITitleService {
}
// Maximize/Restore on doubleclick
this.titleContainer.on(DOM.EventType.DBLCLICK, (e) => {
DOM.EventHelper.stop(e);
this.titleContainer.on(EventType.DBLCLICK, (e) => {
EventHelper.stop(e);
this.onTitleDoubleclick();
});
// Context menu on title
this.title.on([DOM.EventType.CONTEXT_MENU, DOM.EventType.MOUSE_DOWN], (e: MouseEvent) => {
if (e.type === DOM.EventType.CONTEXT_MENU || e.metaKey) {
DOM.EventHelper.stop(e);
this.title.on([EventType.CONTEXT_MENU, EventType.MOUSE_DOWN], (e: MouseEvent) => {
if (e.type === EventType.CONTEXT_MENU || e.metaKey) {
EventHelper.stop(e);
this.onContextMenu(e);
}
@@ -253,7 +253,7 @@ export class TitlebarPart extends Part implements ITitleService {
// Since the title area is used to drag the window, we do not want to steal focus from the
// currently active element. So we restore focus after a timeout back to where it was.
this.titleContainer.on([DOM.EventType.MOUSE_DOWN], () => {
this.titleContainer.on([EventType.MOUSE_DOWN], () => {
const active = document.activeElement;
setTimeout(() => {
if (active instanceof HTMLElement) {
@@ -262,20 +262,19 @@ export class TitlebarPart extends Part implements ITitleService {
}, 0 /* need a timeout because we are in capture phase */);
}, void 0, true /* use capture to know the currently active element properly */);
return this.titleContainer;
return this.titleContainer.getHTMLElement();
}
protected updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
if (container) {
container.style('color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND));
container.style('background-color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND));
if (this.titleContainer) {
this.titleContainer.style('color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND));
this.titleContainer.style('background-color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND));
const titleBorder = this.getColor(TITLE_BAR_BORDER);
container.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
this.titleContainer.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
}
}

View File

@@ -0,0 +1,446 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ViewsRegistry, IViewDescriptor, ViewLocation } from 'vs/workbench/common/views';
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
import { Event, chain, filterEvent, Emitter } from 'vs/base/common/event';
import { sortedDiff, firstIndex, move } from 'vs/base/common/arrays';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
function filterViewEvent(location: ViewLocation, event: Event<IViewDescriptor[]>): Event<IViewDescriptor[]> {
return chain(event)
.map(views => views.filter(view => view.location === location))
.filter(views => views.length > 0)
.event;
}
class CounterSet<T> implements IReadableSet<T> {
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
}
export interface IViewItem {
viewDescriptor: IViewDescriptor;
active: boolean;
}
class ViewDescriptorCollection {
private contextKeys = new CounterSet<string>();
private items: IViewItem[] = [];
private disposables: IDisposable[] = [];
private _onDidChange = new Emitter<void>();
readonly onDidChange: Event<void> = this._onDidChange.event;
get viewDescriptors(): IViewDescriptor[] {
return this.items
.filter(i => i.active)
.map(i => i.viewDescriptor);
}
constructor(
location: ViewLocation,
@IContextKeyService private contextKeyService: IContextKeyService
) {
const onRelevantViewsRegistered = filterViewEvent(location, ViewsRegistry.onViewsRegistered);
onRelevantViewsRegistered(this.onViewsRegistered, this, this.disposables);
const onRelevantViewsDeregistered = filterViewEvent(location, ViewsRegistry.onViewsDeregistered);
onRelevantViewsDeregistered(this.onViewsDeregistered, this, this.disposables);
const onRelevantContextChange = filterEvent(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
onRelevantContextChange(this.onContextChanged, this);
this.onViewsRegistered(ViewsRegistry.getViews(location));
}
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): any {
let fireChangeEvent = false;
for (const viewDescriptor of viewDescriptors) {
const item = {
viewDescriptor,
active: this.isViewDescriptorActive(viewDescriptor) // TODO: should read from some state?
};
this.items.push(item);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.add(key);
}
}
if (item.active) {
fireChangeEvent = true;
}
}
if (fireChangeEvent) {
this._onDidChange.fire();
}
}
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): any {
let fireChangeEvent = false;
for (const viewDescriptor of viewDescriptors) {
const index = firstIndex(this.items, i => i.viewDescriptor.id === viewDescriptor.id);
if (index === -1) {
continue;
}
const item = this.items[index];
this.items.splice(index, 1);
if (viewDescriptor.when) {
for (const key of viewDescriptor.when.keys()) {
this.contextKeys.delete(key);
}
}
if (item.active) {
fireChangeEvent = true;
}
}
if (fireChangeEvent) {
this._onDidChange.fire();
}
}
private onContextChanged(event: IContextKeyChangeEvent): any {
let fireChangeEvent = false;
for (const item of this.items) {
const active = this.isViewDescriptorActive(item.viewDescriptor);
if (item.active !== active) {
fireChangeEvent = true;
}
item.active = active;
}
if (fireChangeEvent) {
this._onDidChange.fire();
}
}
private isViewDescriptorActive(viewDescriptor: IViewDescriptor): boolean {
return !viewDescriptor.when || this.contextKeyService.contextMatchesRules(viewDescriptor.when);
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export interface IView {
viewDescriptor: IViewDescriptor;
visible: boolean;
}
export interface IViewState {
visible: boolean;
collapsed: boolean;
order?: number;
size?: number;
}
export interface IViewDescriptorRef {
viewDescriptor: IViewDescriptor;
index: number;
}
export interface IAddedViewDescriptorRef extends IViewDescriptorRef {
collapsed: boolean;
size?: number;
}
export class ContributableViewsModel {
readonly viewDescriptors: IViewDescriptor[] = [];
get visibleViewDescriptors(): IViewDescriptor[] {
return this.viewDescriptors.filter(v => this.viewStates.get(v.id).visible);
}
private _onDidAdd = new Emitter<IAddedViewDescriptorRef>();
readonly onDidAdd: Event<IAddedViewDescriptorRef> = this._onDidAdd.event;
private _onDidRemove = new Emitter<IViewDescriptorRef>();
readonly onDidRemove: Event<IViewDescriptorRef> = this._onDidRemove.event;
private _onDidMove = new Emitter<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }>();
readonly onDidMove: Event<{ from: IViewDescriptorRef; to: IViewDescriptorRef; }> = this._onDidMove.event;
private disposables: IDisposable[] = [];
constructor(
location: ViewLocation,
@IContextKeyService contextKeyService: IContextKeyService,
protected viewStates = new Map<string, IViewState>()
) {
const viewDescriptorCollection = new ViewDescriptorCollection(location, contextKeyService);
this.disposables.push(viewDescriptorCollection);
viewDescriptorCollection.onDidChange(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.viewDescriptors), this, this.disposables);
this.onDidChangeViewDescriptors(viewDescriptorCollection.viewDescriptors);
}
isVisible(id: string): boolean {
const state = this.viewStates.get(id);
if (!state) {
throw new Error(`Unknown view ${id}`);
}
return state.visible;
}
setVisible(id: string, visible: boolean): void {
const { visibleIndex, viewDescriptor, state } = this.find(id);
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
}
if (state.visible === visible) {
return;
}
state.visible = visible;
if (visible) {
this._onDidAdd.fire({ index: visibleIndex, viewDescriptor, size: state.size, collapsed: state.collapsed });
} else {
this._onDidRemove.fire({ index: visibleIndex, viewDescriptor });
}
}
isCollapsed(id: string): boolean {
const state = this.viewStates.get(id);
if (!state) {
throw new Error(`Unknown view ${id}`);
}
return state.collapsed;
}
setCollapsed(id: string, collapsed: boolean): void {
const { state } = this.find(id);
state.collapsed = collapsed;
}
getSize(id: string): number | undefined {
const state = this.viewStates.get(id);
if (!state) {
throw new Error(`Unknown view ${id}`);
}
return state.size;
}
setSize(id: string, size: number): void {
const { state } = this.find(id);
state.size = size;
}
move(from: string, to: string): void {
const fromIndex = firstIndex(this.viewDescriptors, v => v.id === from);
const toIndex = firstIndex(this.viewDescriptors, v => v.id === to);
const fromViewDescriptor = this.viewDescriptors[fromIndex];
const toViewDescriptor = this.viewDescriptors[toIndex];
move(this.viewDescriptors, fromIndex, toIndex);
for (let index = 0; index < this.viewDescriptors.length; index++) {
const state = this.viewStates.get(this.viewDescriptors[index].id);
state.order = index;
}
this._onDidMove.fire({
from: { index: fromIndex, viewDescriptor: fromViewDescriptor },
to: { index: toIndex, viewDescriptor: toViewDescriptor }
});
}
private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState } {
for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) {
const viewDescriptor = this.viewDescriptors[i];
const state = this.viewStates.get(viewDescriptor.id);
if (viewDescriptor.id === id) {
return { index: i, visibleIndex, viewDescriptor, state };
}
if (state.visible) {
visibleIndex++;
}
}
throw new Error(`view descriptor ${id} not found`);
}
private compareViewDescriptors(a: IViewDescriptor, b: IViewDescriptor): number {
const viewStateA = this.viewStates.get(a.id);
const viewStateB = this.viewStates.get(b.id);
let orderA = viewStateA && viewStateA.order;
orderA = typeof orderA === 'number' ? orderA : a.order;
orderA = typeof orderA === 'number' ? orderA : Number.POSITIVE_INFINITY;
let orderB = viewStateB && viewStateB.order;
orderB = typeof orderB === 'number' ? orderB : b.order;
orderB = typeof orderB === 'number' ? orderB : Number.POSITIVE_INFINITY;
if (orderA !== orderB) {
return orderA - orderB;
}
if (a.id === b.id) {
return 0;
}
return a.id < b.id ? -1 : 1;
}
private onDidChangeViewDescriptors(viewDescriptors: IViewDescriptor[]): void {
const ids = new Set<string>();
for (const viewDescriptor of this.viewDescriptors) {
ids.add(viewDescriptor.id);
}
viewDescriptors = viewDescriptors.sort(this.compareViewDescriptors.bind(this));
for (const viewDescriptor of viewDescriptors) {
if (!this.viewStates.has(viewDescriptor.id)) {
this.viewStates.set(viewDescriptor.id, {
visible: true,
collapsed: false
});
}
}
const splices = sortedDiff<IViewDescriptor>(
this.viewDescriptors,
viewDescriptors,
this.compareViewDescriptors.bind(this)
).reverse();
for (const splice of splices) {
const startViewDescriptor = this.viewDescriptors[splice.start];
let startIndex = startViewDescriptor ? this.find(startViewDescriptor.id).visibleIndex : this.viewDescriptors.length;
for (let i = 0; i < splice.deleteCount; i++) {
const viewDescriptor = this.viewDescriptors[splice.start + i];
const { state } = this.find(viewDescriptor.id);
if (state.visible) {
this._onDidRemove.fire({ index: startIndex, viewDescriptor });
}
}
for (let i = 0; i < splice.toInsert.length; i++) {
const viewDescriptor = splice.toInsert[i];
const state = this.viewStates.get(viewDescriptor.id);
if (state.visible) {
this._onDidAdd.fire({ index: startIndex++, viewDescriptor, size: state.size, collapsed: state.collapsed });
}
}
}
this.viewDescriptors.splice(0, this.viewDescriptors.length, ...viewDescriptors);
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
interface ISerializedViewState {
id: string;
state: IViewState;
}
export class PersistentContributableViewsModel extends ContributableViewsModel {
private viewletStateStorageId: string;
private storageService: IStorageService;
private contextService: IWorkspaceContextService;
constructor(
location: ViewLocation,
viewletStateStorageId: string,
@IContextKeyService contextKeyService: IContextKeyService,
@IStorageService storageService: IStorageService,
@IWorkspaceContextService contextService: IWorkspaceContextService
) {
const scope = contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL;
const raw = storageService.get(viewletStateStorageId, scope, '[]');
const serializedViewsStates = JSON.parse(raw) as ISerializedViewState[];
const viewStates = new Map<string, IViewState>();
for (const { id, state } of serializedViewsStates) {
viewStates.set(id, state);
}
super(location, contextKeyService, viewStates);
this.viewletStateStorageId = viewletStateStorageId;
this.storageService = storageService;
this.contextService = contextService;
}
saveViewsStates(): void {
const serializedViewStates: ISerializedViewState[] = [];
this.viewStates.forEach((state, id) => serializedViewStates.push({ id, state }));
const raw = JSON.stringify(serializedViewStates);
const scope = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? StorageScope.WORKSPACE : StorageScope.GLOBAL;
this.storageService.store(this.viewletStateStorageId, raw, scope);
}
dispose(): void {
this.saveViewsStates();
super.dispose();
}
}

View File

@@ -9,10 +9,9 @@ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
import * as DOM from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import { LIGHT, FileThemeIcon, FolderThemeIcon } from 'vs/platform/theme/common/themeService';
import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { TreeItemCollapsibleState, ITreeItem, ITreeViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, IViewDescriptor, TreeViewItemHandleArg, ICustomViewDescriptor, IViewsViewlet } from 'vs/workbench/common/views';
import { TreeItemCollapsibleState, ITreeItem, ITreeViewer, IViewsService, ITreeViewDataProvider, ViewsRegistry, IViewDescriptor, TreeViewItemHandleArg, ICustomViewDescriptor, IViewsViewlet, ViewLocation } from 'vs/workbench/common/views';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
@@ -34,7 +33,7 @@ import { FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { isUndefinedOrNull } from 'vs/base/common/types';
export class CustomViewsService extends Disposable implements ICustomViewsService {
export class CustomViewsService extends Disposable implements IViewsService {
_serviceBrand: any;
@@ -57,12 +56,16 @@ export class CustomViewsService extends Disposable implements ICustomViewsServic
openView(id: string, focus: boolean): TPromise<void> {
const viewDescriptor = ViewsRegistry.getView(id);
if (viewDescriptor) {
return this.viewletService.openViewlet(viewDescriptor.id)
.then((viewlet: IViewsViewlet) => {
if (viewlet && viewlet.openView) {
viewlet.openView(id, focus);
}
});
const viewletId = viewDescriptor.location === ViewLocation.SCM ? 'workbench.view.scm' : viewDescriptor.location.id;
const viewletDescriptor = this.viewletService.getViewlet(viewletId);
if (viewletDescriptor) {
return this.viewletService.openViewlet(viewletDescriptor.id)
.then((viewlet: IViewsViewlet) => {
if (viewlet && viewlet.openView) {
viewlet.openView(id, focus);
}
});
}
}
return TPromise.as(null);
}
@@ -70,7 +73,7 @@ export class CustomViewsService extends Disposable implements ICustomViewsServic
private createViewers(viewDescriptors: IViewDescriptor[]): void {
for (const viewDescriptor of viewDescriptors) {
if ((<ICustomViewDescriptor>viewDescriptor).treeView) {
this.viewers.set(viewDescriptor.id, this.instantiationService.createInstance(CustomTreeViewer, viewDescriptor.id));
this.viewers.set(viewDescriptor.id, this.instantiationService.createInstance(CustomTreeViewer, viewDescriptor.id, viewDescriptor.location));
}
}
}
@@ -111,6 +114,7 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
constructor(
private id: string,
private location: ViewLocation,
@IExtensionService private extensionService: IExtensionService,
@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
@IInstantiationService private instantiationService: IInstantiationService,
@@ -133,7 +137,7 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
onDidChange = dataProvider.onDidChange;
onDispose = dataProvider.onDispose;
getChildren(node?: ITreeItem): TPromise<ITreeItem[]> {
if (node.children) {
if (node && node.children) {
return TPromise.as(node.children);
}
const promise = node instanceof Root ? dataProvider.getChildren() : dataProvider.getChildren(node);
@@ -171,9 +175,9 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
if (this.tree) {
if (this.isVisible) {
$(this.tree.getHTMLElement()).show();
DOM.show(this.tree.getHTMLElement());
} else {
$(this.tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
DOM.hide(this.tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it
}
if (this.isVisible) {
@@ -198,7 +202,7 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
}
// Pass Focus to Viewer
this.tree.DOMFocus();
this.tree.domFocus();
}
}
@@ -213,7 +217,7 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
this.treeContainer = DOM.$('.tree-explorer-viewlet-tree-view');
const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined;
const menus = this.instantiationService.createInstance(Menus, this.id);
const dataSource = this.instantiationService.createInstance(TreeDataSource, this);
const dataSource = this.instantiationService.createInstance(TreeDataSource, this, this.getProgressLocation());
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, actionItemProvider);
const controller = this.instantiationService.createInstance(TreeController, this.id, menus);
this.tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {});
@@ -223,6 +227,18 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
this.tree.setInput(this.root);
}
private getProgressLocation(): ProgressLocation {
switch (this.location.id) {
case ViewLocation.Explorer.id:
return ProgressLocation.Explorer;
case ViewLocation.SCM.id:
return ProgressLocation.Scm;
case ViewLocation.Debug.id:
return null /* No debug progress location yet */;
}
return null;
}
layout(size: number) {
if (this.tree) {
this.treeContainer.style.height = size + 'px';
@@ -265,7 +281,7 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
return result.then(() => this.tree.reveal(item))
.then(() => {
if (select) {
this.tree.setSelection([item]);
this.tree.setSelection([item], { source: 'api' });
}
});
}
@@ -287,6 +303,9 @@ class CustomTreeViewer extends Disposable implements ITreeViewer {
}
private onSelection({ payload }: any): void {
if (payload && payload.source === 'api') {
return;
}
const selection: ITreeItem = this.tree.getSelection()[0];
if (selection) {
if (selection.command) {
@@ -306,6 +325,7 @@ class TreeDataSource implements IDataSource {
constructor(
private treeView: ITreeViewer,
private location: ProgressLocation,
@IProgressService2 private progressService: IProgressService2
) {
}
@@ -320,7 +340,7 @@ class TreeDataSource implements IDataSource {
public getChildren(tree: ITree, node: ITreeItem): TPromise<any[]> {
if (this.treeView.dataProvider) {
return this.progressService.withProgress({ location: ProgressLocation.Explorer }, () => this.treeView.dataProvider.getChildren(node));
return this.location ? this.progressService.withProgress({ location: this.location }, () => this.treeView.dataProvider.getChildren(node)) : this.treeView.dataProvider.getChildren(node);
}
return TPromise.as([]);
}
@@ -406,7 +426,7 @@ class TreeRenderer implements IRenderer {
templateData.actionBar.context = (<TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle });
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
templateData.aligner.align(node);
templateData.aligner.treeItem = node;
}
private getFileKind(node: ITreeItem): FileKind {
@@ -430,7 +450,7 @@ class TreeRenderer implements IRenderer {
class Aligner extends Disposable {
private node: ITreeItem;
private _treeItem: ITreeItem;
constructor(
private container: HTMLElement,
@@ -438,65 +458,47 @@ class Aligner extends Disposable {
private themeService: IWorkbenchThemeService
) {
super();
this._register(this.themeService.onDidFileIconThemeChange(() => this.alignByTheme()));
this._register(this.themeService.onDidFileIconThemeChange(() => this.render()));
}
align(treeItem: ITreeItem): void {
this.node = treeItem;
this.alignByTheme();
set treeItem(treeItem: ITreeItem) {
this._treeItem = treeItem;
this.render();
}
private alignByTheme(): void {
if (this.node) {
DOM.toggleClass(this.container, 'align-with-twisty', this.hasToAlignWithTwisty());
private render(): void {
if (this._treeItem) {
DOM.toggleClass(this.container, 'align-icon-with-twisty', this.hasToAlignIconWithTwisty());
}
}
private hasToAlignWithTwisty(): boolean {
if (this.hasParentHasIcon()) {
private hasToAlignIconWithTwisty(): boolean {
if (this._treeItem.collapsibleState !== TreeItemCollapsibleState.None) {
return false;
}
if (!this.hasIcon(this._treeItem)) {
return false;
const fileIconTheme = this.themeService.getFileIconTheme();
if (!(fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons)) {
}
const parent: ITreeItem = this.tree.getNavigator(this._treeItem).parent() || this.tree.getInput();
if (this.hasIcon(parent)) {
return false;
}
if (this.node.collapsibleState !== TreeItemCollapsibleState.None) {
return false;
}
const icon = this.themeService.getTheme().type === LIGHT ? this.node.icon : this.node.iconDark;
const hasIcon = !!icon || !!this.node.resourceUri;
if (!hasIcon) {
return false;
}
const siblingsWithChildren = this.getSiblings().filter(s => s.collapsibleState !== TreeItemCollapsibleState.None);
for (const s of siblingsWithChildren) {
const icon = this.themeService.getTheme().type === LIGHT ? s.icon : s.iconDark;
if (icon) {
return false;
}
}
return true;
return parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
}
private getSiblings(): ITreeItem[] {
const parent: ITreeItem = this.tree.getNavigator(this.node).parent() || this.tree.getInput();
return parent.children;
}
private hasParentHasIcon(): boolean {
const parent = this.tree.getNavigator(this.node).parent() || this.tree.getInput();
const icon = this.themeService.getTheme().type === LIGHT ? parent.icon : parent.iconDark;
private hasIcon(node: ITreeItem): boolean {
const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark;
if (icon) {
return true;
}
if (parent.resourceUri) {
if (node.resourceUri || node.themeIcon) {
const fileIconTheme = this.themeService.getFileIconTheme();
if (fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons) {
return true;
const isFolder = node.themeIcon ? node.themeIcon.id === FolderThemeIcon.id : node.collapsibleState !== TreeItemCollapsibleState.None;
if (isFolder) {
return fileIconTheme.hasFileIcons && fileIconTheme.hasFolderIcons;
}
return fileIconTheme.hasFileIcons;
}
return false;
}
@@ -541,7 +543,7 @@ class TreeController extends WorkbenchTreeController {
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
tree.DOMFocus();
tree.domFocus();
}
},

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/views';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -14,7 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { fillInActions, ContextAwareMenuItemActionItem } from 'vs/platform/actions/browser/menuItemActionItem';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ICustomViewsService, ITreeViewer } from 'vs/workbench/common/views';
import { IViewsService, ITreeViewer } from 'vs/workbench/common/views';
import { IViewletViewOptions, IViewOptions, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -31,10 +31,10 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
@IContextMenuService contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@ICustomViewsService customViewsService: ICustomViewsService,
@IViewsService viewsService: IViewsService,
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService, configurationService);
this.treeViewer = customViewsService.getTreeViewer(this.id);
this.treeViewer = viewsService.getTreeViewer(this.id);
this.disposables.push(toDisposable(() => this.treeViewer.setVisibility(false)));
this.menus = this.instantiationService.createInstance(Menus, this.id);
this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables);

View File

@@ -50,8 +50,11 @@
display: none;
}
.tree-explorer-viewlet-tree-view.file-icon-themable-tree.align-icons-and-twisties .monaco-tree-row:not(.has-children) .content:not(.align-with-twisty)::before,
.tree-explorer-viewlet-tree-view.file-icon-themable-tree.hide-arrows .monaco-tree-row .content::before {
.tree-explorer-viewlet-tree-view.file-icon-themable-tree .monaco-tree-row .content.align-icon-with-twisty::before {
display: none;
}
.tree-explorer-viewlet-tree-view.file-icon-themable-tree .monaco-tree-row .content:not(.align-icon-with-twisty)::before {
display: inline-block;
}

View File

@@ -6,12 +6,11 @@
import 'vs/css!./media/panelviewlet';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter, filterEvent } from 'vs/base/common/event';
import { Event, Emitter, filterEvent } from 'vs/base/common/event';
import { ColorIdentifier, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler, IColorMapping, IThemable } from 'vs/platform/theme/common/styler';
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { append, $, trackFocus, toggleClass, EventType, isAncestor } from 'vs/base/browser/dom';
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { firstIndex } from 'vs/base/common/arrays';
import { IAction, IActionRunner } from 'vs/base/common/actions';
@@ -36,15 +35,6 @@ export interface IPanelColors extends IColorMapping {
headerHighContrastBorder?: ColorIdentifier;
}
export function attachPanelStyler(widget: IThemable, themeService: IThemeService) {
return attachStyler<IPanelColors>(themeService, {
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
headerHighContrastBorder: contrastBorder,
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
}, widget);
}
export interface IViewletPanelOptions extends IPanelOptions {
actionRunner?: IActionRunner;
}
@@ -56,6 +46,9 @@ export abstract class ViewletPanel extends Panel {
private _onDidFocus = new Emitter<void>();
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private _onDidChangeTitleArea = new Emitter<void>();
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
protected actionRunner: IActionRunner;
protected toolbar: ToolBar;
private headerContainer: HTMLElement;
@@ -95,7 +88,7 @@ export abstract class ViewletPanel extends Panel {
});
this.disposables.push(this.toolbar);
this.updateActions();
this.setActions();
const onDidRelevantConfigurationChange = filterEvent(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPanel.AlwaysShowActionsConfig));
onDidRelevantConfigurationChange(this.updateActionsVisibility, this, this.disposables);
@@ -110,16 +103,21 @@ export abstract class ViewletPanel extends Panel {
this._onDidFocus.fire();
}
protected updateActions(): void {
private setActions(): void {
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
this.toolbar.context = this.getActionsContext();
}
protected updateActionsVisibility(): void {
private updateActionsVisibility(): void {
const shouldAlwaysShowActions = this.configurationService.getValue<boolean>('workbench.view.alwaysShowHeaderActions');
toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions);
}
protected updateActions(): void {
this.setActions();
this._onDidChangeTitleArea.fire();
}
getActions(): IAction[] {
return [];
}
@@ -160,6 +158,10 @@ export class PanelViewlet extends Viewlet {
return this.panelview.onDidSashChange;
}
protected get panels(): ViewletPanel[] {
return this.panelItems.map(i => i.panel);
}
protected get length(): number {
return this.panelItems.length;
}
@@ -175,13 +177,12 @@ export class PanelViewlet extends Viewlet {
super(id, partService, telemetryService, themeService);
}
async create(parent: Builder): TPromise<void> {
async create(parent: HTMLElement): TPromise<void> {
super.create(parent);
const container = parent.getHTMLElement();
this.panelview = this._register(new PanelView(container, this.options));
this.panelview = this._register(new PanelView(parent, this.options));
this._register(this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel)));
this._register(parent.on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
}
private showContextMenu(event: StandardMouseEvent): void {
@@ -228,6 +229,14 @@ export class PanelViewlet extends Viewlet {
return [];
}
getActionItem(action: IAction): IActionItem {
if (this.isSingleView()) {
return this.panelItems[0].panel.getActionItem(action);
}
return super.getActionItem(action);
}
focus(): void {
super.focus();
@@ -254,26 +263,58 @@ export class PanelViewlet extends Viewlet {
return Math.max(...sizes);
}
addPanel(panel: ViewletPanel, size: number, index = this.panelItems.length - 1): void {
addPanels(panels: { panel: ViewletPanel, size: number, index?: number }[]): void {
const wasSingleView = this.isSingleView();
for (const { panel, size, index } of panels) {
this.addPanel(panel, size, index);
}
this.updateViewHeaders();
if (this.isSingleView() !== wasSingleView) {
this.updateTitleArea();
}
}
private addPanel(panel: ViewletPanel, size: number, index = this.panelItems.length - 1): void {
const disposables: IDisposable[] = [];
const onDidFocus = panel.onDidFocus(() => this.lastFocusedPanel = panel, null, disposables);
const onDidChangeTitleArea = panel.onDidChangeTitleArea(() => {
if (this.isSingleView()) {
this.updateTitleArea();
}
}, null, disposables);
const onDidChange = panel.onDidChange(() => {
if (panel === this.lastFocusedPanel && !panel.isExpanded()) {
this.lastFocusedPanel = undefined;
}
}, null, disposables);
const styler = attachPanelStyler(panel, this.themeService);
const disposable = combinedDisposable([onDidFocus, styler, onDidChange]);
const panelStyler = attachStyler<IPanelColors>(this.themeService, {
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
headerHighContrastBorder: index === 0 ? null : contrastBorder,
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
}, panel);
const disposable = combinedDisposable([onDidFocus, onDidChangeTitleArea, panelStyler, onDidChange]);
const panelItem: IViewletPanelItem = { panel, disposable };
this.panelItems.splice(index, 0, panelItem);
this.panelview.addPanel(panel, size, index);
this.updateViewHeaders();
this.updateTitleArea();
}
removePanel(panel: ViewletPanel): void {
removePanels(panels: ViewletPanel[]): void {
const wasSingleView = this.isSingleView();
panels.forEach(panel => this.removePanel(panel));
this.updateViewHeaders();
if (wasSingleView !== this.isSingleView()) {
this.updateTitleArea();
}
}
private removePanel(panel: ViewletPanel): void {
const index = firstIndex(this.panelItems, i => i.panel === panel);
if (index === -1) {
@@ -288,8 +329,6 @@ export class PanelViewlet extends Viewlet {
const [panelItem] = this.panelItems.splice(index, 1);
panelItem.disposable.dispose();
this.updateViewHeaders();
this.updateTitleArea();
}
movePanel(from: ViewletPanel, to: ViewletPanel): void {
@@ -336,4 +375,4 @@ export class PanelViewlet extends Viewlet {
this.panelItems.forEach(i => i.disposable.dispose());
this.panelview.dispose();
}
}
}

View File

@@ -6,7 +6,6 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import * as DOM from 'vs/base/browser/dom';
import { $, Dimension, Builder } from 'vs/base/browser/builder';
import { Scope } from 'vs/workbench/common/memento';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IAction, IActionRunner } from 'vs/base/common/actions';
@@ -24,11 +23,11 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/
import { IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { PanelViewlet, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IPanelOptions } from 'vs/base/browser/ui/splitview/panelview';
import { IPanelOptions, DefaultPanelDndController } from 'vs/base/browser/ui/splitview/panelview';
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { localize } from 'vs/nls';
@@ -137,9 +136,9 @@ export abstract class TreeViewsViewletPanel extends ViewsViewletPanel {
}
if (isVisible) {
$(tree.getHTMLElement()).show();
DOM.show(tree.getHTMLElement());
} else {
$(tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
DOM.hide(tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it
}
if (isVisible) {
@@ -161,7 +160,7 @@ export abstract class TreeViewsViewletPanel extends ViewsViewletPanel {
}
// Pass Focus to Viewer
this.tree.DOMFocus();
this.tree.domFocus();
}
dispose(): void {
@@ -190,11 +189,11 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
private readonly viewsContextKeys: Set<string> = new Set<string>();
private viewsViewletPanels: ViewsViewletPanel[] = [];
private didLayout = false;
private dimension: Dimension;
private dimension: DOM.Dimension;
protected viewsStates: Map<string, IViewState> = new Map<string, IViewState>();
private areExtensionsReady: boolean = false;
private _onDidChangeViewVisibilityState: Emitter<string> = new Emitter<string>();
private readonly _onDidChangeViewVisibilityState: Emitter<string> = new Emitter<string>();
readonly onDidChangeViewVisibilityState: Event<string> = this._onDidChangeViewVisibilityState.event;
constructor(
@@ -210,12 +209,12 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
@IContextMenuService protected contextMenuService: IContextMenuService,
@IExtensionService protected extensionService: IExtensionService
) {
super(id, { showHeaderInTitleWhenSingleView, dnd: true }, partService, contextMenuService, telemetryService, themeService);
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, partService, contextMenuService, telemetryService, themeService);
this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
}
async create(parent: Builder): TPromise<void> {
async create(parent: HTMLElement): TPromise<void> {
await super.create(parent);
this._register(this.onDidSashChange(() => this.snapshotViewsStates()));
@@ -276,7 +275,7 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
}
}
layout(dimension: Dimension): void {
layout(dimension: DOM.Dimension): void {
super.layout(dimension);
this.dimension = dimension;
if (this.didLayout) {
@@ -371,14 +370,15 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
this.snapshotViewsStates();
if (toRemove.length) {
for (const viewDescriptor of toRemove) {
let view = this.getView(viewDescriptor.id);
this.removePanel(view);
this.viewsViewletPanels.splice(this.viewsViewletPanels.indexOf(view), 1);
view.dispose();
const panelsToRemove: ViewsViewletPanel[] = toRemove.map(viewDescriptor => this.getView(viewDescriptor.id));
this.removePanels(panelsToRemove);
for (const panel of panelsToRemove) {
this.viewsViewletPanels.splice(this.viewsViewletPanels.indexOf(panel), 1);
panel.dispose();
}
}
const panelsToAdd: { panel: ViewsViewletPanel, size: number, index: number }[] = [];
for (const viewDescriptor of toAdd) {
let viewState = this.viewsStates.get(viewDescriptor.id);
let index = visible.indexOf(viewDescriptor);
@@ -393,8 +393,13 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
toCreate.push(view);
const size = (viewState && viewState.size) || 200;
this.addPanel(view, size, index);
this.viewsViewletPanels.splice(index, 0, view);
panelsToAdd.push({ panel: view, size, index });
}
this.addPanels(panelsToAdd);
for (const { panel, index } of panelsToAdd) {
this.viewsViewletPanels.splice(index, 0, panel);
}
return TPromise.join(toCreate.map(view => view.create()))
@@ -545,19 +550,17 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
return false;
}
if (ViewLocation.getContributedViewLocation(this.location.id)) {
let visibleViewsCount = 0;
if (this.areExtensionsReady) {
visibleViewsCount = this.getViewDescriptorsFromRegistry().reduce((visibleViewsCount, v) => visibleViewsCount + (this.canBeVisible(v) ? 1 : 0), 0);
} else {
if (ViewLocation.get(this.location.id)) {
if (!this.areExtensionsReady) {
let visibleViewsCount = 0;
// Check in cache so that view do not jump. See #29609
this.viewsStates.forEach((viewState, id) => {
if (!viewState.isHidden) {
visibleViewsCount++;
}
});
return visibleViewsCount === 1;
}
return visibleViewsCount === 1;
}
return super.isSingleView();
@@ -603,18 +606,17 @@ export class ViewsViewlet extends PanelViewlet implements IViewsViewlet {
}
const collapsed = !view.isExpanded();
const order = this.viewsViewletPanels.indexOf(view);
const panelSize = this.getPanelSize(view);
// Do not save order because views can come late.
if (currentState) {
currentState.collapsed = collapsed;
currentState.size = collapsed ? currentState.size : panelSize;
currentState.order = order;
} else {
this.viewsStates.set(view.id, {
collapsed,
size: this.didLayout ? panelSize : void 0,
isHidden: false,
order,
order: void 0
});
}
}
@@ -645,7 +647,7 @@ export class PersistentViewsViewlet extends ViewsViewlet {
this._register(this.onDidChangeViewVisibilityState(id => this.onViewVisibilityChanged(id)));
}
create(parent: Builder): TPromise<void> {
create(parent: HTMLElement): TPromise<void> {
this.loadViewsStates();
return super.create(parent);
}

View File

@@ -4,13 +4,13 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as objects from 'vs/base/common/objects';
import arrays = require('vs/base/common/arrays');
import strings = require('vs/base/common/strings');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as arrays from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen';

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import DOM = require('vs/base/browser/dom');
import * as DOM from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { Action, IAction } from 'vs/base/common/actions';
import { ITree } from 'vs/base/parts/tree/browser/tree';
@@ -154,7 +154,7 @@ export class ToggleViewletAction extends Action {
const activeViewlet = this.viewletService.getActiveViewlet();
const activeElement = document.activeElement;
return activeViewlet && activeElement && DOM.isAncestor(activeElement, (<Viewlet>activeViewlet).getContainer().getHTMLElement());
return activeViewlet && activeElement && DOM.isAncestor(activeElement, (<Viewlet>activeViewlet).getContainer());
}
}
@@ -170,7 +170,7 @@ export class CollapseAction extends Action {
viewer.collapseAll();
viewer.clearSelection();
viewer.clearFocus();
viewer.DOMFocus();
viewer.domFocus();
viewer.focusFirst();
return TPromise.as(null);