From 619c7e86124e8f7ca09d21b84ce31aa9940e7ad3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 30 Apr 2018 11:50:13 +0200 Subject: [PATCH] grid - first cut empty editor groups --- .../editor2/media/nextEditorGroupView.css | 11 ++ .../parts/editor2/media/nextTitleControl.css | 2 +- .../parts/editor2/nextEditorGroupView.ts | 120 ++++++++++-------- .../browser/parts/editor2/nextEditorPart.ts | 12 +- .../browser/parts/editor2/nextTitleControl.ts | 5 +- .../common/editor/editorStacksModel.ts | 62 ++++----- 6 files changed, 114 insertions(+), 98 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor2/media/nextEditorGroupView.css b/src/vs/workbench/browser/parts/editor2/media/nextEditorGroupView.css index dbe7997b760..882bba76356 100644 --- a/src/vs/workbench/browser/parts/editor2/media/nextEditorGroupView.css +++ b/src/vs/workbench/browser/parts/editor2/media/nextEditorGroupView.css @@ -7,6 +7,12 @@ height: 100%; } +.monaco-workbench > .part.editor > .content .editor-group-container.empty.active { + outline-width: 1px; + outline-style: solid; + outline-offset: -2px; +} + .monaco-workbench > .part.editor > .content .editor-group-container > .title { height: 35px; display: flex; @@ -16,4 +22,9 @@ .monaco-workbench > .part.editor > .content .editor-group-container > .editor-container { height: calc(100% - 35px); /* below title control */ +} + +.monaco-workbench > .part.editor > .content .editor-group-container.empty > .title, +.monaco-workbench > .part.editor > .content .editor-group-container.empty > .editor-container { + display: none; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor2/media/nextTitleControl.css b/src/vs/workbench/browser/parts/editor2/media/nextTitleControl.css index 3deb682e43e..686bd8d45a1 100644 --- a/src/vs/workbench/browser/parts/editor2/media/nextTitleControl.css +++ b/src/vs/workbench/browser/parts/editor2/media/nextTitleControl.css @@ -54,7 +54,7 @@ display: none; } -/* Drag Cursor */ +/* Drag Cursor (TODO@grid this depends on the feature to drag an entire group to another location) */ .monaco-workbench > .part.editor > .content.multiple-groups .editor-group-container > .title, .monaco-workbench > .part.editor > .content.multiple-groups .editor-group-container > .title.tabs .scrollbar .slider, .monaco-workbench > .part.editor > .content.multiple-groups .editor-group-container > .title .monaco-icon-label::before, diff --git a/src/vs/workbench/browser/parts/editor2/nextEditorGroupView.ts b/src/vs/workbench/browser/parts/editor2/nextEditorGroupView.ts index 50c58bb63d8..1f43fec91d4 100644 --- a/src/vs/workbench/browser/parts/editor2/nextEditorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor2/nextEditorGroupView.ts @@ -10,14 +10,14 @@ import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel'; import { EditorInput, EditorOptions, GroupIdentifier } from 'vs/workbench/common/editor'; import { Event, Emitter } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { addClass, addClasses, Dimension, trackFocus, toggleClass } from 'vs/base/browser/dom'; +import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass } from 'vs/base/browser/dom'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme'; +import { editorBackground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme'; import { IOpenEditorResult, INextEditorGroup } from 'vs/workbench/services/editor/common/nextEditorGroupsService'; import { INextTitleAreaControl } from 'vs/workbench/browser/parts/editor2/nextTitleControl'; import { NextTabsTitleControl } from 'vs/workbench/browser/parts/editor2/nextTabsTitleControl'; @@ -26,14 +26,20 @@ import { IView } from 'vs/base/browser/ui/grid/gridview'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { localize } from 'vs/nls'; export class NextEditorGroupView extends Themable implements IView, INextEditorGroup { private static readonly EDITOR_TITLE_HEIGHT = 35; private _onDidFocus: Emitter = this._register(new Emitter()); + get onDidFocus(): Event { return this._onDidFocus.event; } + private _onWillDispose: Emitter = this._register(new Emitter()); + get onWillDispose(): Event { return this._onWillDispose.event; } + private _onDidActiveEditorChange: Emitter = this._register(new Emitter()); + get onDidActiveEditorChange(): Event { return this._onDidActiveEditorChange.event; } private group: EditorGroup; private dimension: Dimension; @@ -57,17 +63,62 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG this.group = this._register(instantiationService.createInstance(EditorGroup, '')); this.group.label = `Group <${this.group.id}>`; - this.element = document.createElement('div'); - addClass(this.element, 'editor-group-container'); - this.doRender(); + this.doCreate(); + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.group.onEditorsStructureChanged(() => this.updateContainer())); } - get onDidFocus(): Event { - return this._onDidFocus.event; + private doCreate(): void { + + // Container + addClasses(this.element, 'editor-group-container'); + + const focusTracker = this._register(trackFocus(this.element)); + this._register(focusTracker.onDidFocus(() => { + this._onDidFocus.fire(); + })); + + // Title container + this.titleContainer = document.createElement('div'); + addClasses(this.titleContainer, 'title', 'tabs', 'show-file-icons'); + this.element.appendChild(this.titleContainer); + + // Progress bar + this.progressBar = this._register(new ProgressBar(this.element)); + this._register(attachProgressBarStyler(this.progressBar, this.themeService)); + this.progressBar.hide(); + + // Editor container + this.editorContainer = document.createElement('div'); + addClass(this.editorContainer, 'editor-container'); + this.element.appendChild(this.editorContainer); + + // Update styles + this.updateStyles(); + + // Update containers + this.updateContainer(); } - get onWillDispose(): Event { - return this._onWillDispose.event; + private updateContainer(): void { + const empty = this.group.count === 0; + + // Empty Container: allow to focus + if (empty) { + addClass(this.element, 'empty'); + this.element.tabIndex = 0; + this.element.setAttribute('aria-label', localize('emptyEditorGroup', "Empty Editor Group")); + } + + // Non-Empty Container: revert empty container attributes + else { + removeClass(this.element, 'empty'); + this.element.removeAttribute('tabIndex'); + this.element.removeAttribute('aria-label'); + } } setActive(isActive: boolean): void { @@ -84,10 +135,6 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG //#region INextEditorGroup Implementation - get onDidActiveEditorChange(): Event { - return this._onDidActiveEditorChange.event; - } - get id(): GroupIdentifier { return this.group.id; } @@ -175,7 +222,10 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG //#region Themable Implementation protected updateStyles(): void { - super.updateStyles(); + + // Container + this.element.style.backgroundColor = this.getColor(EDITOR_GROUP_BACKGROUND); + this.element.style.outlineColor = this.getColor(focusBorder); // Title control const borderColor = this.getColor(EDITOR_GROUP_HEADER_TABS_BORDER) || this.getColor(contrastBorder); @@ -184,17 +234,15 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG this.titleContainer.style.borderBottomStyle = borderColor ? 'solid' : null; this.titleContainer.style.borderBottomColor = borderColor; - // Editor background - this.element.style.backgroundColor = this.getColor(editorBackground); - - // TODO@grid use editor group background for empty groups? + // Editor container + this.editorContainer.style.backgroundColor = this.getColor(editorBackground); } //#endregion //#region IView implementation - readonly element: HTMLElement; + readonly element: HTMLElement = document.createElement('div'); readonly minimumWidth = 170; readonly minimumHeight = 70; @@ -203,34 +251,6 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG get onDidChange() { return Event.None; } - private doRender(): void { - - // Title container - this.titleContainer = document.createElement('div'); - addClasses(this.titleContainer, 'title', 'tabs', 'show-file-icons', 'active'); - this.element.appendChild(this.titleContainer); - - // Progress bar - this.progressBar = this._register(new ProgressBar(this.element)); - this._register(attachProgressBarStyler(this.progressBar, this.themeService)); - this.progressBar.hide(); - - // Editor container - this.editorContainer = document.createElement('div'); - addClass(this.editorContainer, 'editor-container'); - this.editorContainer.setAttribute('role', 'tabpanel'); - this.element.appendChild(this.editorContainer); - - // Track focus in editor container - const focusTracker = this._register(trackFocus(this.editorContainer)); - this._register(focusTracker.onDidFocus(() => { - this._onDidFocus.fire(); - })); - - // Update styles - this.updateStyles(); - } - layout(width: number, height: number): void { this.dimension = new Dimension(width, height); @@ -251,6 +271,8 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG } } + //#endregion + shutdown(): void { if (this.editorControl) { this.editorControl.shutdown(); @@ -262,6 +284,4 @@ export class NextEditorGroupView extends Themable implements IView, INextEditorG super.dispose(); } - - //#endregion } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor2/nextEditorPart.ts b/src/vs/workbench/browser/parts/editor2/nextEditorPart.ts index 2f040d0840b..398ecf242fc 100644 --- a/src/vs/workbench/browser/parts/editor2/nextEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor2/nextEditorPart.ts @@ -25,7 +25,10 @@ export class NextEditorPart extends Part implements INextEditorGroupsService { _serviceBrand: any; private _onDidLayout: Emitter = this._register(new Emitter()); + get onDidLayout(): Event { return this._onDidLayout.event; } + private _onDidActiveGroupChange: Emitter = this._register(new Emitter()); + get onDidActiveGroupChange(): Event { return this._onDidActiveGroupChange.event; } private dimension: Dimension; @@ -47,10 +50,6 @@ export class NextEditorPart extends Part implements INextEditorGroupsService { //#region INextEditorGroupsService Implementation - get onDidActiveGroupChange(): Event { - return this._onDidActiveGroupChange.event; - } - get activeGroup(): NextEditorGroupView { return this._activeGroup; } @@ -163,12 +162,7 @@ export class NextEditorPart extends Part implements INextEditorGroupsService { //#region Part Implementation - get onDidLayout(): Event { - return this._onDidLayout.event; - } - protected updateStyles(): void { - super.updateStyles(); // Part container const container = this.getContainer(); diff --git a/src/vs/workbench/browser/parts/editor2/nextTitleControl.ts b/src/vs/workbench/browser/parts/editor2/nextTitleControl.ts index 5653f51badb..7a969047942 100644 --- a/src/vs/workbench/browser/parts/editor2/nextTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor2/nextTitleControl.ts @@ -365,10 +365,7 @@ export abstract class NextTitleControl extends Themable implements INextTitleAre //#region IThemeable implementation protected updateStyles(): void { - super.updateStyles(); - - // run a sync update when the theme changes to new styles - this.doScheduleUpdate(true); + this.doScheduleUpdate(true); // run a sync update when the theme changes to new styles } //#endregion diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 0c45a010382..c1ddd6dd2be 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -12,7 +12,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { Position, Direction } from 'vs/platform/editor/common/editor'; import { ResourceMap } from 'vs/base/common/map'; @@ -44,23 +44,10 @@ export interface ISerializedEditorGroup { preview: number; } -export class EditorGroup implements IEditorGroup { +export class EditorGroup extends Disposable implements IEditorGroup { private static IDS = 0; - private _id: GroupIdentifier; - private _label: string; - - private editors: EditorInput[]; - private mru: EditorInput[]; - private mapResourceToEditorCount: ResourceMap; - - private preview: EditorInput; // editor in preview state - private active: EditorInput; // editor in active state - - private toDispose: IDisposable[]; - private editorOpenPositioning: 'left' | 'right' | 'first' | 'last'; - private readonly _onEditorActivated: Emitter; private readonly _onEditorOpened: Emitter; private readonly _onEditorClosed: Emitter; @@ -73,32 +60,43 @@ export class EditorGroup implements IEditorGroup { private readonly _onEditorStateChanged: Emitter; private readonly _onEditorsStructureChanged: Emitter; + private _id: GroupIdentifier; + private _label: string; + + private editors: EditorInput[]; + private mru: EditorInput[]; + private mapResourceToEditorCount: ResourceMap; + + private preview: EditorInput; // editor in preview state + private active: EditorInput; // editor in active state + + private editorOpenPositioning: 'left' | 'right' | 'first' | 'last'; + constructor( arg1: string | ISerializedEditorGroup, @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService ) { + super(); + this._id = EditorGroup.IDS++; this.editors = []; this.mru = []; - this.toDispose = []; this.mapResourceToEditorCount = new ResourceMap(); this.onConfigurationUpdated(); - this._onEditorActivated = new Emitter(); - this._onEditorOpened = new Emitter(); - this._onEditorClosed = new Emitter(); - this._onEditorDisposed = new Emitter(); - this._onEditorDirty = new Emitter(); - this._onEditorLabelChange = new Emitter(); - this._onEditorMoved = new Emitter(); - this._onEditorPinned = new Emitter(); - this._onEditorUnpinned = new Emitter(); - this._onEditorStateChanged = new Emitter(); - this._onEditorsStructureChanged = new Emitter(); - - this.toDispose.push(this._onEditorActivated, this._onEditorOpened, this._onEditorClosed, this._onEditorDisposed, this._onEditorDirty, this._onEditorLabelChange, this._onEditorMoved, this._onEditorPinned, this._onEditorUnpinned, this._onEditorStateChanged, this._onEditorsStructureChanged); + this._onEditorActivated = this._register(new Emitter()); + this._onEditorOpened = this._register(new Emitter()); + this._onEditorClosed = this._register(new Emitter()); + this._onEditorDisposed = this._register(new Emitter()); + this._onEditorDirty = this._register(new Emitter()); + this._onEditorLabelChange = this._register(new Emitter()); + this._onEditorMoved = this._register(new Emitter()); + this._onEditorPinned = this._register(new Emitter()); + this._onEditorUnpinned = this._register(new Emitter()); + this._onEditorStateChanged = this._register(new Emitter()); + this._onEditorsStructureChanged = this._register(new Emitter()); if (typeof arg1 === 'object') { this.deserialize(arg1); @@ -110,7 +108,7 @@ export class EditorGroup implements IEditorGroup { } private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); + this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); } private onConfigurationUpdated(event?: IConfigurationChangeEvent): void { @@ -687,10 +685,6 @@ export class EditorGroup implements IEditorGroup { this.active = this.mru[0]; this.preview = this.editors[data.preview]; } - - public dispose(): void { - dispose(this.toDispose); - } } interface ISerializedEditorStacksModel { -- GitLab