mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 10:12:34 -05:00
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:
@@ -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;
|
||||
}
|
||||
101
src/vs/base/browser/ui/splitview/paneview.css
Normal file
101
src/vs/base/browser/ui/splitview/paneview.css
Normal 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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user