code-server/src/vs/workbench/browser/part.ts

184 lines
5.2 KiB
TypeScript
Raw Normal View History

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/part';
import { Component } from 'vs/workbench/common/component';
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
import { Dimension, size, IDimension } from 'vs/base/browser/dom';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ISerializableView, IViewSize } from 'vs/base/browser/ui/grid/grid';
import { Event, Emitter } from 'vs/base/common/event';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { assertIsDefined } from 'vs/base/common/types';
export interface IPartOptions {
hasTitle?: boolean;
borderWidth?: () => number;
}
export interface ILayoutContentResult {
titleSize: IDimension;
contentSize: IDimension;
}
/**
* Parts are layed out in the workbench and have their own layout that
* arranges an optional title and mandatory content area to show content.
*/
export abstract class Part extends Component implements ISerializableView {
private _dimension: Dimension | undefined;
get dimension(): Dimension | undefined { return this._dimension; }
protected _onDidVisibilityChange = this._register(new Emitter<boolean>());
readonly onDidVisibilityChange = this._onDidVisibilityChange.event;
private parent: HTMLElement | undefined;
private titleArea: HTMLElement | undefined;
private contentArea: HTMLElement | undefined;
private partLayout: PartLayout | undefined;
constructor(
id: string,
private options: IPartOptions,
themeService: IThemeService,
storageService: IStorageService,
protected readonly layoutService: IWorkbenchLayoutService
) {
super(id, themeService, storageService);
layoutService.registerPart(this);
}
protected onThemeChange(theme: IColorTheme): void {
// only call if our create() method has been called
if (this.parent) {
super.onThemeChange(theme);
}
}
updateStyles(): void {
super.updateStyles();
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Called to create title and content area of the part.
*/
create(parent: HTMLElement, options?: object): void {
this.parent = parent;
this.titleArea = this.createTitleArea(parent, options);
this.contentArea = this.createContentArea(parent, options);
this.partLayout = new PartLayout(this.options, this.contentArea);
this.updateStyles();
}
/**
* Returns the overall part container.
*/
getContainer(): HTMLElement | undefined {
return this.parent;
}
/**
* Subclasses override to provide a title area implementation.
*/
protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | undefined {
return undefined;
}
/**
* Returns the title area container.
*/
protected getTitleArea(): HTMLElement | undefined {
return this.titleArea;
}
/**
* Subclasses override to provide a content area implementation.
*/
protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | undefined {
return undefined;
}
/**
* Returns the content area container.
*/
protected getContentArea(): HTMLElement | undefined {
return this.contentArea;
}
/**
* Layout title and content area in the given dimension.
*/
protected layoutContents(width: number, height: number): ILayoutContentResult {
const partLayout = assertIsDefined(this.partLayout);
return partLayout.layout(width, height);
}
//#region ISerializableView
private _onDidChange = this._register(new Emitter<IViewSize | undefined>());
get onDidChange(): Event<IViewSize | undefined> { return this._onDidChange.event; }
element!: HTMLElement;
abstract minimumWidth: number;
abstract maximumWidth: number;
abstract minimumHeight: number;
abstract maximumHeight: number;
layout(width: number, height: number): void {
this._dimension = new Dimension(width, height);
}
setVisible(visible: boolean) {
this._onDidVisibilityChange.fire(visible);
}
abstract toJSON(): object;
//#endregion
}
class PartLayout {
private static readonly TITLE_HEIGHT = 35;
constructor(private options: IPartOptions, private contentArea: HTMLElement | undefined) { }
layout(width: number, height: number): ILayoutContentResult {
// Title Size: Width (Fill), Height (Variable)
let titleSize: Dimension;
if (this.options && this.options.hasTitle) {
titleSize = new Dimension(width, Math.min(height, PartLayout.TITLE_HEIGHT));
} else {
titleSize = new Dimension(0, 0);
}
let contentWidth = width;
if (this.options && typeof this.options.borderWidth === 'function') {
contentWidth -= this.options.borderWidth(); // adjust for border size
}
// Content Size: Width (Fill), Height (Variable)
const contentSize = new Dimension(contentWidth, height - titleSize.height);
// Content
if (this.contentArea) {
size(this.contentArea, contentSize.width, contentSize.height);
}
return { titleSize, contentSize };
}
}