mirror of
https://github.com/microsoft/vscode.git
synced 2026-10-07 01:08:40 +01:00
Merge branch 'panel-viewlet'
This commit is contained in:
commit
dbffd8ac94
24 files changed
+701
-1944
No files matched your search
@@ -83,4 +83,27 @@
|
||||
.monaco-panel-view .panel > .panel-body {
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
|
||||
.monaco-panel-view.animated .split-view-view {
|
||||
transition-duration: 0.15s;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-moz-transition-duration: 0.15s;
|
||||
transition-timing-function: ease-out;
|
||||
-webkit-transition-timing-function: ease-out;
|
||||
-moz-transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.monaco-panel-view.animated.vertical .split-view-view {
|
||||
transition-property: height;
|
||||
-webkit-transition-property: height;
|
||||
-moz-transition-property: height;
|
||||
}
|
||||
|
||||
.monaco-panel-view.animated.horizontal .split-view-view {
|
||||
transition-property: width;
|
||||
-webkit-transition-property: width;
|
||||
-moz-transition-property: width;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { $, append, addClass, removeClass, toggleClass, trackFocus } from 'vs/base/browser/dom';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
import { Color, RGBA } from 'vs/base/common/color';
|
||||
import { SplitView, IView } from './splitview2';
|
||||
import { SplitView, IView } from './splitview';
|
||||
|
||||
export interface IPanelOptions {
|
||||
ariaHeaderLabel?: string;
|
||||
@@ -34,20 +34,29 @@ export abstract class Panel implements IView {
|
||||
|
||||
private static HEADER_SIZE = 22;
|
||||
|
||||
private _expanded: boolean;
|
||||
protected _expanded: boolean;
|
||||
private expandedSize: number | undefined = undefined;
|
||||
private _headerVisible = true;
|
||||
private _onDidChange = new Emitter<void>();
|
||||
private _minimumBodySize: number;
|
||||
private _maximumBodySize: number;
|
||||
private ariaHeaderLabel: string;
|
||||
private styles: IPanelStyles | undefined = undefined;
|
||||
|
||||
private el: HTMLElement;
|
||||
private header: HTMLElement;
|
||||
protected disposables: IDisposable[] = [];
|
||||
|
||||
get draggable(): HTMLElement {
|
||||
private _onDidChange = new Emitter<number | undefined>();
|
||||
readonly onDidChange: Event<number | undefined> = this._onDidChange.event;
|
||||
|
||||
get draggableElement(): HTMLElement {
|
||||
return this.header;
|
||||
}
|
||||
|
||||
get dropTargetElement(): HTMLElement {
|
||||
return this.el;
|
||||
}
|
||||
|
||||
private _dropBackground: Color | undefined;
|
||||
get dropBackground(): Color | undefined {
|
||||
return this._dropBackground;
|
||||
@@ -77,7 +86,7 @@ export abstract class Panel implements IView {
|
||||
|
||||
get minimumSize(): number {
|
||||
const headerSize = this.headerSize;
|
||||
const expanded = !this.headerVisible || this.expanded;
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
const minimumBodySize = expanded ? this._minimumBodySize : 0;
|
||||
|
||||
return headerSize + minimumBodySize;
|
||||
@@ -85,34 +94,31 @@ export abstract class Panel implements IView {
|
||||
|
||||
get maximumSize(): number {
|
||||
const headerSize = this.headerSize;
|
||||
const expanded = !this.headerVisible || this.expanded;
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
const maximumBodySize = expanded ? this._maximumBodySize : 0;
|
||||
|
||||
return headerSize + maximumBodySize;
|
||||
}
|
||||
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
constructor(options: IPanelOptions = {}) {
|
||||
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.header = $('.panel-header');
|
||||
}
|
||||
|
||||
get expanded(): boolean {
|
||||
isExpanded(): boolean {
|
||||
return this._expanded;
|
||||
}
|
||||
|
||||
set expanded(expanded: boolean) {
|
||||
setExpanded(expanded: boolean): void {
|
||||
if (this._expanded === !!expanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._expanded = !!expanded;
|
||||
this.updateHeader();
|
||||
this._onDidChange.fire();
|
||||
this._onDidChange.fire(expanded ? this.expandedSize : undefined);
|
||||
}
|
||||
|
||||
get headerVisible(): boolean {
|
||||
@@ -130,9 +136,10 @@ export abstract class Panel implements IView {
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
const panel = append(container, $('.panel'));
|
||||
this.el = append(container, $('.panel'));
|
||||
|
||||
append(panel, this.header);
|
||||
this.header = $('.panel-header');
|
||||
append(this.el, this.header);
|
||||
this.header.setAttribute('tabindex', '0');
|
||||
this.header.setAttribute('role', 'toolbar');
|
||||
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
|
||||
@@ -148,16 +155,16 @@ export abstract class Panel implements IView {
|
||||
.map(e => new StandardKeyboardEvent(e));
|
||||
|
||||
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space)
|
||||
.event(() => this.expanded = !this.expanded, null, this.disposables);
|
||||
.event(() => this.setExpanded(!this.isExpanded()), null, this.disposables);
|
||||
|
||||
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow)
|
||||
.event(() => this.expanded = false, null, this.disposables);
|
||||
.event(() => this.setExpanded(false), null, this.disposables);
|
||||
|
||||
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow)
|
||||
.event(() => this.expanded = true, null, this.disposables);
|
||||
.event(() => this.setExpanded(true), null, this.disposables);
|
||||
|
||||
domEvent(this.header, 'click')
|
||||
(() => this.expanded = !this.expanded, null, this.disposables);
|
||||
(() => this.setExpanded(!this.isExpanded()), null, this.disposables);
|
||||
|
||||
// TODO@Joao move this down to panelview
|
||||
// onHeaderKeyDown.filter(e => e.keyCode === KeyCode.UpArrow)
|
||||
@@ -166,30 +173,42 @@ export abstract class Panel implements IView {
|
||||
// onHeaderKeyDown.filter(e => e.keyCode === KeyCode.DownArrow)
|
||||
// .event(focusNext, this, this.disposables);
|
||||
|
||||
const body = append(panel, $('.panel-body'));
|
||||
const body = append(this.el, $('.panel-body'));
|
||||
this.renderBody(body);
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0;
|
||||
this.layoutBody(size - headerSize);
|
||||
|
||||
if (this.isExpanded()) {
|
||||
this.expandedSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
style(styles: IPanelStyles): void {
|
||||
this.header.style.color = styles.headerForeground ? styles.headerForeground.toString() : null;
|
||||
this.header.style.backgroundColor = styles.headerBackground ? styles.headerBackground.toString() : null;
|
||||
this.header.style.borderTop = styles.headerHighContrastBorder ? `1px solid ${styles.headerHighContrastBorder}` : null;
|
||||
this._dropBackground = styles.dropBackground;
|
||||
this.styles = styles;
|
||||
|
||||
if (!this.header) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateHeader();
|
||||
}
|
||||
|
||||
private updateHeader(): void {
|
||||
const expanded = !this.headerVisible || this.expanded;
|
||||
protected updateHeader(): void {
|
||||
const expanded = !this.headerVisible || this.isExpanded();
|
||||
|
||||
this.header.style.height = `${this.headerSize}px`;
|
||||
this.header.style.lineHeight = `${this.headerSize}px`;
|
||||
toggleClass(this.header, 'hidden', !this.headerVisible);
|
||||
toggleClass(this.header, 'expanded', expanded);
|
||||
this.header.setAttribute('aria-expanded', String(expanded));
|
||||
|
||||
this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : null;
|
||||
this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : null;
|
||||
this.header.style.borderTop = this.styles.headerHighContrastBorder ? `1px solid ${this.styles.headerHighContrastBorder}` : null;
|
||||
this._dropBackground = this.styles.dropBackground;
|
||||
}
|
||||
|
||||
protected abstract renderHeader(container: HTMLElement): void;
|
||||
@@ -217,17 +236,18 @@ class PanelDraggable implements IDisposable {
|
||||
readonly onDidDrop = this._onDidDrop.event;
|
||||
|
||||
constructor(private panel: Panel, private context: IDndContext) {
|
||||
domEvent(panel.draggable, 'dragstart')(this.onDragStart, this, this.disposables);
|
||||
domEvent(panel.draggable, 'dragenter')(this.onDragEnter, this, this.disposables);
|
||||
domEvent(panel.draggable, 'dragleave')(this.onDragLeave, this, this.disposables);
|
||||
domEvent(panel.draggable, 'dragend')(this.onDragEnd, this, this.disposables);
|
||||
domEvent(panel.draggable, 'drop')(this.onDrop, this, this.disposables);
|
||||
panel.draggableElement.draggable = true;
|
||||
domEvent(panel.draggableElement, 'dragstart')(this.onDragStart, this, this.disposables);
|
||||
domEvent(panel.dropTargetElement, 'dragenter')(this.onDragEnter, this, this.disposables);
|
||||
domEvent(panel.dropTargetElement, 'dragleave')(this.onDragLeave, this, this.disposables);
|
||||
domEvent(panel.dropTargetElement, 'dragend')(this.onDragEnd, this, this.disposables);
|
||||
domEvent(panel.dropTargetElement, 'drop')(this.onDrop, this, this.disposables);
|
||||
}
|
||||
|
||||
private onDragStart(e: DragEvent): void {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
|
||||
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggable.textContent));
|
||||
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent));
|
||||
e.dataTransfer.setDragImage(dragImage, -10, -10);
|
||||
setTimeout(() => document.body.removeChild(dragImage), 0);
|
||||
|
||||
@@ -240,7 +260,7 @@ class PanelDraggable implements IDisposable {
|
||||
}
|
||||
|
||||
this.dragOverCounter++;
|
||||
this.renderHeader();
|
||||
this.render();
|
||||
}
|
||||
|
||||
private onDragLeave(e: DragEvent): void {
|
||||
@@ -251,7 +271,7 @@ class PanelDraggable implements IDisposable {
|
||||
this.dragOverCounter--;
|
||||
|
||||
if (this.dragOverCounter === 0) {
|
||||
this.renderHeader();
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +281,7 @@ class PanelDraggable implements IDisposable {
|
||||
}
|
||||
|
||||
this.dragOverCounter = 0;
|
||||
this.renderHeader();
|
||||
this.render();
|
||||
this.context.draggable = null;
|
||||
}
|
||||
|
||||
@@ -271,7 +291,7 @@ class PanelDraggable implements IDisposable {
|
||||
}
|
||||
|
||||
this.dragOverCounter = 0;
|
||||
this.renderHeader();
|
||||
this.render();
|
||||
|
||||
if (this.context.draggable !== this) {
|
||||
this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel });
|
||||
@@ -280,14 +300,14 @@ class PanelDraggable implements IDisposable {
|
||||
this.context.draggable = null;
|
||||
}
|
||||
|
||||
private renderHeader(): void {
|
||||
private render(): void {
|
||||
let backgroundColor: string = null;
|
||||
|
||||
if (this.dragOverCounter > 0) {
|
||||
backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString();
|
||||
}
|
||||
|
||||
this.panel.draggable.style.backgroundColor = backgroundColor;
|
||||
this.panel.dropTargetElement.style.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -326,16 +346,15 @@ export class PanelView implements IDisposable {
|
||||
const disposables: IDisposable[] = [];
|
||||
panel.onDidChange(this.setupAnimation, this, disposables);
|
||||
|
||||
const panelItem = { panel, disposable: combinedDisposable(disposables) };
|
||||
this.panelItems.splice(index, 0, panelItem);
|
||||
this.splitview.addView(panel, size, index);
|
||||
|
||||
if (this.dnd) {
|
||||
const draggable = new PanelDraggable(panel, this.dndContext);
|
||||
disposables.push(draggable);
|
||||
draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop, disposables);
|
||||
}
|
||||
|
||||
const panelItem = { panel, disposable: combinedDisposable(disposables) };
|
||||
|
||||
this.panelItems.splice(index, 0, panelItem);
|
||||
this.splitview.addView(panel, size, index);
|
||||
}
|
||||
|
||||
removePanel(panel: Panel): void {
|
||||
@@ -359,7 +378,7 @@ export class PanelView implements IDisposable {
|
||||
}
|
||||
|
||||
const [panelItem] = this.panelItems.splice(fromIndex, 1);
|
||||
this.panelItems.splice(toIndex < fromIndex ? toIndex : toIndex - 1, 0, panelItem);
|
||||
this.panelItems.splice(toIndex, 0, panelItem);
|
||||
|
||||
this.splitview.moveView(fromIndex, toIndex);
|
||||
}
|
||||
@@ -374,6 +393,16 @@ export class PanelView implements IDisposable {
|
||||
this.splitview.resizeView(index, size);
|
||||
}
|
||||
|
||||
getPanelSize(panel: Panel): number {
|
||||
const index = firstIndex(this.panelItems, item => item.panel === panel);
|
||||
|
||||
if (index === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.splitview.getViewSize(index);
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
this.splitview.layout(size);
|
||||
}
|
||||
|
||||
@@ -3,96 +3,21 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-split-view {
|
||||
.monaco-split-view2 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-split-view.vertical > .split-view-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-split-view.horizontal > .split-view-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view > .header {
|
||||
position: relative;
|
||||
line-height: 22px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
padding-left: 20px;
|
||||
.monaco-split-view2 > .split-view-view {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view > .header.hide {
|
||||
display: none;
|
||||
.monaco-split-view2.vertical > .split-view-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Bold font style does not go well with CJK fonts */
|
||||
.monaco-split-view:lang(zh-Hans) > .split-view-view > .header,
|
||||
.monaco-split-view:lang(zh-Hant) > .split-view-view > .header,
|
||||
.monaco-split-view:lang(ja) > .split-view-view > .header,
|
||||
.monaco-split-view:lang(ko) > .split-view-view > .header { font-weight: normal; }
|
||||
|
||||
.monaco-split-view > .split-view-view > .header.collapsible {
|
||||
cursor: pointer;
|
||||
.monaco-split-view2.horizontal > .split-view-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view > .header.collapsible {
|
||||
background-image: url('arrow-collapse.svg');
|
||||
background-position: 2px center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
|
||||
background-image: url('arrow-expand.svg');
|
||||
background-position: 2px center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible {
|
||||
background-image: url('arrow-collapse-dark.svg');
|
||||
}
|
||||
|
||||
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
|
||||
background-image: url('arrow-expand-dark.svg');
|
||||
background-position: 2px center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
|
||||
.monaco-split-view.animated > .split-view-view {
|
||||
transition-duration: 0.15s;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-moz-transition-duration: 0.15s;
|
||||
transition-timing-function: ease-out;
|
||||
-webkit-transition-timing-function: ease-out;
|
||||
-moz-transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.monaco-split-view.vertical.animated > .split-view-view {
|
||||
transition-property: height;
|
||||
-webkit-transition-property: height;
|
||||
-moz-transition-property: height;
|
||||
}
|
||||
|
||||
.monaco-split-view.horizontal.animated > .split-view-view {
|
||||
transition-property: width;
|
||||
-webkit-transition-property: width;
|
||||
-moz-transition-property: width;
|
||||
}
|
||||
|
||||
.hc-black .split-view-view .action-label {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.hc-black .split-view-view > .header .action-label:before {
|
||||
top: 4px !important;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,23 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-split-view2 {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-split-view2 > .split-view-view {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-split-view2.vertical > .split-view-view {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.monaco-split-view2.horizontal > .split-view-view {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./splitview2';
|
||||
import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { fromEventEmitter, mapEvent } from 'vs/base/common/event';
|
||||
import types = require('vs/base/common/types');
|
||||
import dom = require('vs/base/browser/dom');
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { range, firstIndex } from 'vs/base/common/arrays';
|
||||
import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash';
|
||||
export { Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
|
||||
export interface ISplitViewOptions {
|
||||
orientation?: Orientation; // default Orientation.VERTICAL
|
||||
}
|
||||
|
||||
export interface IView {
|
||||
readonly minimumSize: number;
|
||||
readonly maximumSize: number;
|
||||
readonly onDidChange: Event<void>;
|
||||
render(container: HTMLElement, orientation: Orientation): void;
|
||||
layout(size: number, orientation: Orientation): void;
|
||||
}
|
||||
|
||||
interface ISashEvent {
|
||||
sash: Sash;
|
||||
start: number;
|
||||
current: number;
|
||||
}
|
||||
|
||||
interface IViewItem {
|
||||
view: IView;
|
||||
size: number;
|
||||
container: HTMLElement;
|
||||
disposable: IDisposable;
|
||||
layout(): void;
|
||||
}
|
||||
|
||||
interface ISashItem {
|
||||
sash: Sash;
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
interface ISashDragState {
|
||||
index: number;
|
||||
start: number;
|
||||
sizes: number[];
|
||||
minDelta: number;
|
||||
maxDelta: number;
|
||||
}
|
||||
|
||||
export class SplitView implements IDisposable {
|
||||
|
||||
private orientation: Orientation;
|
||||
private el: HTMLElement;
|
||||
private size = 0;
|
||||
private contentSize = 0;
|
||||
private viewItems: IViewItem[] = [];
|
||||
private sashItems: ISashItem[] = [];
|
||||
private sashDragState: ISashDragState;
|
||||
|
||||
get length(): number {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
|
||||
constructor(private container: HTMLElement, options: ISplitViewOptions = {}) {
|
||||
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
|
||||
|
||||
this.el = document.createElement('div');
|
||||
dom.addClass(this.el, 'monaco-split-view2');
|
||||
dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
|
||||
container.appendChild(this.el);
|
||||
}
|
||||
|
||||
addView(view: IView, size: number, index = this.viewItems.length): void {
|
||||
// Add view
|
||||
const container = dom.$('.split-view-view');
|
||||
|
||||
if (this.viewItems.length === 1) {
|
||||
this.el.appendChild(container);
|
||||
} else {
|
||||
this.el.insertBefore(container, this.el.children.item(index));
|
||||
}
|
||||
|
||||
const onChangeDisposable = mapEvent(view.onDidChange, () => item)(this.onViewChange, this);
|
||||
const containerDisposable = toDisposable(() => this.el.removeChild(container));
|
||||
const disposable = combinedDisposable([onChangeDisposable, containerDisposable]);
|
||||
|
||||
const layoutContainer = this.orientation === Orientation.VERTICAL
|
||||
? size => item.container.style.height = `${item.size}px`
|
||||
: size => item.container.style.width = `${item.size}px`;
|
||||
|
||||
const layout = () => {
|
||||
layoutContainer(item.size);
|
||||
item.view.layout(item.size, this.orientation);
|
||||
};
|
||||
|
||||
size = Math.round(size);
|
||||
const item: IViewItem = { view, container, size, layout, disposable };
|
||||
this.viewItems.splice(index, 0, item);
|
||||
|
||||
// Add sash
|
||||
if (this.viewItems.length > 1) {
|
||||
const orientation = this.orientation === Orientation.VERTICAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
|
||||
const layoutProvider = this.orientation === Orientation.VERTICAL ? { getHorizontalSashTop: sash => this.getSashPosition(sash) } : { getVerticalSashLeft: sash => this.getSashPosition(sash) };
|
||||
const sash = new Sash(this.el, layoutProvider, { orientation });
|
||||
const sashEventMapper = this.orientation === Orientation.VERTICAL
|
||||
? (e: IBaseSashEvent) => ({ sash, start: e.startY, current: e.currentY })
|
||||
: (e: IBaseSashEvent) => ({ sash, start: e.startX, current: e.currentX });
|
||||
|
||||
const onStart = mapEvent(fromEventEmitter<IBaseSashEvent>(sash, 'start'), sashEventMapper);
|
||||
const onStartDisposable = onStart(this.onSashStart, this);
|
||||
const onChange = mapEvent(fromEventEmitter<IBaseSashEvent>(sash, 'change'), sashEventMapper);
|
||||
const onSashChangeDisposable = onChange(this.onSashChange, this);
|
||||
const disposable = combinedDisposable([onStartDisposable, onSashChangeDisposable, sash]);
|
||||
const sashItem: ISashItem = { sash, disposable };
|
||||
|
||||
this.sashItems.splice(index - 1, 0, sashItem);
|
||||
}
|
||||
|
||||
view.render(container, this.orientation);
|
||||
this.relayout();
|
||||
}
|
||||
|
||||
removeView(index: number): void {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove view
|
||||
const viewItem = this.viewItems.splice(index, 1)[0];
|
||||
viewItem.disposable.dispose();
|
||||
|
||||
// Remove sash
|
||||
if (this.viewItems.length >= 1) {
|
||||
const sashIndex = Math.max(index - 1, 0);
|
||||
const sashItem = this.sashItems.splice(sashIndex, 1)[0];
|
||||
sashItem.disposable.dispose();
|
||||
}
|
||||
|
||||
this.relayout();
|
||||
}
|
||||
|
||||
moveView(from: number, to: number): void {
|
||||
if (from < 0 || from >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (to < 0 || to >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (from === to) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewItem = this.viewItems.splice(from, 1)[0];
|
||||
this.viewItems.splice(to, 0, viewItem);
|
||||
this.layoutViews();
|
||||
}
|
||||
|
||||
private relayout(): void {
|
||||
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
this.resize(this.viewItems.length - 1, this.contentSize - contentSize);
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
this.size = size;
|
||||
this.resize(this.viewItems.length - 1, size - previousSize);
|
||||
}
|
||||
|
||||
private onSashStart({ sash, start }: ISashEvent): void {
|
||||
const index = firstIndex(this.sashItems, item => item.sash === sash);
|
||||
const sizes = this.viewItems.map(i => i.size);
|
||||
|
||||
const upIndexes = range(index, -1);
|
||||
const collapseUp = upIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
|
||||
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
|
||||
|
||||
const downIndexes = range(index + 1, this.viewItems.length);
|
||||
const collapseDown = downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0);
|
||||
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0);
|
||||
|
||||
const minDelta = -Math.min(collapseUp, expandDown);
|
||||
const maxDelta = Math.min(collapseDown, expandUp);
|
||||
|
||||
this.sashDragState = { start, index, sizes, minDelta, maxDelta };
|
||||
}
|
||||
|
||||
private onSashChange({ sash, current }: ISashEvent): void {
|
||||
const { index, start, sizes, minDelta, maxDelta } = this.sashDragState;
|
||||
const delta = clamp(current - start, minDelta, maxDelta);
|
||||
|
||||
this.resize(index, delta, sizes);
|
||||
}
|
||||
|
||||
private onViewChange(item: IViewItem): void {
|
||||
const index = this.viewItems.indexOf(item);
|
||||
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size = clamp(item.size, item.view.minimumSize, item.view.maximumSize);
|
||||
item.size = size;
|
||||
this.relayout();
|
||||
}
|
||||
|
||||
resizeView(index: number, size: number): void {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = this.viewItems[index];
|
||||
size = Math.round(size);
|
||||
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
|
||||
let delta = size - item.size;
|
||||
|
||||
if (delta !== 0 && index < this.viewItems.length - 1) {
|
||||
const downIndexes = range(index + 1, this.viewItems.length);
|
||||
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
|
||||
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
|
||||
const deltaDown = clamp(delta, -expandDown, collapseDown);
|
||||
|
||||
this.resize(index, deltaDown);
|
||||
delta -= deltaDown;
|
||||
}
|
||||
|
||||
if (delta !== 0 && index > 0) {
|
||||
const upIndexes = range(index - 1, -1);
|
||||
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
|
||||
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
|
||||
const deltaUp = clamp(-delta, -collapseUp, expandUp);
|
||||
|
||||
this.resize(index - 1, deltaUp);
|
||||
}
|
||||
}
|
||||
|
||||
private resize(index: number, delta: number, sizes = this.viewItems.map(i => i.size)): void {
|
||||
if (index < 0 || index >= this.viewItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delta !== 0) {
|
||||
const upIndexes = range(index, -1);
|
||||
const up = upIndexes.map(i => this.viewItems[i]);
|
||||
const upSizes = upIndexes.map(i => sizes[i]);
|
||||
const downIndexes = range(index + 1, this.viewItems.length);
|
||||
const down = downIndexes.map(i => this.viewItems[i]);
|
||||
const downSizes = downIndexes.map(i => sizes[i]);
|
||||
|
||||
for (let i = 0, deltaUp = delta; deltaUp !== 0 && i < up.length; i++) {
|
||||
const item = up[i];
|
||||
const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize);
|
||||
const viewDelta = size - upSizes[i];
|
||||
|
||||
deltaUp -= viewDelta;
|
||||
item.size = size;
|
||||
}
|
||||
|
||||
for (let i = 0, deltaDown = delta; deltaDown !== 0 && i < down.length; i++) {
|
||||
const item = down[i];
|
||||
const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize);
|
||||
const viewDelta = size - downSizes[i];
|
||||
|
||||
deltaDown += viewDelta;
|
||||
item.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
let emptyDelta = this.size - contentSize;
|
||||
|
||||
for (let i = this.viewItems.length - 1; emptyDelta > 0 && i >= 0; i--) {
|
||||
const item = this.viewItems[i];
|
||||
const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize);
|
||||
const viewDelta = size - item.size;
|
||||
|
||||
emptyDelta -= viewDelta;
|
||||
item.size = size;
|
||||
}
|
||||
|
||||
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
|
||||
|
||||
this.layoutViews();
|
||||
}
|
||||
|
||||
private layoutViews(): void {
|
||||
this.viewItems.forEach(item => item.layout());
|
||||
this.sashItems.forEach(item => item.sash.layout());
|
||||
|
||||
// Update sashes enablement
|
||||
let previous = false;
|
||||
const collapsesDown = this.viewItems.map(i => previous = (i.size - i.view.minimumSize > 0) || previous);
|
||||
|
||||
previous = false;
|
||||
const expandsDown = this.viewItems.map(i => previous = (i.view.maximumSize - i.size > 0) || previous);
|
||||
|
||||
const reverseViews = [...this.viewItems].reverse();
|
||||
previous = false;
|
||||
const collapsesUp = reverseViews.map(i => previous = (i.size - i.view.minimumSize > 0) || previous).reverse();
|
||||
|
||||
previous = false;
|
||||
const expandsUp = reverseViews.map(i => previous = (i.view.maximumSize - i.size > 0) || previous).reverse();
|
||||
|
||||
this.sashItems.forEach((s, i) => {
|
||||
if ((collapsesDown[i] && expandsUp[i + 1]) || (expandsDown[i] && collapsesUp[i + 1])) {
|
||||
s.sash.enable();
|
||||
} else {
|
||||
s.sash.disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getSashPosition(sash: Sash): number {
|
||||
let position = 0;
|
||||
|
||||
for (let i = 0; i < this.sashItems.length; i++) {
|
||||
position += this.viewItems[i].size;
|
||||
|
||||
if (this.sashItems[i].sash === sash) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.viewItems.forEach(i => i.disposable.dispose());
|
||||
this.viewItems = [];
|
||||
|
||||
this.sashItems.forEach(i => i.disposable.dispose());
|
||||
this.sashItems = [];
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { SplitView, IView, Orientation } from 'vs/base/browser/ui/splitview/splitview2';
|
||||
import { SplitView, IView, Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Sash } from 'vs/base/browser/ui/sash/sash';
|
||||
|
||||
class TestView implements IView {
|
||||
|
||||
private _onDidChange = new Emitter<void>();
|
||||
private _onDidChange = new Emitter<number | undefined>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
get minimumSize(): number { return this._minimumSize; }
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { PanelView, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
|
||||
import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
|
||||
|
||||
export interface IPanelColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
@@ -50,8 +50,8 @@ export abstract class ViewletPanel extends Panel {
|
||||
private _onDidFocus = new Emitter<void>();
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
|
||||
private actionRunner: IActionRunner;
|
||||
private toolbar: ToolBar;
|
||||
protected actionRunner: IActionRunner;
|
||||
protected toolbar: ToolBar;
|
||||
|
||||
constructor(
|
||||
readonly title: string,
|
||||
@@ -114,7 +114,7 @@ export abstract class ViewletPanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IViewsViewletOptions {
|
||||
export interface IViewsViewletOptions extends IPanelViewOptions {
|
||||
showHeaderInTitleWhenSingleView: boolean;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class PanelViewlet extends Viewlet {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
private options: Partial<IViewsViewletOptions>,
|
||||
private options: IViewsViewletOptions,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService
|
||||
) {
|
||||
@@ -146,7 +146,8 @@ export class PanelViewlet extends Viewlet {
|
||||
super.create(parent);
|
||||
|
||||
const container = parent.getHTMLElement();
|
||||
this.panelview = this._register(new PanelView(container));
|
||||
this.panelview = this._register(new PanelView(container, this.options));
|
||||
this.panelview.onDidDrop(({ from, to }) => this.movePanel(from as ViewletPanel, to as ViewletPanel));
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
@@ -241,9 +242,9 @@ export class PanelViewlet extends Viewlet {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const [panelItem] = this.panelItems.splice(fromIndex, 1);
|
||||
this.panelItems.splice(toIndex < fromIndex ? toIndex : toIndex - 1, 0, panelItem);
|
||||
this.panelItems.splice(toIndex, 0, panelItem);
|
||||
|
||||
this.panelview.movePanel(from, to);
|
||||
}
|
||||
|
||||
@@ -251,8 +252,13 @@ export class PanelViewlet extends Viewlet {
|
||||
this.panelview.resizePanel(panel, size);
|
||||
}
|
||||
|
||||
getPanelSize(panel: ViewletPanel): number {
|
||||
return this.panelview.getPanelSize(panel);
|
||||
}
|
||||
|
||||
private updateViewHeaders(): void {
|
||||
if (this.isSingleView()) {
|
||||
this.panelItems[0].panel.setExpanded(true);
|
||||
this.panelItems[0].panel.headerVisible = false;
|
||||
} else {
|
||||
this.panelItems.forEach(i => i.panel.headerVisible = true);
|
||||
|
||||
@@ -27,12 +27,11 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c
|
||||
import { ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ViewsRegistry } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { TreeItemCollapsibleState, ITreeItem, ITreeViewDataProvider, TreeViewItemHandleArg } from 'vs/workbench/common/views';
|
||||
|
||||
export class TreeView extends CollapsibleView {
|
||||
export class TreeView extends ViewsViewletPanel {
|
||||
|
||||
private menus: Menus;
|
||||
private viewFocusContext: IContextKey<boolean>;
|
||||
@@ -40,10 +39,8 @@ export class TreeView extends CollapsibleView {
|
||||
private treeInputPromise: TPromise<void>;
|
||||
|
||||
private dataProviderElementChangeListener: IDisposable;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@@ -55,12 +52,12 @@ export class TreeView extends CollapsibleView {
|
||||
@IExtensionService private extensionService: IExtensionService,
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: options.name, sizing: ViewSizing.Flexible, collapsed: options.collapsed === void 0 ? true : options.collapsed }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService);
|
||||
this.menus = this.instantiationService.createInstance(Menus, this.id);
|
||||
this.viewFocusContext = this.contextKeyService.createKey<boolean>(this.id, void 0);
|
||||
this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables);
|
||||
this.themeService.onThemeChange(() => this.tree.refresh() /* soft refresh */, this, this.disposables);
|
||||
if (!options.collapsed) {
|
||||
if (options.expanded) {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
@@ -79,9 +76,10 @@ export class TreeView extends CollapsibleView {
|
||||
this.setInput();
|
||||
}
|
||||
|
||||
protected changeState(state: CollapsibleState): void {
|
||||
super.changeState(state);
|
||||
if (state === CollapsibleState.EXPANDED) {
|
||||
setExpanded(expanded: boolean): void {
|
||||
super.setExpanded(expanded);
|
||||
|
||||
if (expanded) {
|
||||
this.activate();
|
||||
}
|
||||
}
|
||||
@@ -106,8 +104,8 @@ export class TreeView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(tree, [this.viewFocusContext]));
|
||||
this.disposables.push(attachListStyler(tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(tree, [this.viewFocusContext]));
|
||||
tree.addListener('selection', (event: any) => this.onSelection());
|
||||
return tree;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITreeViewDataProvider } from 'vs/workbench/common/views';
|
||||
import { IViewConstructorSignature } from 'vs/workbench/browser/parts/views/views';
|
||||
|
||||
export class ViewLocation {
|
||||
|
||||
@@ -39,7 +38,8 @@ export interface IViewDescriptor {
|
||||
|
||||
readonly location: ViewLocation;
|
||||
|
||||
readonly ctor: IViewConstructorSignature;
|
||||
// TODO do we really need this?!
|
||||
readonly ctor: any;
|
||||
|
||||
readonly when?: ContextKeyExpr;
|
||||
|
||||
|
||||
+173
-300
@@ -5,24 +5,19 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IThemable, attachStyler } from 'vs/platform/theme/common/styler';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { $, Dimension, Builder } from 'vs/base/browser/builder';
|
||||
import { Scope } from 'vs/workbench/common/memento';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
import { DelayedDragHandler } from 'vs/base/browser/dnd';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { AbstractCollapsibleView, CollapsibleState, IView as IBaseView, SplitView, ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -31,124 +26,58 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { PanelViewlet, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { IPanelOptions } from 'vs/base/browser/ui/splitview/panelview';
|
||||
|
||||
export interface IViewOptions {
|
||||
export interface IViewOptions extends IPanelOptions {
|
||||
id: string;
|
||||
name: string;
|
||||
actionRunner: IActionRunner;
|
||||
collapsed: boolean;
|
||||
}
|
||||
|
||||
export interface IViewConstructorSignature {
|
||||
new(initialSize: number, options: IViewOptions, ...services: { _serviceBrand: any; }[]): IViewletView;
|
||||
export interface IViewConstructorSignature<T extends ViewsViewletPanel> {
|
||||
new(options: IViewOptions, ...services: { _serviceBrand: any; }[]): T;
|
||||
}
|
||||
|
||||
export interface IViewletView extends IBaseView, IThemable {
|
||||
id: string;
|
||||
name: string;
|
||||
getHeaderElement(): HTMLElement;
|
||||
create(): TPromise<void>;
|
||||
setVisible(visible: boolean): TPromise<void>;
|
||||
isVisible(): boolean;
|
||||
getActions(): IAction[];
|
||||
getSecondaryActions(): IAction[];
|
||||
getActionItem(action: IAction): IActionItem;
|
||||
getActionsContext(): any;
|
||||
showHeader(): boolean;
|
||||
hideHeader(): boolean;
|
||||
focusBody(): void;
|
||||
isExpanded(): boolean;
|
||||
expand(): void;
|
||||
collapse(): void;
|
||||
getOptimalWidth(): number;
|
||||
shutdown(): void;
|
||||
}
|
||||
|
||||
export interface ICollapsibleViewOptions extends IViewOptions {
|
||||
ariaHeaderLabel?: string;
|
||||
sizing: ViewSizing;
|
||||
initialBodySize?: number;
|
||||
}
|
||||
|
||||
export abstract class CollapsibleView extends AbstractCollapsibleView implements IViewletView {
|
||||
export abstract class ViewsViewletPanel extends ViewletPanel {
|
||||
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
protected treeContainer: HTMLElement;
|
||||
|
||||
// TODO@sandeep why is tree here? isn't this coming only from TreeView
|
||||
protected tree: ITree;
|
||||
protected toDispose: IDisposable[];
|
||||
protected toolBar: ToolBar;
|
||||
protected actionRunner: IActionRunner;
|
||||
protected isDisposed: boolean;
|
||||
private _isVisible: boolean;
|
||||
private dragHandler: DelayedDragHandler;
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
options: ICollapsibleViewOptions,
|
||||
options: IViewOptions,
|
||||
protected keybindingService: IKeybindingService,
|
||||
protected contextMenuService: IContextMenuService
|
||||
) {
|
||||
super(initialSize, {
|
||||
ariaHeaderLabel: options.ariaHeaderLabel,
|
||||
sizing: options.sizing,
|
||||
bodySize: options.initialBodySize ? options.initialBodySize : 4 * 22,
|
||||
initialState: options.collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED,
|
||||
});
|
||||
super(options.name, options, keybindingService, contextMenuService);
|
||||
|
||||
this.id = options.id;
|
||||
this.name = options.name;
|
||||
this.actionRunner = options.actionRunner;
|
||||
this.toDispose = [];
|
||||
this._expanded = options.expanded;
|
||||
}
|
||||
|
||||
protected changeState(state: CollapsibleState): void {
|
||||
this.updateTreeVisibility(this.tree, state === CollapsibleState.EXPANDED);
|
||||
|
||||
super.changeState(state);
|
||||
setExpanded(expanded: boolean): void {
|
||||
this.updateTreeVisibility(this.tree, expanded);
|
||||
super.setExpanded(expanded);
|
||||
}
|
||||
|
||||
get draggableLabel(): string { return this.name; }
|
||||
|
||||
create(): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
getHeaderElement(): HTMLElement {
|
||||
return this.header;
|
||||
}
|
||||
|
||||
renderHeader(container: HTMLElement): void {
|
||||
|
||||
// Tool bar
|
||||
this.toolBar = new ToolBar($('div.actions').appendTo(container).getHTMLElement(), this.contextMenuService, {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionItemProvider: (action) => this.getActionItem(action),
|
||||
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.name),
|
||||
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
|
||||
});
|
||||
this.toolBar.actionRunner = this.actionRunner;
|
||||
this.updateActions();
|
||||
protected renderHeader(container: HTMLElement): void {
|
||||
super.renderHeader(container);
|
||||
|
||||
// Expand on drag over
|
||||
this.dragHandler = new DelayedDragHandler(container, () => {
|
||||
if (!this.isExpanded()) {
|
||||
this.expand();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected updateActions(): void {
|
||||
this.toolBar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
|
||||
this.toolBar.context = this.getActionsContext();
|
||||
this.dragHandler = new DelayedDragHandler(container, () => this.setExpanded(true));
|
||||
}
|
||||
|
||||
protected renderViewTree(container: HTMLElement): HTMLElement {
|
||||
const treeContainer = document.createElement('div');
|
||||
container.appendChild(treeContainer);
|
||||
|
||||
return treeContainer;
|
||||
}
|
||||
|
||||
@@ -163,13 +92,14 @@ export abstract class CollapsibleView extends AbstractCollapsibleView implements
|
||||
setVisible(visible: boolean): TPromise<void> {
|
||||
if (this._isVisible !== visible) {
|
||||
this._isVisible = visible;
|
||||
this.updateTreeVisibility(this.tree, visible && this.state === CollapsibleState.EXPANDED);
|
||||
this.updateTreeVisibility(this.tree, visible && this.isExpanded());
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
focusBody(): void {
|
||||
focus(): void {
|
||||
super.focus();
|
||||
this.focusTree();
|
||||
}
|
||||
|
||||
@@ -204,14 +134,18 @@ export abstract class CollapsibleView extends AbstractCollapsibleView implements
|
||||
return undefined;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
// Subclass to implement
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
create(): TPromise<void> {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
// Subclass to implement
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.isDisposed = true;
|
||||
this.treeContainer = null;
|
||||
@@ -224,12 +158,6 @@ export abstract class CollapsibleView extends AbstractCollapsibleView implements
|
||||
this.dragHandler.dispose();
|
||||
}
|
||||
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
|
||||
if (this.toolBar) {
|
||||
this.toolBar.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -278,15 +206,14 @@ export interface IViewState {
|
||||
order: number;
|
||||
}
|
||||
|
||||
export class ViewsViewlet extends Viewlet {
|
||||
export class ViewsViewlet extends PanelViewlet {
|
||||
|
||||
protected viewletContainer: HTMLElement;
|
||||
protected lastFocusedView: IViewletView;
|
||||
private splitView: SplitView;
|
||||
private viewHeaderContextMenuListeners: IDisposable[] = [];
|
||||
protected dimension: Dimension;
|
||||
private viewletSettings: object;
|
||||
private readonly viewsContextKeys: Set<string> = new Set<string>();
|
||||
|
||||
private viewsViewletPanels: ViewsViewletPanel[] = [];
|
||||
|
||||
protected viewsStates: Map<string, IViewState> = new Map<string, IViewState>();
|
||||
private areExtensionsReady: boolean = false;
|
||||
|
||||
@@ -300,63 +227,28 @@ export class ViewsViewlet extends Viewlet {
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService protected contextKeyService: IContextKeyService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IExtensionService extensionService: IExtensionService
|
||||
@IExtensionService protected extensionService: IExtensionService
|
||||
) {
|
||||
super(id, telemetryService, themeService);
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: true }, telemetryService, themeService);
|
||||
|
||||
this.viewletSettings = this.getMemento(storageService, Scope.WORKSPACE);
|
||||
}
|
||||
|
||||
async create(parent: Builder): TPromise<void> {
|
||||
super.create(parent);
|
||||
|
||||
this._register(ViewsRegistry.onViewsRegistered(this.onViewsRegistered, this));
|
||||
this._register(ViewsRegistry.onViewsDeregistered(this.onViewsDeregistered, this));
|
||||
this._register(contextKeyService.onDidChangeContext(keys => this.onContextChanged(keys)));
|
||||
this._register(this.contextKeyService.onDidChangeContext(keys => this.onContextChanged(keys)));
|
||||
|
||||
extensionService.onReady().then(() => {
|
||||
this.extensionService.onReady().then(() => {
|
||||
this.areExtensionsReady = true;
|
||||
this.onViewsUpdated();
|
||||
this.updateViews();
|
||||
// this.onViewsUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
create(parent: Builder): TPromise<void> {
|
||||
super.create(parent);
|
||||
|
||||
this.viewletContainer = DOM.append(parent.getHTMLElement(), DOM.$(''));
|
||||
this.splitView = this._register(new SplitView(this.viewletContainer, { canChangeOrderByDragAndDrop: true }));
|
||||
this.attachSplitViewStyler(this.splitView);
|
||||
this._register(this.splitView.onFocus((view: IViewletView) => this.lastFocusedView = view));
|
||||
this._register(this.splitView.onDidOrderChange(() => {
|
||||
const views = this.splitView.getViews<IViewletView>();
|
||||
for (let order = 0; order < views.length; order++) {
|
||||
this.viewsStates.get(views[order].id).order = order;
|
||||
}
|
||||
}));
|
||||
|
||||
return this.onViewsRegistered(ViewsRegistry.getViews(this.location))
|
||||
.then(() => {
|
||||
this.lastFocusedView = this.splitView.getViews<IViewletView>()[0];
|
||||
this.focus();
|
||||
});
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
let title = Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
|
||||
if (this.showHeaderInTitleArea() && this.splitView.getViews<IViewletView>()[0]) {
|
||||
title += ': ' + this.splitView.getViews<IViewletView>()[0].name;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
if (this.showHeaderInTitleArea() && this.splitView.getViews<IViewletView>()[0]) {
|
||||
return this.splitView.getViews<IViewletView>()[0].getActions();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
if (this.showHeaderInTitleArea() && this.splitView.getViews<IViewletView>()[0]) {
|
||||
return this.splitView.getViews<IViewletView>()[0].getSecondaryActions();
|
||||
}
|
||||
return [];
|
||||
await this.onViewsRegistered(ViewsRegistry.getViews(this.location));
|
||||
this.focus();
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
@@ -373,47 +265,43 @@ export class ViewsViewlet extends Viewlet {
|
||||
|
||||
setVisible(visible: boolean): TPromise<void> {
|
||||
return super.setVisible(visible)
|
||||
.then(() => TPromise.join(this.splitView.getViews<IViewletView>().filter(view => view.isVisible() !== visible)
|
||||
.then(() => TPromise.join(this.viewsViewletPanels.filter(view => view.isVisible() !== visible)
|
||||
.map((view) => view.setVisible(visible))))
|
||||
.then(() => void 0);
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
super.focus();
|
||||
|
||||
if (this.lastFocusedView) {
|
||||
this.lastFocusedView.focus();
|
||||
} else if (this.views.length > 0) {
|
||||
this.views[0].focus();
|
||||
}
|
||||
}
|
||||
private didLayout = false;
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
this.layoutViews();
|
||||
super.layout(dimension);
|
||||
|
||||
if (!this.didLayout) {
|
||||
this.didLayout = true;
|
||||
|
||||
for (const panel of this.viewsViewletPanels) {
|
||||
const viewState = this.viewsStates.get(panel.id);
|
||||
const size = viewState ? viewState.size : 200;
|
||||
this.resizePanel(panel, size);
|
||||
}
|
||||
}
|
||||
|
||||
for (const view of this.viewsViewletPanels) {
|
||||
let viewState = this.updateViewStateSize(view);
|
||||
this.viewsStates.set(view.id, viewState);
|
||||
}
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
const additionalMargin = 16;
|
||||
const optimalWidth = Math.max(...this.splitView.getViews<IViewletView>().map(view => view.getOptimalWidth() || 0));
|
||||
const optimalWidth = Math.max(...this.viewsViewletPanels.map(view => view.getOptimalWidth() || 0));
|
||||
return optimalWidth + additionalMargin;
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
this.splitView.getViews<IViewletView>().forEach((view) => view.shutdown());
|
||||
this.viewsViewletPanels.forEach((view) => view.shutdown());
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
private layoutViews(): void {
|
||||
if (this.splitView) {
|
||||
this.splitView.layout(this.dimension.height);
|
||||
for (const view of this.splitView.getViews<IViewletView>()) {
|
||||
let viewState = this.updateViewStateSize(view);
|
||||
this.viewsStates.set(view.id, viewState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleViewVisibility(id: string, visible?: boolean): void {
|
||||
const view = this.getView(id);
|
||||
let viewState = this.viewsStates.get(id);
|
||||
@@ -433,7 +321,7 @@ export class ViewsViewlet extends Viewlet {
|
||||
this.updateViews();
|
||||
}
|
||||
|
||||
private onViewsRegistered(views: IViewDescriptor[]): TPromise<IViewletView[]> {
|
||||
private onViewsRegistered(views: IViewDescriptor[]): TPromise<ViewsViewletPanel[]> {
|
||||
this.viewsContextKeys.clear();
|
||||
for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) {
|
||||
if (viewDescriptor.when) {
|
||||
@@ -446,7 +334,7 @@ export class ViewsViewlet extends Viewlet {
|
||||
return this.updateViews();
|
||||
}
|
||||
|
||||
private onViewsDeregistered(views: IViewDescriptor[]): TPromise<IViewletView[]> {
|
||||
private onViewsDeregistered(views: IViewDescriptor[]): TPromise<ViewsViewletPanel[]> {
|
||||
return this.updateViews(views);
|
||||
}
|
||||
|
||||
@@ -468,94 +356,103 @@ export class ViewsViewlet extends Viewlet {
|
||||
}
|
||||
}
|
||||
|
||||
protected updateViews(unregisteredViews: IViewDescriptor[] = []): TPromise<IViewletView[]> {
|
||||
if (this.splitView) {
|
||||
protected updateViews(unregisteredViews: IViewDescriptor[] = []): TPromise<ViewsViewletPanel[]> {
|
||||
const registeredViews = this.getViewDescriptorsFromRegistry();
|
||||
const [visible, toAdd, toRemove] = registeredViews.reduce<[IViewDescriptor[], IViewDescriptor[], IViewDescriptor[]]>((result, viewDescriptor) => {
|
||||
const isCurrentlyVisible = this.isCurrentlyVisible(viewDescriptor);
|
||||
const canBeVisible = this.canBeVisible(viewDescriptor);
|
||||
|
||||
const registeredViews = this.getViewDescriptorsFromRegistry();
|
||||
const [visible, toAdd, toRemove] = registeredViews.reduce<[IViewDescriptor[], IViewDescriptor[], IViewDescriptor[]]>((result, viewDescriptor) => {
|
||||
const isCurrentlyVisible = this.isCurrentlyVisible(viewDescriptor);
|
||||
const canBeVisible = this.canBeVisible(viewDescriptor);
|
||||
|
||||
if (canBeVisible) {
|
||||
result[0].push(viewDescriptor);
|
||||
}
|
||||
|
||||
if (!isCurrentlyVisible && canBeVisible) {
|
||||
result[1].push(viewDescriptor);
|
||||
}
|
||||
|
||||
if (isCurrentlyVisible && !canBeVisible) {
|
||||
result[2].push(viewDescriptor);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}, [[], [], unregisteredViews]);
|
||||
|
||||
const toCreate: IViewletView[] = [];
|
||||
|
||||
if (toAdd.length || toRemove.length) {
|
||||
for (const view of this.splitView.getViews<IViewletView>()) {
|
||||
let viewState = this.viewsStates.get(view.id);
|
||||
if (!viewState || typeof viewState.size === 'undefined' || view.size !== viewState.size || !view.isExpanded() !== viewState.collapsed) {
|
||||
viewState = this.updateViewStateSize(view);
|
||||
this.viewsStates.set(view.id, viewState);
|
||||
}
|
||||
}
|
||||
if (toRemove.length) {
|
||||
for (const viewDescriptor of toRemove) {
|
||||
let view = this.getView(viewDescriptor.id);
|
||||
this.splitView.removeView(view);
|
||||
if (this.lastFocusedView === view) {
|
||||
this.lastFocusedView = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const viewDescriptor of toAdd) {
|
||||
let viewState = this.viewsStates.get(viewDescriptor.id);
|
||||
let index = visible.indexOf(viewDescriptor);
|
||||
const view = this.createView(viewDescriptor,
|
||||
viewState ? viewState.size : this.getDefaultViewSize(),
|
||||
{
|
||||
id: viewDescriptor.id,
|
||||
name: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
collapsed: viewState ? viewState.collapsed : void 0,
|
||||
viewletSettings: this.viewletSettings
|
||||
});
|
||||
toCreate.push(view);
|
||||
|
||||
this.attachViewStyler(view);
|
||||
this.splitView.addView(view, index);
|
||||
}
|
||||
|
||||
return TPromise.join(toCreate.map(view => view.create()))
|
||||
.then(() => this.onViewsUpdated())
|
||||
.then(() => toCreate);
|
||||
if (canBeVisible) {
|
||||
result[0].push(viewDescriptor);
|
||||
}
|
||||
|
||||
if (!isCurrentlyVisible && canBeVisible) {
|
||||
result[1].push(viewDescriptor);
|
||||
}
|
||||
|
||||
if (isCurrentlyVisible && !canBeVisible) {
|
||||
result[2].push(viewDescriptor);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}, [[], [], unregisteredViews]);
|
||||
|
||||
const toCreate: ViewsViewletPanel[] = [];
|
||||
|
||||
if (toAdd.length || toRemove.length) {
|
||||
const panels = [...this.viewsViewletPanels];
|
||||
|
||||
for (const view of panels) {
|
||||
let viewState = this.viewsStates.get(view.id);
|
||||
if (!viewState || typeof viewState.size === 'undefined' || !view.isExpanded() !== viewState.collapsed) {
|
||||
viewState = this.updateViewStateSize(view);
|
||||
this.viewsStates.set(view.id, viewState);
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove.length) {
|
||||
for (const viewDescriptor of toRemove) {
|
||||
let view = this.getView(viewDescriptor.id);
|
||||
const viewState = this.updateViewStateSize(view);
|
||||
this.viewsStates.set(view.id, viewState);
|
||||
this.removePanel(view);
|
||||
this.viewsViewletPanels.splice(this.viewsViewletPanels.indexOf(view), 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (const viewDescriptor of toAdd) {
|
||||
let viewState = this.viewsStates.get(viewDescriptor.id);
|
||||
let index = visible.indexOf(viewDescriptor);
|
||||
const view = this.createView(viewDescriptor,
|
||||
{
|
||||
id: viewDescriptor.id,
|
||||
name: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
expanded: !(viewState ? viewState.collapsed : void 0),
|
||||
viewletSettings: this.viewletSettings
|
||||
});
|
||||
toCreate.push(view);
|
||||
|
||||
const size = viewState ? viewState.size : (viewDescriptor.size || 200);
|
||||
this.addPanel(view, size, index);
|
||||
this.viewsViewletPanels.splice(index, 0, view);
|
||||
}
|
||||
|
||||
return TPromise.join(toCreate.map(view => view.create()))
|
||||
.then(() => this.onViewsUpdated())
|
||||
.then(() => toCreate);
|
||||
}
|
||||
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
movePanel(from: ViewletPanel, to: ViewletPanel): void {
|
||||
const fromIndex = firstIndex(this.viewsViewletPanels, panel => panel === from);
|
||||
const toIndex = firstIndex(this.viewsViewletPanels, panel => panel === to);
|
||||
|
||||
if (fromIndex < 0 || fromIndex >= this.viewsViewletPanels.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toIndex < 0 || toIndex >= this.viewsViewletPanels.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.movePanel(from, to);
|
||||
|
||||
const [panel] = this.viewsViewletPanels.splice(fromIndex, 1);
|
||||
this.viewsViewletPanels.splice(toIndex, 0, panel);
|
||||
|
||||
for (let order = 0; order < this.viewsViewletPanels.length; order++) {
|
||||
this.viewsStates.get(this.viewsViewletPanels[order].id).order = order;
|
||||
}
|
||||
}
|
||||
|
||||
protected getDefaultViewSize(): number | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private attachViewStyler(widget: IThemable, options?: { noContrastBorder?: boolean }): IDisposable {
|
||||
return attachStyler(this.themeService, {
|
||||
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
|
||||
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
|
||||
headerHighContrastBorder: (options && options.noContrastBorder) ? null : contrastBorder
|
||||
}, widget);
|
||||
}
|
||||
|
||||
private attachSplitViewStyler(widget: IThemable): IDisposable {
|
||||
return attachStyler(this.themeService, {
|
||||
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
}, widget);
|
||||
}
|
||||
|
||||
private isCurrentlyVisible(viewDescriptor: IViewDescriptor): boolean {
|
||||
return !!this.getView(viewDescriptor.id);
|
||||
}
|
||||
@@ -569,31 +466,13 @@ export class ViewsViewlet extends Viewlet {
|
||||
}
|
||||
|
||||
private onViewsUpdated(): TPromise<void> {
|
||||
if (!this.splitView) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
if (this.showHeaderInTitleArea()) {
|
||||
if (this.splitView.getViews<IViewletView>()[0]) {
|
||||
this.splitView.getViews<IViewletView>()[0].hideHeader();
|
||||
if (!this.splitView.getViews<IViewletView>()[0].isExpanded()) {
|
||||
this.splitView.getViews<IViewletView>()[0].expand();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const view of this.splitView.getViews<IViewletView>()) {
|
||||
view.showHeader();
|
||||
}
|
||||
}
|
||||
|
||||
// Update title area since the title actions have changed.
|
||||
this.updateTitleArea();
|
||||
|
||||
this.viewHeaderContextMenuListeners = dispose(this.viewHeaderContextMenuListeners);
|
||||
|
||||
for (const viewDescriptor of this.getViewDescriptorsFromRegistry()) {
|
||||
const view = this.getView(viewDescriptor.id);
|
||||
|
||||
if (view) {
|
||||
this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.getHeaderElement(), DOM.EventType.CONTEXT_MENU, e => {
|
||||
this.viewHeaderContextMenuListeners.push(DOM.addDisposableListener(view.draggableElement, DOM.EventType.CONTEXT_MENU, e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (viewDescriptor.canToggleVisibility) {
|
||||
@@ -603,14 +482,10 @@ export class ViewsViewlet extends Viewlet {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.dimension) {
|
||||
this.layoutViews();
|
||||
}
|
||||
|
||||
return this.setVisible(this.isVisible());
|
||||
}
|
||||
|
||||
private onContextMenu(event: StandardMouseEvent, view: IViewletView): void {
|
||||
private onContextMenu(event: StandardMouseEvent, view: ViewsViewletPanel): void {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
@@ -630,7 +505,7 @@ export class ViewsViewlet extends Viewlet {
|
||||
if (!this.showHeaderInTitleWhenSingleView) {
|
||||
return false;
|
||||
}
|
||||
if (this.splitView.getViews<IViewletView>().length > 1) {
|
||||
if (this.viewsViewletPanels.length > 1) {
|
||||
return false;
|
||||
}
|
||||
if (ViewLocation.getContributedViewLocation(this.location.id) && !this.areExtensionsReady) {
|
||||
@@ -668,32 +543,30 @@ export class ViewsViewlet extends Viewlet {
|
||||
});
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IViewletView {
|
||||
return this.instantiationService.createInstance(viewDescriptor.ctor, initialSize, options);
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewsViewletPanel {
|
||||
return this.instantiationService.createInstance(viewDescriptor.ctor, options);
|
||||
}
|
||||
|
||||
protected get views(): IViewletView[] {
|
||||
return this.splitView ? this.splitView.getViews<IViewletView>() : [];
|
||||
protected get views(): ViewsViewletPanel[] {
|
||||
return this.viewsViewletPanels;
|
||||
}
|
||||
|
||||
protected getView(id: string): IViewletView {
|
||||
return this.splitView.getViews<IViewletView>().filter(view => view.id === id)[0];
|
||||
protected getView(id: string): ViewsViewletPanel {
|
||||
return this.viewsViewletPanels.filter(view => view.id === id)[0];
|
||||
}
|
||||
|
||||
private updateViewStateSize(view: IViewletView): IViewState {
|
||||
private updateViewStateSize(view: ViewsViewletPanel): IViewState {
|
||||
const currentState = this.viewsStates.get(view.id);
|
||||
const newViewState = this.createViewState(view);
|
||||
return currentState ? { ...currentState, collapsed: newViewState.collapsed, size: newViewState.size } : newViewState;
|
||||
}
|
||||
|
||||
protected createViewState(view: IViewletView): IViewState {
|
||||
const collapsed = !view.isExpanded();
|
||||
const size = collapsed && view instanceof CollapsibleView ? view.previousSize : view.size;
|
||||
protected createViewState(view: ViewsViewletPanel): IViewState {
|
||||
return {
|
||||
collapsed,
|
||||
size: size && size > 0 ? size : void 0,
|
||||
collapsed: !view.isExpanded(),
|
||||
size: this.getPanelSize(view),
|
||||
isHidden: false,
|
||||
order: this.splitView.getViews<IViewletView>().indexOf(view)
|
||||
order: this.viewsViewletPanels.indexOf(view)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -728,9 +601,9 @@ export class PersistentViewsViewlet extends ViewsViewlet {
|
||||
const registeredViewDescriptors = this.getViewDescriptorsFromRegistry();
|
||||
this.viewsStates.forEach((viewState, id) => {
|
||||
const view = this.getView(id);
|
||||
|
||||
if (view) {
|
||||
viewState = this.createViewState(view);
|
||||
viewsStates[id] = { size: viewState.size, collapsed: viewState.collapsed, isHidden: viewState.isHidden, order: viewState.order };
|
||||
viewsStates[id] = this.createViewState(view);
|
||||
} else {
|
||||
const viewDescriptor = registeredViewDescriptors.filter(v => v.id === id)[0];
|
||||
if (viewDescriptor) {
|
||||
@@ -9,7 +9,7 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { PersistentViewsViewlet } from 'vs/workbench/browser/parts/views/views';
|
||||
import { PersistentViewsViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, VIEWLET_ID, State } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { StartAction, ToggleReplAction, ConfigureAction } from 'vs/workbench/parts/debug/browser/debugActions';
|
||||
import { StartDebugActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems';
|
||||
@@ -49,8 +49,11 @@ export class DebugViewlet extends PersistentViewsViewlet {
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateTitleArea()));
|
||||
}
|
||||
|
||||
public create(parent: Builder): TPromise<void> {
|
||||
return super.create(parent).then(() => DOM.addClass(this.viewletContainer, 'debug-viewlet'));
|
||||
async create(parent: Builder): TPromise<void> {
|
||||
await super.create(parent);
|
||||
|
||||
const el = parent.getHTMLElement();
|
||||
DOM.addClass(el, 'debug-viewlet');
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
|
||||
@@ -15,9 +15,8 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IHighlightEvent, ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { CollapsibleState, ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IDebugService, State, IBreakpoint, IExpression, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED } from 'vs/workbench/parts/debug/common/debug';
|
||||
import { Expression, Variable, ExceptionBreakpoint, FunctionBreakpoint, Thread, StackFrame, Breakpoint, ThreadAndProcessIds } from 'vs/workbench/parts/debug/common/debugModel';
|
||||
import * as viewer from 'vs/workbench/parts/debug/electron-browser/debugViewer';
|
||||
@@ -42,7 +41,7 @@ function renderViewTree(container: HTMLElement): HTMLElement {
|
||||
const $ = builder.$;
|
||||
const twistiePixels = 20;
|
||||
|
||||
export class VariablesView extends CollapsibleView {
|
||||
export class VariablesView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'variablesview.memento';
|
||||
private onFocusStackFrameScheduler: RunOnceScheduler;
|
||||
@@ -50,7 +49,6 @@ export class VariablesView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@@ -61,7 +59,7 @@ export class VariablesView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), sizing: ViewSizing.Flexible, ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.variablesFocusedContext = CONTEXT_VARIABLES_FOCUSED.bindTo(contextKeyService);
|
||||
@@ -84,13 +82,6 @@ export class VariablesView extends CollapsibleView {
|
||||
}, 400);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-variables');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
@@ -106,17 +97,17 @@ export class VariablesView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.variablesFocusedContext]));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree, [this.variablesFocusedContext]));
|
||||
|
||||
const viewModel = this.debugService.getViewModel();
|
||||
|
||||
this.tree.setInput(viewModel);
|
||||
|
||||
const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, false, 'explorer-action collapse-explorer');
|
||||
this.toolBar.setActions(prepareActions([collapseAction]))();
|
||||
this.toolbar.setActions(prepareActions([collapseAction]))();
|
||||
|
||||
this.toDispose.push(viewModel.onDidFocusStackFrame(sf => {
|
||||
this.disposables.push(viewModel.onDidFocusStackFrame(sf => {
|
||||
// Refresh the tree immediately if it is not visible.
|
||||
// Otherwise postpone the refresh until user stops stepping.
|
||||
if (!this.tree.getContentHeight() || sf.explicit) {
|
||||
@@ -125,11 +116,11 @@ export class VariablesView extends CollapsibleView {
|
||||
this.onFocusStackFrameScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.debugService.onDidChangeState(state => {
|
||||
this.disposables.push(this.debugService.onDidChangeState(state => {
|
||||
collapseAction.enabled = state === State.Running || state === State.Stopped;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Variable)) {
|
||||
return;
|
||||
}
|
||||
@@ -146,12 +137,12 @@ export class VariablesView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[VariablesView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[VariablesView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class WatchExpressionsView extends CollapsibleView {
|
||||
export class WatchExpressionsView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'watchexpressionsview.memento';
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
@@ -160,7 +151,6 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@@ -170,13 +160,13 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('expressionsSection', "Expressions Section") }, keybindingService, contextMenuService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
// only expand when a new watch expression is added.
|
||||
if (we instanceof Expression) {
|
||||
this.expand();
|
||||
this.setExpanded(true);
|
||||
}
|
||||
}));
|
||||
this.watchExpressionsFocusedContext = CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(contextKeyService);
|
||||
@@ -188,13 +178,6 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
}, 50);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
dom.addClass(container, 'debug-watch');
|
||||
this.treeContainer = renderViewTree(container);
|
||||
@@ -212,24 +195,24 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.watchExpressionsFocusedContext]));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree, [this.watchExpressionsFocusedContext]));
|
||||
|
||||
this.tree.setInput(this.debugService.getModel());
|
||||
|
||||
const addWatchExpressionAction = this.instantiationService.createInstance(AddWatchExpressionAction, AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL);
|
||||
const collapseAction = this.instantiationService.createInstance(CollapseAction, this.tree, true, 'explorer-action collapse-explorer');
|
||||
const removeAllWatchExpressionsAction = this.instantiationService.createInstance(RemoveAllWatchExpressionsAction, RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL);
|
||||
this.toolBar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
|
||||
this.toolbar.setActions(prepareActions([addWatchExpressionAction, collapseAction, removeAllWatchExpressionsAction]))();
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeWatchExpressions(we => {
|
||||
if (!this.onWatchExpressionsUpdatedScheduler.isScheduled()) {
|
||||
this.onWatchExpressionsUpdatedScheduler.schedule();
|
||||
}
|
||||
this.toReveal = we;
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(expression => {
|
||||
if (!expression || !(expression instanceof Expression)) {
|
||||
return;
|
||||
}
|
||||
@@ -246,12 +229,12 @@ export class WatchExpressionsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[WatchExpressionsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[WatchExpressionsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class CallStackView extends CollapsibleView {
|
||||
export class CallStackView extends ViewsViewletPanel {
|
||||
|
||||
private static MEMENTO = 'callstackview.memento';
|
||||
private pauseMessage: builder.Builder;
|
||||
@@ -260,7 +243,6 @@ export class CallStackView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@@ -270,7 +252,7 @@ export class CallStackView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService);
|
||||
this.settings = options.viewletSettings;
|
||||
|
||||
// Create scheduler to prevent unnecessary flashing of tree when reacting to changes
|
||||
@@ -301,14 +283,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}, 50);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const title = $('div.debug-call-stack-title').appendTo(container);
|
||||
$('span.title').text(this.options.name).appendTo(title);
|
||||
protected renderHeaderTitle(container: HTMLElement): void {
|
||||
const title = $('.title.debug-call-stack-title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(title);
|
||||
this.pauseMessage = $('span.pause-message').appendTo(title);
|
||||
this.pauseMessage.hide();
|
||||
this.pauseMessageLabel = $('span.label').appendTo(this.pauseMessage);
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
@@ -328,10 +308,10 @@ export class CallStackView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree));
|
||||
|
||||
this.toDispose.push(this.tree.addListener('selection', event => {
|
||||
this.disposables.push(this.tree.addListener('selection', event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
const element = this.tree.getFocus();
|
||||
if (element instanceof ThreadAndProcessIds) {
|
||||
@@ -342,12 +322,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}
|
||||
}));
|
||||
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeCallStack(() => {
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeCallStack(() => {
|
||||
if (!this.onCallStackChangeScheduler.isScheduled()) {
|
||||
this.onCallStackChangeScheduler.schedule();
|
||||
}
|
||||
}));
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
|
||||
this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() =>
|
||||
this.updateTreeSelection().done(undefined, errors.onUnexpectedError)));
|
||||
|
||||
// Schedule the update of the call stack tree if the viewlet is opened after a session started #14684
|
||||
@@ -386,12 +366,12 @@ export class CallStackView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[CallStackView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[CallStackView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
export class BreakpointsView extends CollapsibleView {
|
||||
export class BreakpointsView extends ViewsViewletPanel {
|
||||
|
||||
private static MAX_VISIBLE_FILES = 9;
|
||||
private static MEMENTO = 'breakopintsview.memento';
|
||||
@@ -399,7 +379,6 @@ export class BreakpointsView extends CollapsibleView {
|
||||
private settings: any;
|
||||
|
||||
constructor(
|
||||
size: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IDebugService private debugService: IDebugService,
|
||||
@@ -409,23 +388,15 @@ export class BreakpointsView extends CollapsibleView {
|
||||
@IListService private listService: IListService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(size, {
|
||||
super({
|
||||
...(options as IViewOptions),
|
||||
ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section"),
|
||||
sizing: ViewSizing.Fixed,
|
||||
initialBodySize: BreakpointsView.getExpandedBodySize(debugService.getModel().getBreakpoints().length + debugService.getModel().getFunctionBreakpoints().length + debugService.getModel().getExceptionBreakpoints().length)
|
||||
ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section")
|
||||
}, keybindingService, contextMenuService);
|
||||
|
||||
this.minimumBodySize = this.maximumBodySize = 0;
|
||||
this.settings = options.viewletSettings;
|
||||
this.breakpointsFocusedContext = CONTEXT_BREAKPOINTS_FOCUSED.bindTo(contextKeyService);
|
||||
this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
$('span').text(this.options.name).appendTo(titleDiv);
|
||||
|
||||
super.renderHeader(container);
|
||||
this.disposables.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
|
||||
}
|
||||
|
||||
public renderBody(container: HTMLElement): void {
|
||||
@@ -472,10 +443,10 @@ export class BreakpointsView extends CollapsibleView {
|
||||
keyboardSupport: false
|
||||
});
|
||||
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.breakpointsFocusedContext]));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(this.listService.register(this.tree, [this.breakpointsFocusedContext]));
|
||||
|
||||
this.toDispose.push(this.tree.addListener('selection', event => {
|
||||
this.disposables.push(this.tree.addListener('selection', event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
const element = this.tree.getFocus();
|
||||
if (element instanceof Breakpoint) {
|
||||
@@ -488,7 +459,7 @@ export class BreakpointsView extends CollapsibleView {
|
||||
|
||||
this.tree.setInput(debugModel);
|
||||
|
||||
this.toDispose.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => {
|
||||
this.disposables.push(this.debugService.getViewModel().onDidSelectFunctionBreakpoint(fbp => {
|
||||
if (!fbp || !(fbp instanceof FunctionBreakpoint)) {
|
||||
return;
|
||||
}
|
||||
@@ -514,8 +485,9 @@ export class BreakpointsView extends CollapsibleView {
|
||||
|
||||
private onBreakpointsChange(): void {
|
||||
const model = this.debugService.getModel();
|
||||
this.setBodySize(BreakpointsView.getExpandedBodySize(
|
||||
model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length));
|
||||
|
||||
const bodySize = BreakpointsView.getExpandedBodySize(model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length);
|
||||
this.minimumBodySize = this.maximumBodySize = bodySize;
|
||||
|
||||
if (this.tree) {
|
||||
this.tree.refresh();
|
||||
@@ -527,7 +499,7 @@ export class BreakpointsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this.settings[BreakpointsView.MEMENTO] = (this.state === CollapsibleState.COLLAPSED);
|
||||
this.settings[BreakpointsView.MEMENTO] = !this.isExpanded();
|
||||
super.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ViewsRegistry, ViewLocation, IViewDescriptor } from 'vs/workbench/browser/parts/views/viewsRegistry';
|
||||
import { PersistentViewsViewlet, IViewletView } from 'vs/workbench/browser/parts/views/views';
|
||||
import { PersistentViewsViewlet, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IContextKeyService, ContextKeyExpr, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -136,7 +136,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('marketPlace', "Marketplace"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: ExtensionsListView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions')),
|
||||
size: 100
|
||||
};
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('installedExtensions', "Installed"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: InstalledExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.not('searchExtensions')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions')),
|
||||
size: 50
|
||||
};
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('searchInstalledExtensions', "Installed"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: InstalledExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.has('searchInstalledExtensions')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('searchInstalledExtensions')),
|
||||
size: 50
|
||||
};
|
||||
}
|
||||
@@ -169,13 +169,13 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
name: localize('recommendedExtensions', "Recommended"),
|
||||
location: ViewLocation.Extensions,
|
||||
ctor: RecommendedExtensionsView,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('extensionsViewletVisible'), ContextKeyExpr.not('searchExtensions')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions')),
|
||||
size: 50,
|
||||
canToggleVisibility: true
|
||||
};
|
||||
}
|
||||
|
||||
create(parent: Builder): TPromise<void> {
|
||||
async create(parent: Builder): TPromise<void> {
|
||||
parent.addClass('extensions-viewlet');
|
||||
this.root = parent.getHTMLElement();
|
||||
|
||||
@@ -204,14 +204,14 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
|
||||
this.onSearchChange = mapEvent(onSearchInput, e => e.target.value);
|
||||
|
||||
return this.extensionManagementService.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => {
|
||||
if (installed.length === 0) {
|
||||
this.searchBox.value = '@sort:installs';
|
||||
this.searchExtensionsContextKey.set(true);
|
||||
}
|
||||
return super.create(new Builder(this.extensionsBox));
|
||||
});
|
||||
await super.create(new Builder(this.extensionsBox));
|
||||
|
||||
const installed = await this.extensionManagementService.getInstalled(LocalExtensionType.User);
|
||||
|
||||
if (installed.length === 0) {
|
||||
this.searchBox.value = '@sort:installs';
|
||||
this.searchExtensionsContextKey.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
public updateStyles(): void {
|
||||
@@ -310,7 +310,7 @@ export class ExtensionsViewlet extends PersistentViewsViewlet implements IExtens
|
||||
await this.updateViews([], !!value);
|
||||
}
|
||||
|
||||
protected async updateViews(unregisteredViews: IViewDescriptor[] = [], showAll = false): TPromise<IViewletView[]> {
|
||||
protected async updateViews(unregisteredViews: IViewDescriptor[] = [], showAll = false): TPromise<ViewsViewletPanel[]> {
|
||||
const created = await super.updateViews();
|
||||
const toShow = showAll ? this.views : created;
|
||||
if (toShow.length) {
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { chain } from 'vs/base/common/event';
|
||||
import { isPromiseCanceledError, create as createError } from 'vs/base/common/errors';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { PagedModel, IPagedModel, mergePagers, IPager } from 'vs/base/common/paging';
|
||||
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
|
||||
import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
@@ -32,7 +31,7 @@ import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachListStyler, attachBadgeStyler } from 'vs/platform/theme/common/styler';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
@@ -41,17 +40,15 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
|
||||
|
||||
export class ExtensionsListView extends CollapsibleView {
|
||||
export class ExtensionsListView extends ViewsViewletPanel {
|
||||
|
||||
private messageBox: HTMLElement;
|
||||
private extensionsList: HTMLElement;
|
||||
private badge: CountBadge;
|
||||
|
||||
private list: PagedList<IExtension>;
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
private options: IViewletViewOptions,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@@ -70,7 +67,7 @@ export class ExtensionsListView extends CollapsibleView {
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
@IProgressService private progressService: IProgressService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: options.name, sizing: ViewSizing.Flexible, collapsed: !!options.collapsed, initialBodySize: 1 * 62 }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService);
|
||||
}
|
||||
|
||||
renderHeader(container: HTMLElement): void {
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .header {
|
||||
.extensions-viewlet > .extensions .panel-header {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .header > .title {
|
||||
.extensions-viewlet > .extensions .panel-header > .title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
}
|
||||
|
||||
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .name {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition } from 'vs/workbench/parts/files/common/files';
|
||||
import { PersistentViewsViewlet, IViewletView, IViewletViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { PersistentViewsViewlet, ViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||
import { ActionRunner, FileViewletState } from 'vs/workbench/parts/files/browser/views/explorerViewer';
|
||||
@@ -70,8 +70,11 @@ export class ExplorerViewlet extends PersistentViewsViewlet {
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.registerViews()));
|
||||
}
|
||||
|
||||
public create(parent: Builder): TPromise<void> {
|
||||
return super.create(parent).then(() => DOM.addClass(this.viewletContainer, 'explorer-viewlet'));
|
||||
async create(parent: Builder): TPromise<void> {
|
||||
await super.create(parent);
|
||||
|
||||
const el = parent.getHTMLElement();
|
||||
DOM.addClass(el, 'explorer-viewlet');
|
||||
}
|
||||
|
||||
private registerViews(): void {
|
||||
@@ -152,7 +155,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet {
|
||||
this.openEditorsVisibleContextKey.set(this.contextService.getWorkbenchState() === WorkbenchState.EMPTY || (<IFilesConfiguration>this.configurationService.getConfiguration()).explorer.openEditors.visible !== 0);
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IViewletView {
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewsViewletPanel {
|
||||
if (viewDescriptor.id === ExplorerView.ID) {
|
||||
// Create a delegating editor service for the explorer to be able to delay the refresh in the opened
|
||||
// editors view above. This is a workaround for being able to double click on a file to make it pinned
|
||||
@@ -192,9 +195,9 @@ export class ExplorerViewlet extends PersistentViewsViewlet {
|
||||
});
|
||||
|
||||
const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IWorkbenchEditorService, delegatingEditorService]));
|
||||
return explorerInstantiator.createInstance(ExplorerView, initialSize, <IExplorerViewOptions>{ ...options, viewletState: this.viewletState });
|
||||
return explorerInstantiator.createInstance(ExplorerView, <IExplorerViewOptions>{ ...options, viewletState: this.viewletState });
|
||||
}
|
||||
return super.createView(viewDescriptor, initialSize, options);
|
||||
return super.createView(viewDescriptor, options);
|
||||
}
|
||||
|
||||
public getExplorerView(): ExplorerView {
|
||||
@@ -218,28 +221,28 @@ export class ExplorerViewlet extends PersistentViewsViewlet {
|
||||
const hasOpenedEditors = !!this.editorGroupService.getStacksModel().activeGroup;
|
||||
|
||||
let openEditorsView = this.getOpenEditorsView();
|
||||
if (this.lastFocusedView && this.lastFocusedView.isExpanded() && this.hasSelectionOrFocus(this.lastFocusedView)) {
|
||||
if (this.lastFocusedView !== openEditorsView || hasOpenedEditors) {
|
||||
this.lastFocusedView.focusBody();
|
||||
if (this.lastFocusedPanel && this.lastFocusedPanel.isExpanded() && this.hasSelectionOrFocus(this.lastFocusedPanel as ViewsViewletPanel)) {
|
||||
if (this.lastFocusedPanel !== openEditorsView || hasOpenedEditors) {
|
||||
this.lastFocusedPanel.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasSelectionOrFocus(openEditorsView) && hasOpenedEditors) {
|
||||
return openEditorsView.focusBody();
|
||||
return openEditorsView.focus();
|
||||
}
|
||||
|
||||
let explorerView = this.getExplorerView();
|
||||
if (this.hasSelectionOrFocus(explorerView)) {
|
||||
return explorerView.focusBody();
|
||||
return explorerView.focus();
|
||||
}
|
||||
|
||||
if (openEditorsView && openEditorsView.isExpanded() && hasOpenedEditors) {
|
||||
return openEditorsView.focusBody(); // we have entries in the opened editors view to focus on
|
||||
return openEditorsView.focus(); // we have entries in the opened editors view to focus on
|
||||
}
|
||||
|
||||
if (explorerView && explorerView.isExpanded()) {
|
||||
return explorerView.focusBody();
|
||||
return explorerView.focus();
|
||||
}
|
||||
|
||||
let emptyView = this.getEmptyView();
|
||||
@@ -250,7 +253,7 @@ export class ExplorerViewlet extends PersistentViewsViewlet {
|
||||
super.focus();
|
||||
}
|
||||
|
||||
private hasSelectionOrFocus(view: IViewletView): boolean {
|
||||
private hasSelectionOrFocus(view: ViewsViewletPanel): boolean {
|
||||
if (!view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -497,7 +497,7 @@ export abstract class BaseGlobalNewAction extends Action {
|
||||
}
|
||||
|
||||
if (!explorerView.isExpanded()) {
|
||||
explorerView.expand();
|
||||
explorerView.setExpanded(true);
|
||||
}
|
||||
|
||||
const action = this.toDispose = this.instantiationService.createInstance(this.getAction(), explorerView.getViewer(), null);
|
||||
@@ -1716,7 +1716,7 @@ export class FocusOpenEditorsView extends Action {
|
||||
return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => {
|
||||
const openEditorsView = viewlet.getOpenEditorsView();
|
||||
if (openEditorsView) {
|
||||
openEditorsView.expand();
|
||||
openEditorsView.setExpanded(true);
|
||||
openEditorsView.getViewer().DOMFocus();
|
||||
}
|
||||
});
|
||||
@@ -1740,7 +1740,7 @@ export class FocusFilesExplorer extends Action {
|
||||
return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => {
|
||||
const view = viewlet.getExplorerView();
|
||||
if (view) {
|
||||
view.expand();
|
||||
view.setExpanded(true);
|
||||
view.getViewer().DOMFocus();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -97,13 +97,13 @@ export const revealInExplorerCommand = (accessor: ServicesAccessor, resource: UR
|
||||
if (isInsideWorkspace) {
|
||||
const explorerView = viewlet.getExplorerView();
|
||||
if (explorerView) {
|
||||
explorerView.expand();
|
||||
explorerView.setExpanded(true);
|
||||
explorerView.select(resource, true);
|
||||
}
|
||||
} else {
|
||||
const openEditorsView = viewlet.getOpenEditorsView();
|
||||
if (openEditorsView) {
|
||||
openEditorsView.expand();
|
||||
openEditorsView.setExpanded(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.explorer-viewlet .header .monaco-count-badge.hidden {
|
||||
.explorer-viewlet .panel-header .monaco-count-badge.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -13,16 +13,15 @@ import { IAction } from 'vs/base/common/actions';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export class EmptyView extends CollapsibleView {
|
||||
export class EmptyView extends ViewsViewletPanel {
|
||||
|
||||
public static ID: string = 'workbench.explorer.emptyView';
|
||||
public static NAME = nls.localize('noWorkspace', "No Folder Opened");
|
||||
@@ -30,14 +29,13 @@ export class EmptyView extends CollapsibleView {
|
||||
private openFolderButton: Button;
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
options: IViewletViewOptions,
|
||||
@IThemeService private themeService: IThemeService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
|
||||
@@ -27,7 +27,7 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -45,13 +45,12 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
export interface IExplorerViewOptions extends IViewletViewOptions {
|
||||
viewletState: FileViewletState;
|
||||
}
|
||||
|
||||
export class ExplorerView extends CollapsibleView {
|
||||
export class ExplorerView extends ViewsViewletPanel {
|
||||
|
||||
public static ID: string = 'workbench.explorer.fileView';
|
||||
private static EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first
|
||||
@@ -84,7 +83,6 @@ export class ExplorerView extends CollapsibleView {
|
||||
private settings: object;
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
options: IExplorerViewOptions,
|
||||
@IMessageService private messageService: IMessageService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@@ -102,11 +100,10 @@ export class ExplorerView extends CollapsibleView {
|
||||
@IWorkbenchThemeService private themeService: IWorkbenchThemeService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService
|
||||
) {
|
||||
super(initialSize, { ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section"), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
|
||||
super({ ...(options as IViewOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService);
|
||||
|
||||
this.settings = options.viewletSettings;
|
||||
this.viewletState = options.viewletState;
|
||||
this.actionRunner = options.actionRunner;
|
||||
this.autoReveal = true;
|
||||
|
||||
this.explorerRefreshDelayer = new ThrottledDelayer<void>(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY);
|
||||
@@ -128,18 +125,19 @@ export class ExplorerView extends CollapsibleView {
|
||||
return (configuration && configuration.files && configuration.files.exclude) || Object.create(null);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = $('div.title').appendTo(container);
|
||||
const titleSpan = $('span').appendTo(titleDiv);
|
||||
protected renderHeader(container: HTMLElement): void {
|
||||
super.renderHeader(container);
|
||||
|
||||
const titleElement = container.querySelector('.title') as HTMLElement;
|
||||
const setHeader = () => {
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
const title = workspace.folders.map(folder => folder.name).join();
|
||||
titleSpan.text(this.name).title(title);
|
||||
titleElement.textContent = this.name;
|
||||
titleElement.title = title;
|
||||
};
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceName(() => setHeader()));
|
||||
setHeader();
|
||||
|
||||
super.renderHeader(container);
|
||||
this.disposables.push(this.contextService.onDidChangeWorkspaceName(setHeader));
|
||||
setHeader();
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
@@ -157,16 +155,16 @@ export class ExplorerView extends CollapsibleView {
|
||||
|
||||
this.tree = this.createViewer($(this.treeContainer));
|
||||
|
||||
if (this.toolBar) {
|
||||
this.toolBar.setActions(prepareActions(this.getActions()), this.getSecondaryActions())();
|
||||
if (this.toolbar) {
|
||||
this.toolbar.setActions(prepareActions(this.getActions()), this.getSecondaryActions())();
|
||||
}
|
||||
|
||||
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
|
||||
DOM.toggleClass(this.treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
|
||||
};
|
||||
|
||||
this.toDispose.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange));
|
||||
this.toDispose.push(this.contextService.onDidChangeWorkspaceFolders((e) => this.refreshFromEvent(e.added)));
|
||||
this.disposables.push(this.themeService.onDidFileIconThemeChange(onFileIconThemeChange));
|
||||
this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added)));
|
||||
onFileIconThemeChange(this.themeService.getFileIconTheme());
|
||||
}
|
||||
|
||||
@@ -201,10 +199,10 @@ export class ExplorerView extends CollapsibleView {
|
||||
return this.doRefresh(targetsToExpand).then(() => {
|
||||
|
||||
// When the explorer viewer is loaded, listen to changes to the editor input
|
||||
this.toDispose.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
|
||||
this.disposables.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
|
||||
|
||||
// Also handle configuration updates
|
||||
this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IFilesConfiguration>(), true)));
|
||||
this.disposables.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IFilesConfiguration>(), true)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -276,7 +274,9 @@ export class ExplorerView extends CollapsibleView {
|
||||
}
|
||||
}
|
||||
|
||||
public focusBody(): void {
|
||||
public focus(): void {
|
||||
super.focus();
|
||||
|
||||
let keepFocus = false;
|
||||
|
||||
// Make sure the current selected element is revealed
|
||||
@@ -379,7 +379,7 @@ export class ExplorerView extends CollapsibleView {
|
||||
@memoize
|
||||
private get model(): Model {
|
||||
const model = this.instantiationService.createInstance(Model);
|
||||
this.toDispose.push(model);
|
||||
this.disposables.push(model);
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -389,9 +389,9 @@ export class ExplorerView extends CollapsibleView {
|
||||
const renderer = this.instantiationService.createInstance(FileRenderer, this.viewletState);
|
||||
const controller = this.instantiationService.createInstance(FileController, this.viewletState);
|
||||
const sorter = this.instantiationService.createInstance(FileSorter);
|
||||
this.toDispose.push(sorter);
|
||||
this.disposables.push(sorter);
|
||||
this.filter = this.instantiationService.createInstance(FileFilter);
|
||||
this.toDispose.push(this.filter);
|
||||
this.disposables.push(this.filter);
|
||||
const dnd = this.instantiationService.createInstance(FileDragAndDrop);
|
||||
const accessibilityProvider = this.instantiationService.createInstance(FileAccessibilityProvider);
|
||||
|
||||
@@ -412,23 +412,23 @@ export class ExplorerView extends CollapsibleView {
|
||||
});
|
||||
|
||||
// Theme styler
|
||||
this.toDispose.push(attachListStyler(this.explorerViewer, this.themeService));
|
||||
this.disposables.push(attachListStyler(this.explorerViewer, this.themeService));
|
||||
|
||||
// Register to list service
|
||||
this.toDispose.push(this.listService.register(this.explorerViewer, [this.explorerFocusedContext, this.filesExplorerFocusedContext]));
|
||||
this.disposables.push(this.listService.register(this.explorerViewer, [this.explorerFocusedContext, this.filesExplorerFocusedContext]));
|
||||
|
||||
// Update Viewer based on File Change Events
|
||||
this.toDispose.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
|
||||
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
|
||||
this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
|
||||
// Update resource context based on focused element
|
||||
this.toDispose.push(this.explorerViewer.addListener('focus', (e: { focus: FileStat }) => {
|
||||
this.disposables.push(this.explorerViewer.addListener('focus', (e: { focus: FileStat }) => {
|
||||
this.resourceContext.set(e.focus && e.focus.resource);
|
||||
this.folderContext.set(e.focus && e.focus.isDirectory);
|
||||
}));
|
||||
|
||||
// Open when selecting via keyboard
|
||||
this.toDispose.push(this.explorerViewer.addListener('selection', event => {
|
||||
this.disposables.push(this.explorerViewer.addListener('selection', event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
const element = this.tree.getSelection();
|
||||
|
||||
@@ -933,12 +933,4 @@ export class ExplorerView extends CollapsibleView {
|
||||
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this.toolBar) {
|
||||
this.toolBar.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { SaveAllAction } from 'vs/workbench/parts/files/browser/fileActions';
|
||||
import { CollapsibleView, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/views';
|
||||
import { ViewsViewletPanel, IViewletViewOptions, IViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IFilesConfiguration, VIEWLET_ID, OpenEditorsFocusedContext, ExplorerFocusedContext } from 'vs/workbench/parts/files/common/files';
|
||||
import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
@@ -33,11 +33,10 @@ import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
|
||||
import { attachListStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
export class OpenEditorsView extends CollapsibleView {
|
||||
export class OpenEditorsView extends ViewsViewletPanel {
|
||||
|
||||
private static DEFAULT_VISIBLE_OPEN_EDITORS = 9;
|
||||
private static DEFAULT_DYNAMIC_HEIGHT = true;
|
||||
@@ -58,7 +57,6 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
private explorerFocusedContext: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
initialSize: number,
|
||||
options: IViewletViewOptions,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@@ -72,11 +70,9 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
@IViewletService private viewletService: IViewletService,
|
||||
@IThemeService private themeService: IThemeService
|
||||
) {
|
||||
super(initialSize, {
|
||||
super({
|
||||
...(options as IViewOptions),
|
||||
ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"),
|
||||
sizing: ViewSizing.Fixed,
|
||||
initialBodySize: OpenEditorsView.computeExpandedBodySize(editorGroupService.getStacksModel())
|
||||
}, keybindingService, contextMenuService);
|
||||
|
||||
this.model = editorGroupService.getStacksModel();
|
||||
@@ -88,14 +84,14 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
this.structuralTreeRefreshScheduler = new RunOnceScheduler(() => this.structuralTreeUpdate(), this.structuralRefreshDelay);
|
||||
}
|
||||
|
||||
public renderHeader(container: HTMLElement): void {
|
||||
const titleDiv = dom.append(container, $('.title'));
|
||||
const titleSpan = dom.append(titleDiv, $('span'));
|
||||
titleSpan.textContent = this.name;
|
||||
protected renderHeaderTitle(container: HTMLElement): void {
|
||||
const title = dom.append(container, $('.title'));
|
||||
title.textContent = this.name;
|
||||
|
||||
this.dirtyCountElement = dom.append(titleDiv, $('.monaco-count-badge'));
|
||||
const count = dom.append(container, $('.count'));
|
||||
this.dirtyCountElement = dom.append(count, $('.monaco-count-badge'));
|
||||
|
||||
this.toDispose.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => {
|
||||
this.disposables.push((attachStylerCallback(this.themeService, { badgeBackground, badgeForeground, contrastBorder }, colors => {
|
||||
const background = colors.badgeBackground ? colors.badgeBackground.toString() : null;
|
||||
const foreground = colors.badgeForeground ? colors.badgeForeground.toString() : null;
|
||||
const border = colors.contrastBorder ? colors.contrastBorder.toString() : null;
|
||||
@@ -109,8 +105,6 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
})));
|
||||
|
||||
this.updateDirtyIndicator();
|
||||
|
||||
super.renderHeader(container);
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
@@ -148,20 +142,20 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
});
|
||||
|
||||
// Theme styler
|
||||
this.toDispose.push(attachListStyler(this.tree, this.themeService));
|
||||
this.disposables.push(attachListStyler(this.tree, this.themeService));
|
||||
|
||||
// Register to list service
|
||||
this.toDispose.push(this.listService.register(this.tree, [this.explorerFocusedContext, this.openEditorsFocusedContext]));
|
||||
this.disposables.push(this.listService.register(this.tree, [this.explorerFocusedContext, this.openEditorsFocusedContext]));
|
||||
|
||||
// Open when selecting via keyboard
|
||||
this.toDispose.push(this.tree.addListener('selection', event => {
|
||||
this.disposables.push(this.tree.addListener('selection', event => {
|
||||
if (event && event.payload && event.payload.origin === 'keyboard') {
|
||||
controller.openEditor(this.tree.getFocus(), { pinned: false, sideBySide: false, preserveFocus: false });
|
||||
}
|
||||
}));
|
||||
|
||||
// Prevent collapsing of editor groups
|
||||
this.toDispose.push(this.tree.addListener('item:collapsed', (event: IItemCollapseEvent) => {
|
||||
this.disposables.push(this.tree.addListener('item:collapsed', (event: IItemCollapseEvent) => {
|
||||
if (event.item && event.item.getElement() instanceof EditorGroup) {
|
||||
setTimeout(() => this.tree.expand(event.item.getElement())); // unwind from callback
|
||||
}
|
||||
@@ -186,20 +180,20 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
private registerListeners(): void {
|
||||
|
||||
// update on model changes
|
||||
this.toDispose.push(this.model.onModelChanged(e => this.onEditorStacksModelChanged(e)));
|
||||
this.disposables.push(this.model.onModelChanged(e => this.onEditorStacksModelChanged(e)));
|
||||
|
||||
// Also handle configuration updates
|
||||
this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IFilesConfiguration>())));
|
||||
this.disposables.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IFilesConfiguration>())));
|
||||
|
||||
// Handle dirty counter
|
||||
this.toDispose.push(this.untitledEditorService.onDidChangeDirty(e => this.updateDirtyIndicator()));
|
||||
this.toDispose.push(this.textFileService.models.onModelsDirty(e => this.updateDirtyIndicator()));
|
||||
this.toDispose.push(this.textFileService.models.onModelsSaved(e => this.updateDirtyIndicator()));
|
||||
this.toDispose.push(this.textFileService.models.onModelsSaveError(e => this.updateDirtyIndicator()));
|
||||
this.toDispose.push(this.textFileService.models.onModelsReverted(e => this.updateDirtyIndicator()));
|
||||
this.disposables.push(this.untitledEditorService.onDidChangeDirty(e => this.updateDirtyIndicator()));
|
||||
this.disposables.push(this.textFileService.models.onModelsDirty(e => this.updateDirtyIndicator()));
|
||||
this.disposables.push(this.textFileService.models.onModelsSaved(e => this.updateDirtyIndicator()));
|
||||
this.disposables.push(this.textFileService.models.onModelsSaveError(e => this.updateDirtyIndicator()));
|
||||
this.disposables.push(this.textFileService.models.onModelsReverted(e => this.updateDirtyIndicator()));
|
||||
|
||||
// We are not updating the tree while the viewlet is not visible. Thus refresh when viewlet becomes visible #6702
|
||||
this.toDispose.push(this.viewletService.onDidViewletOpen(viewlet => {
|
||||
this.disposables.push(this.viewletService.onDidViewletOpen(viewlet => {
|
||||
if (viewlet.getId() === VIEWLET_ID) {
|
||||
this.fullRefreshNeeded = true;
|
||||
this.structuralTreeUpdate();
|
||||
@@ -231,7 +225,7 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
|
||||
private structuralTreeUpdate(): void {
|
||||
// View size
|
||||
this.setBodySize(this.getExpandedBodySize(this.model));
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize(this.model);
|
||||
// Show groups only if there is more than 1 group
|
||||
const treeInput = this.model.groups.length === 1 ? this.model.groups[0] : this.model;
|
||||
// TODO@Isidor temporary workaround due to a partial tree refresh issue
|
||||
@@ -285,7 +279,7 @@ export class OpenEditorsView extends CollapsibleView {
|
||||
}
|
||||
|
||||
// Adjust expanded body size
|
||||
this.setBodySize(this.getExpandedBodySize(this.model));
|
||||
this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize(this.model);
|
||||
}
|
||||
|
||||
private updateDirtyIndicator(): void {
|
||||
|
||||
@@ -550,7 +550,7 @@ export class RepositoryPanel extends ViewletPanel {
|
||||
focus(): void {
|
||||
super.focus();
|
||||
|
||||
if (this.expanded) {
|
||||
if (this.isExpanded()) {
|
||||
this.inputBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user