Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workbench grid dimension constraint propagation #51989

Merged
merged 13 commits into from
Jun 16, 2018
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/grid/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { tail2 as tail } from 'vs/base/common/arrays';
import { orthogonal, IView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles } from './gridview';
import { Event } from 'vs/base/common/event';

export { Orientation } from './gridview';

Expand Down Expand Up @@ -192,9 +193,9 @@ export class Grid<T extends IView> implements IDisposable {

get minimumWidth(): number { return this.gridview.minimumWidth; }
get minimumHeight(): number { return this.gridview.minimumHeight; }

get maximumWidth(): number { return this.gridview.maximumWidth; }
get maximumHeight(): number { return this.gridview.maximumHeight; }
get onDidChange(): Event<{ width: number; height: number; }> { return this.gridview.onDidChange; }

get element(): HTMLElement { return this.gridview.element; }

Expand Down
36 changes: 12 additions & 24 deletions src/vs/base/browser/ui/grid/gridview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ class BranchNode implements ISplitView, IDisposable {
}

get minimumOrthogonalSize(): number {
return this.children.length === 0 ? 0 : this.children.reduce((r, c) => r + c.minimumSize, 0);
return this.splitview.minimumSize;
}

get maximumOrthogonalSize(): number {
return this.children.length === 0 ? Number.POSITIVE_INFINITY : this.children.reduce((r, c) => r + c.maximumSize, 0);
return this.splitview.maximumSize;
}

get minimumWidth(): number {
Expand Down Expand Up @@ -363,7 +363,7 @@ class LeafNode implements ISplitView, IDisposable {
}

get onDidChange(): Event<number> {
return mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? ({ width }) => width : ({ height }) => height);
return mapEvent(this.view.onDidChange, this.orientation === Orientation.HORIZONTAL ? e => e && e.width : e => e && e.height);
}

set orthogonalStartSash(sash: Sash) {
Expand Down Expand Up @@ -442,6 +442,7 @@ export class GridView implements IDisposable {
this._root = root;
this.element.appendChild(root.element);
this.onDidSashResetRelay.input = root.onDidSashReset;
this._onDidChange.input = mapEvent(root.onDidChange, () => undefined); // TODO
}

get orientation(): Orientation {
Expand All @@ -459,29 +460,16 @@ export class GridView implements IDisposable {
this.root.orthogonalLayout(orthogonalSize);
}

get width(): number {
return this.root.width;
}

get height(): number {
return this.root.height;
}
get width(): number { return this.root.width; }
get height(): number { return this.root.height; }

get minimumWidth(): number {
return this.root.minimumWidth;
}
get minimumWidth(): number { return this.root.minimumWidth; }
get minimumHeight(): number { return this.root.minimumHeight; }
get maximumWidth(): number { return this.root.maximumHeight; }
get maximumHeight(): number { return this.root.maximumHeight; }

get minimumHeight(): number {
return this.root.minimumHeight;
}

get maximumWidth(): number {
return this.root.maximumHeight;
}

get maximumHeight(): number {
return this.root.maximumHeight;
}
private _onDidChange = new Relay<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;

constructor(options: IGridViewOptions = {}) {
this.element = $('.monaco-grid-view');
Expand Down
8 changes: 8 additions & 0 deletions src/vs/base/browser/ui/splitview/splitview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ export class SplitView implements IDisposable {
return this.viewItems.length;
}

get minimumSize(): number {
return this.viewItems.reduce((r, item) => r + item.view.minimumSize, 0);
}

get maximumSize(): number {
return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.view.maximumSize, 0);
}

private _orthogonalStartSash: Sash | undefined;
get orthogonalStartSash(): Sash | undefined { return this._orthogonalStartSash; }
set orthogonalStartSash(sash: Sash | undefined) {
Expand Down
37 changes: 18 additions & 19 deletions src/vs/workbench/browser/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,23 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr

private registerListeners(): void {
this._register(this.themeService.onThemeChange(_ => this.layout()));
this._register(this.parts.editor.onDidPreferredSizeChange(() => this.onDidPreferredSizeChange()));
this._register(this.parts.editor.onDidSizeConstraintsChange(() => this.onDidEditorSizeConstraintsChange()));

this.registerSashListeners();
}

private onDidPreferredSizeChange(): void {
private onDidEditorSizeConstraintsChange(): void {
if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
if (this.editorGroupService.count > 1) {
const preferredEditorPartSize = this.parts.editor.preferredSize;
const minimumEditorPartSize = new Dimension(this.parts.editor.minimumWidth, this.parts.editor.minimumHeight);

const sidebarOverflow = this.workbenchSize.width - this.sidebarWidth < preferredEditorPartSize.width;
const sidebarOverflow = this.workbenchSize.width - this.sidebarWidth < minimumEditorPartSize.width;

let panelOverflow = false;
if (this.partService.getPanelPosition() === Position.RIGHT) {
panelOverflow = this.workbenchSize.width - this.panelWidth - this.sidebarWidth < preferredEditorPartSize.width;
panelOverflow = this.workbenchSize.width - this.panelWidth - this.sidebarWidth < minimumEditorPartSize.width;
} else {
panelOverflow = this.workbenchSize.height - this.panelHeight < preferredEditorPartSize.height;
panelOverflow = this.workbenchSize.height - this.panelHeight < minimumEditorPartSize.height;
}

// Trigger a layout if we detect that either sidebar or panel overflow
Expand Down Expand Up @@ -191,11 +191,11 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
minSidebarWidth = 0;
}

return Math.max(this.partLayoutInfo.panel.minWidth, this.workbenchSize.width - this.parts.editor.preferredSize.width - minSidebarWidth - this.activitybarWidth);
return Math.max(this.partLayoutInfo.panel.minWidth, this.workbenchSize.width - this.parts.editor.minimumWidth - minSidebarWidth - this.activitybarWidth);
}

private computeMaxPanelHeight(): number {
return Math.max(this.partLayoutInfo.panel.minHeight, this.sidebarHeight /* simplification for: window.height - status.height - title-height */ - this.parts.editor.preferredSize.height);
return Math.max(this.partLayoutInfo.panel.minHeight, this.sidebarHeight /* simplification for: window.height - status.height - title-height */ - this.parts.editor.minimumHeight);
}

private get sidebarWidth(): number {
Expand All @@ -208,7 +208,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr

private set sidebarWidth(value: number) {
const panelMinWidth = this.partService.getPanelPosition() === Position.RIGHT && this.partService.isVisible(Parts.PANEL_PART) ? this.partLayoutInfo.panel.minWidth : 0;
const maxSidebarWidth = this.workbenchSize.width - this.activitybarWidth - this.parts.editor.preferredSize.width - panelMinWidth;
const maxSidebarWidth = this.workbenchSize.width - this.activitybarWidth - this.parts.editor.minimumWidth - panelMinWidth;

this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, Math.min(maxSidebarWidth, value));
}
Expand Down Expand Up @@ -486,10 +486,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
editorSize.width = this.workbenchSize.width - sidebarSize.width - activityBarSize.width - (panelPosition === Position.RIGHT ? panelDimension.width : 0);
editorSize.height = sidebarSize.height - (panelPosition === Position.BOTTOM ? panelDimension.height : 0);

// Adjust for Editor Part preferred width
const preferredEditorPartSize = this.parts.editor.preferredSize;
if (editorSize.width < preferredEditorPartSize.width) {
const missingPreferredEditorWidth = preferredEditorPartSize.width - editorSize.width;
// Adjust for Editor Part minimum width
const minimumEditorPartSize = new Dimension(this.parts.editor.minimumWidth, this.parts.editor.minimumHeight);
if (editorSize.width < minimumEditorPartSize.width) {
const missingPreferredEditorWidth = minimumEditorPartSize.width - editorSize.width;
let outstandingMissingPreferredEditorWidth = missingPreferredEditorWidth;

// Take from Panel if Panel Position on the Right and Visible
Expand All @@ -512,9 +512,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
}
}

// Adjust for Editor Part preferred height
if (editorSize.height < preferredEditorPartSize.height) {
const missingPreferredEditorHeight = preferredEditorPartSize.height - editorSize.height;
// Adjust for Editor Part minimum height
if (editorSize.height < minimumEditorPartSize.height) {
const missingPreferredEditorHeight = minimumEditorPartSize.height - editorSize.height;
let outstandingMissingPreferredEditorHeight = missingPreferredEditorHeight;

// Take from Panel if Panel Position on the Bottom and Visible
Expand Down Expand Up @@ -713,9 +713,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
case Parts.SIDEBAR_PART:
this.sidebarWidth = this.sidebarWidth + sizeChangePxWidth; // Sidebar can not become smaller than MIN_PART_WIDTH

const preferredEditorPartSize = this.parts.editor.preferredSize;
if (this.workbenchSize.width - this.sidebarWidth < preferredEditorPartSize.width) {
this.sidebarWidth = this.workbenchSize.width - preferredEditorPartSize.width;
if (this.workbenchSize.width - this.sidebarWidth < this.parts.editor.minimumWidth) {
this.sidebarWidth = this.workbenchSize.width - this.parts.editor.minimumWidth;
}

doLayout = true;
Expand Down
10 changes: 9 additions & 1 deletion src/vs/workbench/browser/parts/editor/baseEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/
import { IStorageService } from 'vs/platform/storage/common/storage';
import { LRUCache } from 'vs/base/common/map';
import URI from 'vs/base/common/uri';
import { once } from 'vs/base/common/event';
import { once, Event } from 'vs/base/common/event';
import { isEmptyObject } from 'vs/base/common/types';
import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';

/**
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
Expand All @@ -32,6 +33,13 @@ import { isEmptyObject } from 'vs/base/common/types';
*/
export abstract class BaseEditor extends Panel implements IEditor {

readonly minimumWidth = DEFAULT_EDITOR_MIN_DIMENSIONS.width;
readonly maximumWidth = DEFAULT_EDITOR_MAX_DIMENSIONS.width;
readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height;
readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height;

readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; }> = Event.None;

private static readonly EDITOR_MEMENTOS: Map<string, EditorMemento<any>> = new Map<string, EditorMemento<any>>();

protected _input: EditorInput;
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/browser/parts/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic

export const EDITOR_TITLE_HEIGHT = 35;

export const EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
export const EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);

export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
iconTheme?: string;
Expand Down
47 changes: 32 additions & 15 deletions src/vs/workbench/browser/parts/editor/editorControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { toWinJsPromise } from 'vs/base/common/async';
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';

export interface IOpenEditorResult {
Expand All @@ -26,17 +26,24 @@ export interface IOpenEditorResult {

export class EditorControl extends Disposable {

get minimumWidth() { return this._activeControl ? this._activeControl.minimumWidth : DEFAULT_EDITOR_MIN_DIMENSIONS.width; }
get minimumHeight() { return this._activeControl ? this._activeControl.minimumHeight : DEFAULT_EDITOR_MIN_DIMENSIONS.height; }
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }

private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }

private activeControlFocusListener: IDisposable;

private dimension: Dimension;
private editorOperation: LongRunningOperation;
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; }>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return this._onDidSizeConstraintsChange.event; }

private _activeControl: BaseEditor;
private controls: BaseEditor[] = [];

private activeControlDisposeables: IDisposable[] = [];
private dimension: Dimension;
private editorOperation: LongRunningOperation;

constructor(
private parent: HTMLElement,
private groupView: IEditorGroupView,
Expand Down Expand Up @@ -76,16 +83,13 @@ export class EditorControl extends Disposable {
// Create editor
const control = this.doCreateEditorControl(descriptor);

// Remember editor as active
this._activeControl = control;
// Set editor as active
this.doSetActiveControl(control);

// Show editor
this.parent.appendChild(control.getContainer());
show(control.getContainer());

// Track focus
this.activeControlFocusListener = control.onDidFocus(() => this._onDidFocus.fire());

// Indicate to editor that it is now visible
control.setVisible(true, this.groupView);

Expand Down Expand Up @@ -129,6 +133,22 @@ export class EditorControl extends Disposable {
return control;
}

private doSetActiveControl(control: BaseEditor) {
this._activeControl = control;

// Clear out previous active control listeners
this.activeControlDisposeables = dispose(this.activeControlDisposeables);

// Listen to control changes
if (control) {
this.activeControlDisposeables.push(control.onDidSizeConstraintsChange(e => this._onDidSizeConstraintsChange.fire(e)));
this.activeControlDisposeables.push(control.onDidFocus(() => this._onDidFocus.fire()));
}

// Indicate that size constraints could have changed due to new editor
this._onDidSizeConstraintsChange.fire();
}

private doSetInput(control: BaseEditor, editor: EditorInput, options: EditorOptions): TPromise<boolean> {

// If the input did not change, return early and only apply the options
Expand Down Expand Up @@ -196,10 +216,7 @@ export class EditorControl extends Disposable {
this._activeControl.setVisible(false, this.groupView);

// Clear active control
this._activeControl = null;

// Clear focus listener
this.activeControlFocusListener = dispose(this.activeControlFocusListener);
this.doSetActiveControl(null);
}

closeEditor(editor: EditorInput): void {
Expand All @@ -223,7 +240,7 @@ export class EditorControl extends Disposable {
}

dispose(): void {
this.activeControlFocusListener = dispose(this.activeControlFocusListener);
this.activeControlDisposeables = dispose(this.activeControlDisposeables);

super.dispose();
}
Expand Down
16 changes: 9 additions & 7 deletions src/vs/workbench/browser/parts/editor/editorGroupView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'vs/css!./media/editorgroupview';
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext } from 'vs/workbench/common/editor';
import { Event, Emitter, once } from 'vs/base/common/event';
import { Event, Emitter, once, Relay } from 'vs/base/common/event';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
Expand All @@ -34,7 +34,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RunOnceWorker } from 'vs/base/common/async';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, EDITOR_MIN_DIMENSIONS, EDITOR_MAX_DIMENSIONS, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, EDITOR_TITLE_HEIGHT, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { join } from 'vs/base/common/paths';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
Expand Down Expand Up @@ -197,6 +197,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {

// Editor control
this.editorControl = this._register(this.scopedInstantiationService.createInstance(EditorControl, this.editorContainer, this));
this._onDidChange.input = this.editorControl.onDidSizeConstraintsChange;

// Track Focus
this.doTrackFocus();
Expand Down Expand Up @@ -1330,12 +1331,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {

readonly element: HTMLElement = document.createElement('div');

readonly minimumWidth = EDITOR_MIN_DIMENSIONS.width;
readonly minimumHeight = EDITOR_MIN_DIMENSIONS.height;
readonly maximumWidth = EDITOR_MAX_DIMENSIONS.width;
readonly maximumHeight = EDITOR_MAX_DIMENSIONS.height;
get minimumWidth(): number { return this.editorControl.minimumWidth; }
get minimumHeight(): number { return this.editorControl.minimumHeight; }
get maximumWidth(): number { return this.editorControl.maximumWidth; }
get maximumHeight(): number { return this.editorControl.maximumHeight; }

get onDidChange() { return Event.None; } // only needed if minimum sizes ever change
private _onDidChange = this._register(new Relay<{ width: number; height: number; }>());
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;

layout(width: number, height: number): void {
this.dimension = new Dimension(width, height);
Expand Down