Merge branch 'panel-viewlet'

This commit is contained in:
Joao Moreno committed 2017-09-25 14:40:42 +02:00
commit dbffd8ac94
24 files changed
+701 -1944

No files matched your search

+24 -1
View File
@@ -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;
}
+72 -43
View File
@@ -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);
}
+6 -81
View File
@@ -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;
@@ -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();
}
}