提交 1090fc63 编写于 作者: J Joao Moreno

wip: more panelview work, PanelViewlet

上级 a2964093
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-panel-view {
width: 100%;
height: 100%;
}
.monaco-panel-view .panel {
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.monaco-panel-view .panel > .panel-header {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
padding-left: 20px;
overflow: hidden;
}
/* Bold font style does not go well with CJK fonts */
.monaco-panel-view:lang(zh-Hans) .panel > .panel-header,
.monaco-panel-view:lang(zh-Hant) .panel > .panel-header,
.monaco-panel-view:lang(ja) .panel > .panel-header,
.monaco-panel-view:lang(ko) .panel > .panel-header {
font-weight: normal;
}
.monaco-panel-view .panel > .panel-header.hidden {
display: none;
}
.monaco-panel-view .panel > .panel-body {
overflow: hidden;
flex: 1;
}
.monaco-panel-view .panel > .panel-header {
cursor: pointer;
/* background: rgba(128, 128, 128, 0.2); */
}
.monaco-panel-view .panel > .panel-header {
background-image: url('arrow-collapse.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.monaco-panel-view .panel > .panel-header.expanded {
background-image: url('arrow-expand.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.vs-dark .monaco-panel-view .panel > .panel-header {
background-image: url('arrow-collapse-dark.svg');
}
.vs-dark .monaco-panel-view .panel > .panel-header.expanded {
background-image: url('arrow-expand-dark.svg');
}
\ No newline at end of file
......@@ -5,7 +5,7 @@
'use strict';
import 'vs/css!./splitview';
import 'vs/css!./panelview';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter, chain } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
......@@ -25,6 +25,9 @@ export interface IPanelOptions {
export interface IPanelStyles {
dropBackground?: Color;
headerForeground?: Color;
headerBackground?: Color;
headerHighContrastBorder?: Color;
}
export abstract class Panel implements IView {
......@@ -38,9 +41,18 @@ export abstract class Panel implements IView {
private _maximumBodySize: number;
private ariaHeaderLabel: string;
readonly header: HTMLElement;
private header: HTMLElement;
protected disposables: IDisposable[] = [];
get draggable(): HTMLElement {
return this.header;
}
private _dropBackground: Color | undefined;
get dropBackground(): Color | undefined {
return this._dropBackground;
}
get minimumBodySize(): number {
return this._minimumBodySize;
}
......@@ -59,8 +71,12 @@ export abstract class Panel implements IView {
this._onDidChange.fire();
}
private get headerSize(): number {
return this.headerVisible ? Panel.HEADER_SIZE : 0;
}
get minimumSize(): number {
const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0;
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.expanded;
const minimumBodySize = expanded ? this._minimumBodySize : 0;
......@@ -68,7 +84,7 @@ export abstract class Panel implements IView {
}
get maximumSize(): number {
const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0;
const headerSize = this.headerSize;
const expanded = !this.headerVisible || this.expanded;
const maximumBodySize = expanded ? this._maximumBodySize : 0;
......@@ -82,6 +98,7 @@ export abstract class Panel implements IView {
this.ariaHeaderLabel = options.ariaHeaderLabel || '';
this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 44;
this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY;
this.header = $('.panel-header');
}
get expanded(): boolean {
......@@ -94,7 +111,7 @@ export abstract class Panel implements IView {
}
this._expanded = !!expanded;
this.renderHeader();
this.updateHeader();
this._onDidChange.fire();
}
......@@ -108,20 +125,21 @@ export abstract class Panel implements IView {
}
this._headerVisible = !!visible;
this.renderHeader();
this.updateHeader();
this._onDidChange.fire();
}
render(container: HTMLElement): void {
const panel = append(container, $('.panel'));
const header = append(panel, $('.panel-header'));
header.setAttribute('tabindex', '0');
header.setAttribute('role', 'toolbar');
header.setAttribute('aria-label', this.ariaHeaderLabel);
this.renderHeader();
append(panel, this.header);
this.header.setAttribute('tabindex', '0');
this.header.setAttribute('role', 'toolbar');
this.header.setAttribute('aria-label', this.ariaHeaderLabel);
this.renderHeader(this.header);
this.updateHeader();
const onHeaderKeyDown = chain(domEvent(header, 'keydown'))
const onHeaderKeyDown = chain(domEvent(this.header, 'keydown'))
.map(e => new StandardKeyboardEvent(e));
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space)
......@@ -133,7 +151,7 @@ export abstract class Panel implements IView {
onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow)
.event(() => this.expanded = true, null, this.disposables);
domEvent(header, 'click')
domEvent(this.header, 'click')
(() => this.expanded = !this.expanded, null, this.disposables);
// TODO@Joao move this down to panelview
......@@ -152,18 +170,24 @@ export abstract class Panel implements IView {
this.layoutBody(size - headerSize);
}
focus(): void {
// TODO@joao what to do
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;
}
private renderHeader(): void {
private updateHeader(): void {
const expanded = !this.headerVisible || this.expanded;
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));
}
protected abstract renderHeader(container: HTMLElement): void;
protected abstract renderBody(container: HTMLElement): void;
protected abstract layoutBody(size: number): void;
......@@ -173,7 +197,6 @@ export abstract class Panel implements IView {
}
interface IDndContext {
dropBackground: Color | undefined;
draggable: PanelDraggable | null;
}
......@@ -189,17 +212,17 @@ class PanelDraggable implements IDisposable {
readonly onDidDrop = this._onDidDrop.event;
constructor(private panel: Panel, private context: IDndContext) {
domEvent(panel.header, 'dragstart')(this.onDragStart, this, this.disposables);
domEvent(panel.header, 'dragenter')(this.onDragEnter, this, this.disposables);
domEvent(panel.header, 'dragleave')(this.onDragLeave, this, this.disposables);
domEvent(panel.header, 'dragend')(this.onDragEnd, this, this.disposables);
domEvent(panel.header, 'drop')(this.onDrop, this, this.disposables);
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);
}
private onDragStart(e: DragEvent): void {
e.dataTransfer.effectAllowed = 'move';
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.header.textContent));
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggable.textContent));
e.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
......@@ -256,10 +279,10 @@ class PanelDraggable implements IDisposable {
let backgroundColor: string = null;
if (this.dragOverCounter > 0) {
backgroundColor = (this.context.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString();
backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString();
}
this.panel.header.style.backgroundColor = backgroundColor;
this.panel.draggable.style.backgroundColor = backgroundColor;
}
dispose(): void {
......@@ -279,7 +302,7 @@ interface IPanelItem {
export class PanelView implements IDisposable {
private dnd: boolean;
private dndContext: IDndContext = { dropBackground: undefined, draggable: null };
private dndContext: IDndContext = { draggable: null };
private el: HTMLElement;
private panelItems: IPanelItem[] = [];
private splitview: SplitView;
......@@ -288,10 +311,10 @@ export class PanelView implements IDisposable {
private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>();
readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event;
constructor(private container: HTMLElement, options?: IPanelViewOptions) {
constructor(private container: HTMLElement, options: IPanelViewOptions = {}) {
this.dnd = !!options.dnd;
this.el = append(container, $('.monaco-panel-view'));
this.splitview = new SplitView(container);
this.splitview = new SplitView(this.el);
}
addPanel(panel: Panel, size: number, index = this.splitview.length): void {
......@@ -337,10 +360,6 @@ export class PanelView implements IDisposable {
this.splitview.layout(size);
}
style(styles: IPanelStyles): void {
this.dndContext.dropBackground = styles.dropBackground;
}
private setupAnimation(): void {
if (typeof this.animationTimer === 'number') {
window.clearTimeout(this.animationTimer);
......
/*---------------------------------------------------------------------------------------------
* 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%;
}
......@@ -5,7 +5,7 @@
'use strict';
import 'vs/css!./splitview';
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');
......@@ -25,7 +25,6 @@ export interface IView {
readonly onDidChange: Event<void>;
render(container: HTMLElement, orientation: Orientation): void;
layout(size: number, orientation: Orientation): void;
focus(): void;
}
interface ISashEvent {
......@@ -80,7 +79,7 @@ export class SplitView implements IDisposable {
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
this.el = document.createElement('div');
dom.addClass(this.el, 'monaco-split-view');
dom.addClass(this.el, 'monaco-split-view2');
dom.addClass(this.el, this.orientation === Orientation.VERTICAL ? 'vertical' : 'horizontal');
container.appendChild(this.el);
}
......
......@@ -15,11 +15,15 @@ export interface IThemable {
style: styleFn;
}
export function attachStyler(themeService: IThemeService, optionsMapping: { [optionsKey: string]: ColorIdentifier | ColorFunction }, widgetOrCallback: IThemable | styleFn): IDisposable {
export interface IColorMapping {
[optionsKey: string]: ColorIdentifier | ColorFunction | undefined;
}
export function attachStyler<T extends IColorMapping>(themeService: IThemeService, optionsMapping: T, widgetOrCallback: IThemable | styleFn): IDisposable {
function applyStyles(theme: ITheme): void {
const styles = Object.create(null);
for (let key in optionsMapping) {
const value = optionsMapping[key];
const value = optionsMapping[key as string];
if (typeof value === 'string') {
styles[key] = theme.getColor(value);
} else if (typeof value === 'function') {
......
......@@ -6,7 +6,9 @@
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { attachStyler } from 'vs/platform/theme/common/styler';
import { ColorIdentifier, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler, IColorMapping, IThemable } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND } from 'vs/workbench/common/theme';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { append, $ } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
......@@ -21,9 +23,24 @@ 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 { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
import { PanelView, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
export interface IPanelColors extends IColorMapping {
dropBackground?: ColorIdentifier;
headerForeground?: ColorIdentifier;
headerBackground?: ColorIdentifier;
headerHighContrastBorder?: ColorIdentifier;
}
export function attachPanelStyler(widget: IThemable, themeService: IThemeService) {
return attachStyler<IPanelColors>(themeService, {
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
headerHighContrastBorder: contrastBorder,
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
}, widget);
}
export interface IViewletPanelOptions extends IPanelOptions {
actionRunner?: IActionRunner;
}
......@@ -37,28 +54,24 @@ export abstract class ViewletPanel extends Panel {
private toolbar: ToolBar;
constructor(
readonly name: string,
initialSize: number,
readonly title: string,
options: IViewletPanelOptions,
protected keybindingService: IKeybindingService,
protected contextMenuService: IContextMenuService
@IKeybindingService protected keybindingService: IKeybindingService,
@IContextMenuService protected contextMenuService: IContextMenuService
) {
super(options);
this.actionRunner = options.actionRunner;
}
render(container: HTMLElement): void {
super.render(container);
const title = append(this.header, $('div.title'));
title.textContent = this.name;
protected renderHeader(container: HTMLElement): void {
this.renderHeaderTitle(container);
const actions = append(this.header, $('div.actions'));
const actions = append(container, $('.actions'));
this.toolbar = new ToolBar(actions, this.contextMenuService, {
orientation: ActionsOrientation.HORIZONTAL,
actionItemProvider: action => this.getActionItem(action),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.name),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
actionRunner: this.actionRunner
});
......@@ -67,8 +80,11 @@ export abstract class ViewletPanel extends Panel {
this.updateActions();
}
protected renderHeaderTitle(container: HTMLElement): void {
append(container, $('.title', null, this.title));
}
focus(): void {
super.focus();
this._onDidFocus.fire();
}
......@@ -102,10 +118,6 @@ export interface IViewsViewletOptions {
showHeaderInTitleWhenSingleView: boolean;
}
const SplitViewThemeMapping = {
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
};
interface IViewletPanelItem {
panel: ViewletPanel;
disposable: IDisposable;
......@@ -139,15 +151,13 @@ export class PanelViewlet extends Viewlet {
const container = parent.getHTMLElement();
this.panelview = this._register(new PanelView(container));
this._register(attachStyler(this.themeService, SplitViewThemeMapping, this.panelview));
// this._register(this.panelview.onFocus(view => this.lastFocusedView = view));
}
getTitle(): string {
let title = Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
if (this.isSingleView) {
title += ': ' + this.panelItems[0].panel.name;
title += ': ' + this.panelItems[0].panel.title;
}
return title;
......@@ -190,20 +200,21 @@ export class PanelViewlet extends Viewlet {
return Math.max(...sizes);
}
addView(panel: ViewletPanel, index = this.panelItems.length - 1): void {
addPanel(panel: ViewletPanel, size: number, index = this.panelItems.length - 1): void {
const disposables: IDisposable[] = [];
const onDidFocus = panel.onDidFocus(() => this.lastFocusedPanel = panel, null, disposables);
const disposable = combinedDisposable([onDidFocus]);
const styler = attachPanelStyler(panel, this.themeService);
const disposable = combinedDisposable([onDidFocus, styler]);
const panelItem: IViewletPanelItem = { panel, disposable };
this.panelItems.splice(index, 0, panelItem);
this.panelview.addPanel(panel, 200, index);
this.panelview.addPanel(panel, size, index);
this.updateViewHeaders();
this.updateTitleArea();
}
removeView(panel: ViewletPanel): void {
removePanel(panel: ViewletPanel): void {
const index = firstIndex(this.panelItems, i => i.panel === panel);
if (index === -1) {
......
......@@ -13,7 +13,7 @@ import { basename } from 'vs/base/common/paths';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, dispose, combinedDisposable, empty as EmptyDisposable } from 'vs/base/common/lifecycle';
import { Builder } from 'vs/base/browser/builder';
import { PersistentViewsViewlet, CollapsibleView, IViewletViewOptions, IViewletView, IViewOptions } from 'vs/workbench/browser/parts/views/views';
import { PanelViewlet, ViewletPanel } from 'vs/workbench/browser/parts/views/views2';
import { append, $, toggleClass, trackFocus } from 'vs/base/browser/dom';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { List } from 'vs/base/browser/ui/list/listWidget';
......@@ -43,9 +43,7 @@ import Severity from 'vs/base/common/severity';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/browser/parts/views/viewsRegistry';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { ViewSizing } from 'vs/base/browser/ui/splitview/splitview';
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import * as platform from 'vs/base/common/platform';
......@@ -86,13 +84,6 @@ interface IViewModel {
toggleRepositoryVisibility(repository: ISCMRepository, visible: boolean);
}
class ProvidersViewDescriptor implements IViewDescriptor {
readonly id = 'providers';
readonly name = '';
readonly location = ViewLocation.SCM;
readonly ctor = null;
}
class ProvidersListDelegate implements IDelegate<ISCMRepository> {
getHeight(element: ISCMRepository): number {
......@@ -211,31 +202,19 @@ class ProviderRenderer implements IRenderer<ISCMRepository, RepositoryTemplateDa
}
}
class ProvidersView extends CollapsibleView {
class ProvidersPanel extends ViewletPanel {
private list: List<ISCMRepository>;
constructor(
initialSize: number,
protected viewModel: IViewModel,
options: IViewletViewOptions,
@IKeybindingService protected keybindingService: IKeybindingService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@ISCMService protected scmService: ISCMService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super(initialSize, {
...(options as IViewOptions),
sizing: ViewSizing.Fixed,
name: localize('scm providers', "Source Control Providers"),
}, keybindingService, contextMenuService);
}
renderHeader(container: HTMLElement): void {
const title = append(container, $('div.title'));
title.textContent = this.name;
super.renderHeader(container);
super(localize('scm providers', "Source Control Providers"), {}, keybindingService, contextMenuService);
this.updateBodySize();
}
protected renderBody(container: HTMLElement): void {
......@@ -243,35 +222,32 @@ class ProvidersView extends CollapsibleView {
const renderer = this.instantiationService.createInstance(ProviderRenderer, this.viewModel);
this.list = new List<ISCMRepository>(container, delegate, [renderer]);
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.toDispose);
this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.toDispose);
this.updateList();
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables);
this.update();
}
layoutBody(size: number): void {
if (!this.list) {
return;
}
protected layoutBody(size: number): void {
this.list.layout(size);
}
private updateList(): void {
this.list.splice(0, this.list.length, this.scmService.repositories);
}
private onDidAddRepository(repository: ISCMRepository): void {
this.updateList();
this.setBodySize(this.getExpandedBodySize());
this.update();
}
private onDidRemoveRepository(repository: ISCMRepository): void {
this.updateList();
this.setBodySize(this.getExpandedBodySize());
this.update();
}
private getExpandedBodySize(): number {
return Math.min(5, this.scmService.repositories.length) * 22;
private update(): void {
this.list.splice(0, this.list.length, this.scmService.repositories);
this.updateBodySize();
}
private updateBodySize(): void {
const size = Math.min(5, this.scmService.repositories.length) * 22;
this.minimumBodySize = size;
this.maximumBodySize = size;
}
}
......@@ -420,38 +396,38 @@ class ProviderListDelegate implements IDelegate<ISCMResourceGroup | ISCMResource
}
}
class ProviderViewDescriptor implements IViewDescriptor {
// class ProviderViewDescriptor implements IViewDescriptor {
// This ID magic needs to happen in order to preserve
// good splitview state when reloading the workbench
static idCount = 0;
static freeIds: string[] = [];
// // This ID magic needs to happen in order to preserve
// // good splitview state when reloading the workbench
// static idCount = 0;
// static freeIds: string[] = [];
readonly id: string;
// readonly id: string;
get repository(): ISCMRepository { return this._repository; }
get name(): string {
return this._repository.provider.rootUri
? `${basename(this._repository.provider.rootUri.fsPath)} (${this._repository.provider.label})`
: this._repository.provider.label;
}
get ctor(): any { return null; }
get location(): ViewLocation { return ViewLocation.SCM; }
// get repository(): ISCMRepository { return this._repository; }
// get name(): string {
// return this._repository.provider.rootUri
// ? `${basename(this._repository.provider.rootUri.fsPath)} (${this._repository.provider.label})`
// : this._repository.provider.label;
// }
// get ctor(): any { return null; }
// get location(): ViewLocation { return ViewLocation.SCM; }
constructor(private _repository: ISCMRepository) {
if (ProviderViewDescriptor.freeIds.length > 0) {
this.id = ProviderViewDescriptor.freeIds.shift();
} else {
this.id = `scm${ProviderViewDescriptor.idCount++}`;
}
}
// constructor(private _repository: ISCMRepository) {
// if (ProviderViewDescriptor.freeIds.length > 0) {
// this.id = ProviderViewDescriptor.freeIds.shift();
// } else {
// this.id = `scm${ProviderViewDescriptor.idCount++}`;
// }
// }
dispose(): void {
ProviderViewDescriptor.freeIds.push(this.id);
}
}
// dispose(): void {
// ProviderViewDescriptor.freeIds.push(this.id);
// }
// }
class ProviderView extends CollapsibleView {
class ProviderPanel extends ViewletPanel {
private cachedHeight: number | undefined;
private inputBoxContainer: HTMLElement;
......@@ -459,12 +435,9 @@ class ProviderView extends CollapsibleView {
private listContainer: HTMLElement;
private list: List<ISCMResourceGroup | ISCMResource>;
private menus: SCMMenus;
private disposables: IDisposable[] = [];
constructor(
initialSize: number,
private repository: ISCMRepository,
options: IViewletViewOptions,
@IKeybindingService protected keybindingService: IKeybindingService,
@IThemeService protected themeService: IThemeService,
@IContextMenuService protected contextMenuService: IContextMenuService,
......@@ -476,13 +449,13 @@ class ProviderView extends CollapsibleView {
@IEditorGroupService protected editorGroupService: IEditorGroupService,
@IInstantiationService protected instantiationService: IInstantiationService
) {
super(initialSize, { ...(options as IViewOptions), sizing: ViewSizing.Flexible }, keybindingService, contextMenuService);
super(repository.provider.label, {}, keybindingService, contextMenuService);
this.menus = instantiationService.createInstance(SCMMenus, repository.provider);
this.menus.onDidChangeTitle(this.updateActions, this, this.disposables);
}
renderHeader(container: HTMLElement): void {
protected renderHeaderTitle(container: HTMLElement): void {
const header = append(container, $('.title.scm-provider'));
const name = append(header, $('.name'));
const title = append(name, $('span.title'));
......@@ -495,11 +468,9 @@ class ProviderView extends CollapsibleView {
title.textContent = this.repository.provider.label;
type.textContent = '';
}
super.renderHeader(container);
}
renderBody(container: HTMLElement): void {
protected renderBody(container: HTMLElement): void {
const focusTracker = trackFocus(container);
this.disposables.push(focusTracker.addFocusListener(() => this.repository.focus()));
this.disposables.push(focusTracker);
......@@ -585,7 +556,9 @@ class ProviderView extends CollapsibleView {
}
focus(): void {
if (this.isExpanded()) {
super.focus();
if (this.expanded) {
this.inputBox.focus();
}
}
......@@ -697,16 +670,19 @@ class InstallAdditionalSCMProvidersAction extends Action {
}
}
export class SCMViewlet extends PersistentViewsViewlet {
export class SCMViewlet extends PanelViewlet {
private menus: SCMMenus;
private repositoryToViewDescriptor = new Map<string, ProviderViewDescriptor>();
private providersPanel: ProvidersPanel;
// private repositoryToViewDescriptor = new Map<string, ProviderViewDescriptor>();
private disposables: IDisposable[] = [];
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@ISCMService protected scmService: ISCMService,
@IInstantiationService instantiationService: IInstantiationService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IContextViewService protected contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService,
@IKeybindingService protected keybindingService: IKeybindingService,
......@@ -721,67 +697,56 @@ export class SCMViewlet extends PersistentViewsViewlet {
@IStorageService storageService: IStorageService,
@IExtensionService extensionService: IExtensionService
) {
super(VIEWLET_ID, ViewLocation.SCM, 'scm', false,
telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
super(VIEWLET_ID, { showHeaderInTitleWhenSingleView: false }, telemetryService, themeService);
this.menus = instantiationService.createInstance(SCMMenus, undefined);
this.menus.onDidChangeTitle(this.updateTitleArea, this, this.disposables);
}
private onDidAddRepository(repository: ISCMRepository): void {
const viewDescriptor = new ProviderViewDescriptor(repository);
this.repositoryToViewDescriptor.set(repository.provider.id, viewDescriptor);
async create(parent: Builder): TPromise<void> {
await super.create(parent);
ViewsRegistry.registerViews([viewDescriptor]);
toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0);
this.updateTitleArea();
}
parent.addClass('scm-viewlet'/* , 'empty' */);
// append(parent.getHTMLElement(), $('div.empty-message', null, localize('no open repo', "There are no source control providers active.")));
private onDidRemoveRepository(repository: ISCMRepository): void {
const viewDescriptor = this.repositoryToViewDescriptor.get(repository.provider.id);
this.repositoryToViewDescriptor.delete(repository.provider.id);
viewDescriptor.dispose();
this.providersPanel = this.instantiationService.createInstance(ProvidersPanel, this);
this.addPanel(this.providersPanel, this.providersPanel.minimumSize);
// this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
// this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables);
// this.scmService.repositories.forEach(p => this.onDidAddRepository(p));
ViewsRegistry.deregisterViews([viewDescriptor.id], ViewLocation.SCM);
toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0);
this.updateTitleArea();
// ViewsRegistry.registerViews([new ProvidersViewDescriptor()]);
}
async create(parent: Builder): TPromise<void> {
await super.create(parent);
// private onDidAddRepository(repository: ISCMRepository): void {
// const viewDescriptor = new ProviderViewDescriptor(repository);
// this.repositoryToViewDescriptor.set(repository.provider.id, viewDescriptor);
parent.addClass('scm-viewlet', 'empty');
append(parent.getHTMLElement(), $('div.empty-message', null, localize('no open repo', "There are no source control providers active.")));
// ViewsRegistry.registerViews([viewDescriptor]);
// toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0);
// this.updateTitleArea();
// }
this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);
this.scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables);
this.scmService.repositories.forEach(p => this.onDidAddRepository(p));
// private onDidRemoveRepository(repository: ISCMRepository): void {
// const viewDescriptor = this.repositoryToViewDescriptor.get(repository.provider.id);
// this.repositoryToViewDescriptor.delete(repository.provider.id);
// viewDescriptor.dispose();
ViewsRegistry.registerViews([new ProvidersViewDescriptor()]);
}
// ViewsRegistry.deregisterViews([viewDescriptor.id], ViewLocation.SCM);
// toggleClass(this.getContainer().getHTMLElement(), 'empty', this.views.length === 0);
// this.updateTitleArea();
// }
isRepositoryVisible(repository: ISCMRepository): boolean {
const view = this.repositoryToViewDescriptor.get(repository.provider.id);
return !!this.getView(view.id);
// const view = this.repositoryToViewDescriptor.get(repository.provider.id);
// return !!this.getView(view.id);
return true;
}
toggleRepositoryVisibility(repository: ISCMRepository, visible: boolean): void {
const view = this.repositoryToViewDescriptor.get(repository.provider.id);
this.toggleViewVisibility(view.id, visible);
}
protected createView(viewDescriptor: IViewDescriptor, initialSize: number, options: IViewletViewOptions): IViewletView {
if (viewDescriptor instanceof ProviderViewDescriptor) {
return this.instantiationService.createInstance(ProviderView, initialSize, viewDescriptor.repository, options);
} else if (viewDescriptor instanceof ProvidersViewDescriptor) {
return this.instantiationService.createInstance(ProvidersView, initialSize, this, options);
}
return this.instantiationService.createInstance(viewDescriptor.ctor, initialSize, options);
}
protected getDefaultViewSize(): number | undefined {
return this.dimension && this.dimension.height / Math.max(this.views.length, 1);
// const view = this.repositoryToViewDescriptor.get(repository.provider.id);
// this.toggleViewVisibility(view.id, visible);
}
getOptimalWidth(): number {
......@@ -790,20 +755,20 @@ export class SCMViewlet extends PersistentViewsViewlet {
getTitle(): string {
const title = localize('source control', "Source Control");
const views = ViewsRegistry.getViews(ViewLocation.SCM);
// const views = ViewsRegistry.getViews(ViewLocation.SCM);
if (views.length === 1) {
const view = views[0];
return localize('viewletTitle', "{0}: {1}", title, view.name);
} else {
return title;
}
// if (views.length === 1) {
// const view = views[0];
// return localize('viewletTitle', "{0}: {1}", title, view.name);
// } else {
return title;
// }
}
getActions(): IAction[] {
if (this.showHeaderInTitleArea() && this.views.length === 1) {
return this.views[0].getActions();
}
// if (this.isSingleView) {
// return this.views[0].getActions();
// }
return this.menus.getTitleActions();
}
......@@ -811,18 +776,18 @@ export class SCMViewlet extends PersistentViewsViewlet {
getSecondaryActions(): IAction[] {
let result: IAction[];
if (this.showHeaderInTitleArea() && this.views.length === 1) {
result = [
...this.views[0].getSecondaryActions(),
new Separator()
];
} else {
result = this.menus.getTitleSecondaryActions();
// if (this.isSingleView) {
// result = [
// ...this.views[0].getSecondaryActions(),
// new Separator()
// ];
// } else {
result = this.menus.getTitleSecondaryActions();
if (result.length > 0) {
result.push(new Separator());
}
if (result.length > 0) {
result.push(new Separator());
}
// }
result.push(this.instantiationService.createInstance(InstallAdditionalSCMProvidersAction));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册