mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-29 01:25:37 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 */);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
120
src/vs/workbench/browser/parts/quickinput/quickInput.css
Normal file
120
src/vs/workbench/browser/parts/quickinput/quickInput.css
Normal 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;
|
||||
}
|
||||
526
src/vs/workbench/browser/parts/quickinput/quickInput.ts
Normal file
526
src/vs/workbench/browser/parts/quickinput/quickInput.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/vs/workbench/browser/parts/quickinput/quickInputBox.ts
Normal file
107
src/vs/workbench/browser/parts/quickinput/quickInputBox.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 } };
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
446
src/vs/workbench/browser/parts/views/contributableViews.ts
Normal file
446
src/vs/workbench/browser/parts/views/contributableViews.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user