Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)

* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c

* remove files we don't want

* fix hygiene

* update distro

* update distro

* fix hygiene

* fix strict nulls

* distro

* distro

* fix tests

* fix tests

* add another edit

* fix viewlet icon

* fix azure dialog

* fix some padding

* fix more padding issues
This commit is contained in:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

View File

@@ -1,100 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-panel-view {
width: 100%;
height: 100%;
}
.monaco-panel-view .panel {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.monaco-panel-view .panel > .panel-header {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
overflow: hidden;
display: flex;
cursor: pointer;
}
.monaco-panel-view .panel > .panel-header > .twisties {
width: 20px;
display: flex;
align-items: center;
justify-content: center;
transform-origin: center;
color: inherit;
flex-shrink: 0;
}
.monaco-panel-view .panel > .panel-header.expanded > .twisties::before {
transform: rotate(90deg);
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel > .panel-header > .actions {
display: none;
flex: 1;
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel:hover > .panel-header.expanded > .actions,
.monaco-panel-view .panel > .panel-header.actions-always-visible.expanded > .actions,
.monaco-panel-view .panel > .panel-header.focused.expanded > .actions {
display: initial;
}
/* TODO: actions should be part of the panel, but they aren't yet */
.monaco-panel-view .panel > .panel-header > .actions .action-label.icon,
.monaco-panel-view .panel > .panel-header > .actions .action-label.codicon {
width: 28px;
height: 22px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0;
display: flex;
align-items: center;
justify-content: center;
color: inherit;
}
/* Bold font style does not go well with CJK fonts */
.monaco-panel-view:lang(zh-Hans) .panel > .panel-header,
.monaco-panel-view:lang(zh-Hant) .panel > .panel-header,
.monaco-panel-view:lang(ja) .panel > .panel-header,
.monaco-panel-view:lang(ko) .panel > .panel-header {
font-weight: normal;
}
.monaco-panel-view .panel > .panel-header.hidden {
display: none;
}
.monaco-panel-view .panel > .panel-body {
overflow: hidden;
flex: 1;
}
/* Animation */
.monaco-panel-view.animated .split-view-view {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
.monaco-panel-view.animated.vertical .split-view-view {
transition-property: height;
}
.monaco-panel-view.animated.horizontal .split-view-view {
transition-property: width;
}

View File

@@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-pane-view {
width: 100%;
height: 100%;
}
.monaco-pane-view .pane {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.monaco-pane-view .pane > .pane-header {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
overflow: hidden;
display: flex;
cursor: pointer;
align-items: center;
}
.monaco-pane-view .pane > .pane-header > .twisties {
width: 20px;
display: flex;
align-items: center;
justify-content: center;
transform-origin: center;
color: inherit;
flex-shrink: 0;
}
.monaco-pane-view .pane > .pane-header.expanded > .twisties::before {
transform: rotate(90deg);
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane > .pane-header > .actions {
display: none;
flex: 1;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane:hover > .pane-header.expanded > .actions,
.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions,
.monaco-pane-view .pane > .pane-header.focused.expanded > .actions {
display: initial;
}
/* TODO: actions should be part of the pane, but they aren't yet */
.monaco-pane-view .pane > .pane-header > .actions .action-label.icon,
.monaco-pane-view .pane > .pane-header > .actions .action-label.codicon {
width: 28px;
height: 22px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
margin-right: 0;
display: flex;
align-items: center;
justify-content: center;
color: inherit;
}
/* Bold font style does not go well with CJK fonts */
.monaco-pane-view:lang(zh-Hans) .pane > .pane-header,
.monaco-pane-view:lang(zh-Hant) .pane > .pane-header,
.monaco-pane-view:lang(ja) .pane > .pane-header,
.monaco-pane-view:lang(ko) .pane > .pane-header {
font-weight: normal;
}
.monaco-pane-view .pane > .pane-header.hidden {
display: none;
}
.monaco-pane-view .pane > .pane-body {
overflow: hidden;
flex: 1;
}
/* Animation */
.monaco-pane-view.animated .split-view-view {
transition-duration: 0.15s;
transition-timing-function: ease-out;
}
.monaco-pane-view.animated.vertical .split-view-view {
transition-property: height;
}
.monaco-pane-view.animated.horizontal .split-view-view {
transition-property: width;
}

View File

@@ -3,25 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./panelview';
import 'vs/css!./paneview';
import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { $, append, addClass, removeClass, toggleClass, trackFocus } from 'vs/base/browser/dom';
import { $, append, addClass, removeClass, toggleClass, trackFocus, EventHelper } from 'vs/base/browser/dom';
import { firstIndex } from 'vs/base/common/arrays';
import { Color, RGBA } from 'vs/base/common/color';
import { SplitView, IView } from './splitview';
import { isFirefox } from 'vs/base/browser/browser';
import { DataTransfers } from 'vs/base/browser/dnd';
export interface IPanelOptions {
export interface IPaneOptions {
ariaHeaderLabel?: string;
minimumBodySize?: number;
maximumBodySize?: number;
expanded?: boolean;
}
export interface IPanelStyles {
export interface IPaneStyles {
dropBackground?: Color;
headerForeground?: Color;
headerBackground?: Color;
@@ -29,7 +31,7 @@ export interface IPanelStyles {
}
/**
* A Panel is a structured SplitView view.
* A Pane is a structured SplitView view.
*
* WARNING: You must call `render()` after you contruct it.
* It can't be done automatically at the end of the ctor
@@ -37,7 +39,7 @@ export interface IPanelStyles {
* Subclasses wouldn't be able to set own properties
* before the `render()` call, thus forbiding their use.
*/
export abstract class Panel extends Disposable implements IView {
export abstract class Pane extends Disposable implements IView {
private static readonly HEADER_SIZE = 22;
@@ -52,7 +54,7 @@ export abstract class Panel extends Disposable implements IView {
private _minimumBodySize: number;
private _maximumBodySize: number;
private ariaHeaderLabel: string;
private styles: IPanelStyles = {};
private styles: IPaneStyles = {};
private animationTimer: number | undefined = undefined;
private readonly _onDidChange = this._register(new Emitter<number | undefined>());
@@ -93,7 +95,7 @@ export abstract class Panel extends Disposable implements IView {
}
private get headerSize(): number {
return this.headerVisible ? Panel.HEADER_SIZE : 0;
return this.headerVisible ? Pane.HEADER_SIZE : 0;
}
get minimumSize(): number {
@@ -114,14 +116,14 @@ export abstract class Panel extends Disposable implements IView {
width: number = 0;
constructor(options: IPanelOptions = {}) {
constructor(options: IPaneOptions = {}) {
super();
this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded;
this.ariaHeaderLabel = options.ariaHeaderLabel || '';
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
this.element = $('.panel');
this.element = $('.pane');
}
isExpanded(): boolean {
@@ -167,7 +169,7 @@ export abstract class Panel extends Disposable implements IView {
}
render(): void {
this.header = $('.panel-header');
this.header = $('.pane-header');
append(this.element, this.header);
this.header.setAttribute('tabindex', '0');
this.header.setAttribute('role', 'toolbar');
@@ -196,12 +198,12 @@ export abstract class Panel extends Disposable implements IView {
this._register(domEvent(this.header, 'click')
(() => this.setExpanded(!this.isExpanded()), null));
this.body = append(this.element, $('.panel-body'));
this.body = append(this.element, $('.pane-body'));
this.renderBody(this.body);
}
layout(height: number): void {
const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0;
const headerSize = this.headerVisible ? Pane.HEADER_SIZE : 0;
if (this.isExpanded()) {
this.layoutBody(height - headerSize, this.width);
@@ -209,7 +211,7 @@ export abstract class Panel extends Disposable implements IView {
}
}
style(styles: IPanelStyles): void {
style(styles: IPaneStyles): void {
this.styles = styles;
if (!this.header) {
@@ -240,31 +242,31 @@ export abstract class Panel extends Disposable implements IView {
}
interface IDndContext {
draggable: PanelDraggable | null;
draggable: PaneDraggable | null;
}
class PanelDraggable extends Disposable {
class PaneDraggable extends Disposable {
private static readonly DefaultDragOverBackgroundColor = new Color(new RGBA(128, 128, 128, 0.5));
private dragOverCounter = 0; // see https://github.com/Microsoft/vscode/issues/14470
private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>());
private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>());
readonly onDidDrop = this._onDidDrop.event;
constructor(private panel: Panel, private dnd: IPanelDndController, private context: IDndContext) {
constructor(private pane: Pane, private dnd: IPaneDndController, private context: IDndContext) {
super();
panel.draggableElement.draggable = true;
this._register(domEvent(panel.draggableElement, 'dragstart')(this.onDragStart, this));
this._register(domEvent(panel.dropTargetElement, 'dragenter')(this.onDragEnter, this));
this._register(domEvent(panel.dropTargetElement, 'dragleave')(this.onDragLeave, this));
this._register(domEvent(panel.dropTargetElement, 'dragend')(this.onDragEnd, this));
this._register(domEvent(panel.dropTargetElement, 'drop')(this.onDrop, this));
pane.draggableElement.draggable = true;
this._register(domEvent(pane.draggableElement, 'dragstart')(this.onDragStart, this));
this._register(domEvent(pane.dropTargetElement, 'dragenter')(this.onDragEnter, this));
this._register(domEvent(pane.dropTargetElement, 'dragleave')(this.onDragLeave, this));
this._register(domEvent(pane.dropTargetElement, 'dragend')(this.onDragEnd, this));
this._register(domEvent(pane.dropTargetElement, 'drop')(this.onDrop, this));
}
private onDragStart(e: DragEvent): void {
if (!this.dnd.canDrag(this.panel) || !e.dataTransfer) {
if (!this.dnd.canDrag(this.pane) || !e.dataTransfer) {
e.preventDefault();
e.stopPropagation();
return;
@@ -272,7 +274,12 @@ class PanelDraggable extends Disposable {
e.dataTransfer.effectAllowed = 'move';
const dragImage = append(document.body, $('.monaco-drag-image', {}, this.panel.draggableElement.textContent || ''));
if (isFirefox) {
// Firefox: requires to set a text data transfer to get going
e.dataTransfer?.setData(DataTransfers.TEXT, this.pane.draggableElement.textContent || '');
}
const dragImage = append(document.body, $('.monaco-drag-image', {}, this.pane.draggableElement.textContent || ''));
e.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
@@ -284,7 +291,7 @@ class PanelDraggable extends Disposable {
return;
}
if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) {
if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) {
return;
}
@@ -297,7 +304,7 @@ class PanelDraggable extends Disposable {
return;
}
if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) {
if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) {
return;
}
@@ -323,11 +330,13 @@ class PanelDraggable extends Disposable {
return;
}
EventHelper.stop(e);
this.dragOverCounter = 0;
this.render();
if (this.dnd.canDrop(this.context.draggable.panel, this.panel) && this.context.draggable !== this) {
this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel });
if (this.dnd.canDrop(this.context.draggable.pane, this.pane) && this.context.draggable !== this) {
this._onDidDrop.fire({ from: this.context.draggable.pane, to: this.pane });
}
this.context.draggable = null;
@@ -337,106 +346,106 @@ class PanelDraggable extends Disposable {
let backgroundColor: string | null = null;
if (this.dragOverCounter > 0) {
backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString();
backgroundColor = (this.pane.dropBackground || PaneDraggable.DefaultDragOverBackgroundColor).toString();
}
this.panel.dropTargetElement.style.backgroundColor = backgroundColor || '';
this.pane.dropTargetElement.style.backgroundColor = backgroundColor || '';
}
}
export interface IPanelDndController {
canDrag(panel: Panel): boolean;
canDrop(panel: Panel, overPanel: Panel): boolean;
export interface IPaneDndController {
canDrag(pane: Pane): boolean;
canDrop(pane: Pane, overPane: Pane): boolean;
}
export class DefaultPanelDndController implements IPanelDndController {
export class DefaultPaneDndController implements IPaneDndController {
canDrag(panel: Panel): boolean {
canDrag(pane: Pane): boolean {
return true;
}
canDrop(panel: Panel, overPanel: Panel): boolean {
canDrop(pane: Pane, overPane: Pane): boolean {
return true;
}
}
export interface IPanelViewOptions {
dnd?: IPanelDndController;
export interface IPaneViewOptions {
dnd?: IPaneDndController;
}
interface IPanelItem {
panel: Panel;
interface IPaneItem {
pane: Pane;
disposable: IDisposable;
}
export class PanelView extends Disposable {
export class PaneView extends Disposable {
private dnd: IPanelDndController | undefined;
private dnd: IPaneDndController | undefined;
private dndContext: IDndContext = { draggable: null };
private el: HTMLElement;
private panelItems: IPanelItem[] = [];
private paneItems: IPaneItem[] = [];
private width: number = 0;
private splitview: SplitView;
private animationTimer: number | undefined = undefined;
private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>());
readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event;
private _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>());
readonly onDidDrop: Event<{ from: Pane, to: Pane }> = this._onDidDrop.event;
readonly onDidSashChange: Event<number>;
constructor(container: HTMLElement, options: IPanelViewOptions = {}) {
constructor(container: HTMLElement, options: IPaneViewOptions = {}) {
super();
this.dnd = options.dnd;
this.el = append(container, $('.monaco-panel-view'));
this.el = append(container, $('.monaco-pane-view'));
this.splitview = this._register(new SplitView(this.el));
this.onDidSashChange = this.splitview.onDidSashChange;
}
addPanel(panel: Panel, size: number, index = this.splitview.length): void {
addPane(pane: Pane, size: number, index = this.splitview.length): void {
const disposables = new DisposableStore();
panel.onDidChangeExpansionState(this.setupAnimation, this, disposables);
pane.onDidChangeExpansionState(this.setupAnimation, this, disposables);
const panelItem = { panel, disposable: disposables };
this.panelItems.splice(index, 0, panelItem);
panel.width = this.width;
this.splitview.addView(panel, size, index);
const paneItem = { pane: pane, disposable: disposables };
this.paneItems.splice(index, 0, paneItem);
pane.width = this.width;
this.splitview.addView(pane, size, index);
if (this.dnd) {
const draggable = new PanelDraggable(panel, this.dnd, this.dndContext);
const draggable = new PaneDraggable(pane, this.dnd, this.dndContext);
disposables.add(draggable);
disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop));
}
}
removePanel(panel: Panel): void {
const index = firstIndex(this.panelItems, item => item.panel === panel);
removePane(pane: Pane): void {
const index = firstIndex(this.paneItems, item => item.pane === pane);
if (index === -1) {
return;
}
this.splitview.removeView(index);
const panelItem = this.panelItems.splice(index, 1)[0];
panelItem.disposable.dispose();
const paneItem = this.paneItems.splice(index, 1)[0];
paneItem.disposable.dispose();
}
movePanel(from: Panel, to: Panel): void {
const fromIndex = firstIndex(this.panelItems, item => item.panel === from);
const toIndex = firstIndex(this.panelItems, item => item.panel === to);
movePane(from: Pane, to: Pane): void {
const fromIndex = firstIndex(this.paneItems, item => item.pane === from);
const toIndex = firstIndex(this.paneItems, item => item.pane === to);
if (fromIndex === -1 || toIndex === -1) {
return;
}
const [panelItem] = this.panelItems.splice(fromIndex, 1);
this.panelItems.splice(toIndex, 0, panelItem);
const [paneItem] = this.paneItems.splice(fromIndex, 1);
this.paneItems.splice(toIndex, 0, paneItem);
this.splitview.moveView(fromIndex, toIndex);
}
resizePanel(panel: Panel, size: number): void {
const index = firstIndex(this.panelItems, item => item.panel === panel);
resizePane(pane: Pane, size: number): void {
const index = firstIndex(this.paneItems, item => item.pane === pane);
if (index === -1) {
return;
@@ -445,8 +454,8 @@ export class PanelView extends Disposable {
this.splitview.resizeView(index, size);
}
getPanelSize(panel: Panel): number {
const index = firstIndex(this.panelItems, item => item.panel === panel);
getPaneSize(pane: Pane): number {
const index = firstIndex(this.paneItems, item => item.pane === pane);
if (index === -1) {
return -1;
@@ -458,8 +467,8 @@ export class PanelView extends Disposable {
layout(height: number, width: number): void {
this.width = width;
for (const panelItem of this.panelItems) {
panelItem.panel.width = width;
for (const paneItem of this.paneItems) {
paneItem.pane.width = width;
}
this.splitview.layout(height);
@@ -481,6 +490,6 @@ export class PanelView extends Disposable {
dispose(): void {
super.dispose();
this.panelItems.forEach(i => i.disposable.dispose());
this.paneItems.forEach(i => i.disposable.dispose());
}
}

View File

@@ -23,14 +23,14 @@ const defaultStyles: ISplitViewStyles = {
separatorBorder: Color.transparent
};
export interface ISplitViewOptions {
export interface ISplitViewOptions<TLayoutContext = undefined> {
readonly orientation?: Orientation; // default Orientation.VERTICAL
readonly styles?: ISplitViewStyles;
readonly orthogonalStartSash?: Sash;
readonly orthogonalEndSash?: Sash;
readonly inverseAltBehavior?: boolean;
readonly proportionalLayout?: boolean; // default true,
readonly descriptor?: ISplitViewDescriptor;
readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
}
/**
@@ -42,14 +42,14 @@ export const enum LayoutPriority {
High
}
export interface IView {
export interface IView<TLayoutContext = undefined> {
readonly element: HTMLElement;
readonly minimumSize: number;
readonly maximumSize: number;
readonly onDidChange: Event<number | undefined>;
readonly priority?: LayoutPriority;
readonly snap?: boolean;
layout(size: number, orthogonalSize: number | undefined): void;
layout(size: number, offset: number, context: TLayoutContext | undefined): void;
setVisible?(visible: boolean): void;
}
@@ -62,7 +62,7 @@ interface ISashEvent {
type ViewItemSize = number | { cachedVisibleSize: number };
abstract class ViewItem {
abstract class ViewItem<TLayoutContext> {
private _size: number;
set size(size: number) {
@@ -109,9 +109,13 @@ abstract class ViewItem {
get priority(): LayoutPriority | undefined { return this.view.priority; }
get snap(): boolean { return !!this.view.snap; }
set enabled(enabled: boolean) {
this.container.style.pointerEvents = enabled ? null : 'none';
}
constructor(
protected container: HTMLElement,
private view: IView,
private view: IView<TLayoutContext>,
size: ViewItemSize,
private disposable: IDisposable
) {
@@ -125,31 +129,31 @@ abstract class ViewItem {
}
}
layout(position: number, orthogonalSize: number | undefined): void {
this.layoutContainer(position);
this.view.layout(this.size, orthogonalSize);
layout(offset: number, layoutContext: TLayoutContext | undefined): void {
this.layoutContainer(offset);
this.view.layout(this.size, offset, layoutContext);
}
abstract layoutContainer(position: number): void;
abstract layoutContainer(offset: number): void;
dispose(): IView {
dispose(): IView<TLayoutContext> {
this.disposable.dispose();
return this.view;
}
}
class VerticalViewItem extends ViewItem {
class VerticalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
layoutContainer(position: number): void {
this.container.style.top = `${position}px`;
layoutContainer(offset: number): void {
this.container.style.top = `${offset}px`;
this.container.style.height = `${this.size}px`;
}
}
class HorizontalViewItem extends ViewItem {
class HorizontalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
layoutContainer(position: number): void {
this.container.style.left = `${position}px`;
layoutContainer(offset: number): void {
this.container.style.left = `${offset}px`;
this.container.style.width = `${this.size}px`;
}
}
@@ -194,26 +198,26 @@ export namespace Sizing {
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
}
export interface ISplitViewDescriptor {
export interface ISplitViewDescriptor<TLayoutContext> {
size: number;
views: {
visible?: boolean;
size: number;
view: IView;
view: IView<TLayoutContext>;
}[];
}
export class SplitView extends Disposable {
export class SplitView<TLayoutContext = undefined> extends Disposable {
readonly orientation: Orientation;
readonly el: HTMLElement;
private sashContainer: HTMLElement;
private viewContainer: HTMLElement;
private size = 0;
private orthogonalSize: number | undefined;
private layoutContext: TLayoutContext | undefined;
private contentSize = 0;
private proportions: undefined | number[] = undefined;
private viewItems: ViewItem[] = [];
private viewItems: ViewItem<TLayoutContext>[] = [];
private sashItems: ISashItem[] = [];
private sashDragState: ISashDragState | undefined;
private state: State = State.Idle;
@@ -262,7 +266,29 @@ export class SplitView extends Disposable {
return this.sashItems.map(s => s.sash);
}
constructor(container: HTMLElement, options: ISplitViewOptions = {}) {
private _startSnappingEnabled = true;
get startSnappingEnabled(): boolean { return this._startSnappingEnabled; }
set startSnappingEnabled(startSnappingEnabled: boolean) {
if (this._startSnappingEnabled === startSnappingEnabled) {
return;
}
this._startSnappingEnabled = startSnappingEnabled;
this.updateSashEnablement();
}
private _endSnappingEnabled = true;
get endSnappingEnabled(): boolean { return this._endSnappingEnabled; }
set endSnappingEnabled(endSnappingEnabled: boolean) {
if (this._endSnappingEnabled === endSnappingEnabled) {
return;
}
this._endSnappingEnabled = endSnappingEnabled;
this.updateSashEnablement();
}
constructor(container: HTMLElement, options: ISplitViewOptions<TLayoutContext> = {}) {
super();
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
@@ -305,11 +331,11 @@ export class SplitView extends Disposable {
}
}
addView(view: IView, size: number | Sizing, index = this.viewItems.length): void {
addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length): void {
this.doAddView(view, size, index, false);
}
removeView(index: number, sizing?: Sizing): IView {
removeView(index: number, sizing?: Sizing): IView<TLayoutContext> {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
@@ -401,10 +427,10 @@ export class SplitView extends Disposable {
return viewItem.cachedVisibleSize;
}
layout(size: number, orthogonalSize?: number): void {
layout(size: number, layoutContext?: TLayoutContext): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.orthogonalSize = orthogonalSize;
this.layoutContext = layoutContext;
if (!this.proportions) {
const indexes = range(this.viewItems.length);
@@ -430,6 +456,10 @@ export class SplitView extends Disposable {
}
private onSashStart({ sash, start, alt }: ISashEvent): void {
for (const item of this.viewItems) {
item.enabled = false;
}
const index = firstIndex(this.sashItems, item => item.sash === sash);
// This way, we can press Alt while we resize a sash, macOS style!
@@ -535,9 +565,13 @@ export class SplitView extends Disposable {
this._onDidSashChange.fire(index);
this.sashDragState!.disposable.dispose();
this.saveProportions();
for (const item of this.viewItems) {
item.enabled = true;
}
}
private onViewChange(item: ViewItem, size: number | undefined): void {
private onViewChange(item: ViewItem<TLayoutContext>, size: number | undefined): void {
const index = this.viewItems.indexOf(item);
if (index < 0 || index >= this.viewItems.length) {
@@ -584,7 +618,7 @@ export class SplitView extends Disposable {
}
distributeViewSizes(): void {
const flexibleViewItems: ViewItem[] = [];
const flexibleViewItems: ViewItem<TLayoutContext>[] = [];
let flexibleSize = 0;
for (const item of this.viewItems) {
@@ -615,7 +649,7 @@ export class SplitView extends Disposable {
return this.viewItems[index].size;
}
private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
private doAddView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
@@ -849,17 +883,19 @@ export class SplitView extends Disposable {
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
// Layout views
let position = 0;
let offset = 0;
for (const viewItem of this.viewItems) {
viewItem.layout(position, this.orthogonalSize);
position += viewItem.size;
viewItem.layout(offset, this.layoutContext);
offset += viewItem.size;
}
// Layout sashes
this.sashItems.forEach(item => item.sash.layout());
this.updateSashEnablement();
}
// Update sashes enablement
private updateSashEnablement(): void {
let previous = false;
const collapsesDown = this.viewItems.map(i => previous = (i.size - i.minimumSize > 0) || previous);
@@ -873,7 +909,12 @@ export class SplitView extends Disposable {
previous = false;
const expandsUp = reverseViews.map(i => previous = (i.maximumSize - i.size > 0) || previous).reverse();
this.sashItems.forEach(({ sash }, index) => {
let position = 0;
for (let index = 0; index < this.sashItems.length; index++) {
const { sash } = this.sashItems[index];
const viewItem = this.viewItems[index];
position += viewItem.size;
const min = !(collapsesDown[index] && expandsUp[index + 1]);
const max = !(expandsDown[index] && collapsesUp[index + 1]);
@@ -886,9 +927,9 @@ export class SplitView extends Disposable {
const snappedBefore = typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible;
const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;
if (snappedBefore && collapsesUp[index]) {
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
sash.state = SashState.Minimum;
} else if (snappedAfter && collapsesDown[index]) {
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
sash.state = SashState.Maximum;
} else {
sash.state = SashState.Disabled;
@@ -900,8 +941,7 @@ export class SplitView extends Disposable {
} else {
sash.state = SashState.Enabled;
}
// }
});
}
}
private getSashPosition(sash: Sash): number {