mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode a4177f50c475fc0fa278a78235e3bee9ffdec781 (#8649)
* Merge from vscode a4177f50c475fc0fa278a78235e3bee9ffdec781 * distro * fix tests
This commit is contained in:
@@ -39,6 +39,7 @@ export interface IDraggedResource {
|
||||
}
|
||||
|
||||
export class DraggedEditorIdentifier {
|
||||
|
||||
constructor(private _identifier: IEditorIdentifier) { }
|
||||
|
||||
get identifier(): IEditorIdentifier {
|
||||
@@ -47,6 +48,7 @@ export class DraggedEditorIdentifier {
|
||||
}
|
||||
|
||||
export class DraggedEditorGroupIdentifier {
|
||||
|
||||
constructor(private _identifier: GroupIdentifier) { }
|
||||
|
||||
get identifier(): GroupIdentifier {
|
||||
@@ -56,13 +58,17 @@ export class DraggedEditorGroupIdentifier {
|
||||
|
||||
export interface IDraggedEditor extends IDraggedResource {
|
||||
backupResource?: URI;
|
||||
encoding?: string;
|
||||
mode?: string;
|
||||
viewState?: IEditorViewState;
|
||||
}
|
||||
|
||||
export interface ISerializedDraggedEditor {
|
||||
resource: string;
|
||||
backupResource?: string;
|
||||
viewState: IEditorViewState | null;
|
||||
encoding?: string;
|
||||
mode?: string;
|
||||
viewState?: IEditorViewState;
|
||||
}
|
||||
|
||||
export const CodeDataTransfers = {
|
||||
@@ -86,7 +92,9 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
|
||||
resources.push({
|
||||
resource: URI.parse(draggedEditor.resource),
|
||||
backupResource: draggedEditor.backupResource ? URI.parse(draggedEditor.backupResource) : undefined,
|
||||
viewState: withNullAsUndefined(draggedEditor.viewState),
|
||||
viewState: draggedEditor.viewState,
|
||||
encoding: draggedEditor.encoding,
|
||||
mode: draggedEditor.mode,
|
||||
isExternal: false
|
||||
});
|
||||
});
|
||||
@@ -195,6 +203,8 @@ export class ResourcesDropHandler {
|
||||
|
||||
const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({
|
||||
resource: untitledOrFileResource.resource,
|
||||
encoding: (untitledOrFileResource as IDraggedEditor).encoding,
|
||||
mode: (untitledOrFileResource as IDraggedEditor).mode,
|
||||
options: {
|
||||
pinned: true,
|
||||
index: targetIndex,
|
||||
@@ -234,7 +244,7 @@ export class ResourcesDropHandler {
|
||||
|
||||
// Untitled: always ensure that we open a new untitled for each file we drop
|
||||
if (droppedDirtyEditor.resource.scheme === Schemas.untitled) {
|
||||
droppedDirtyEditor.resource = this.untitledTextEditorService.createOrGet().getResource();
|
||||
droppedDirtyEditor.resource = this.untitledTextEditorService.createOrGet(undefined, droppedDirtyEditor.mode, undefined, droppedDirtyEditor.encoding).getResource();
|
||||
}
|
||||
|
||||
// Return early if the resource is already dirty in target or opened already
|
||||
@@ -342,6 +352,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
|
||||
// Editors: enables cross window DND of tabs into the editor area
|
||||
const textFileService = accessor.get(ITextFileService);
|
||||
const untitledTextEditorService = accessor.get(IUntitledTextEditorService);
|
||||
const backupFileService = accessor.get(IBackupFileService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
@@ -349,24 +360,42 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
files.forEach(file => {
|
||||
|
||||
// Try to find editor view state from the visible editors that match given resource
|
||||
let viewState: IEditorViewState | null = null;
|
||||
let viewState: IEditorViewState | undefined = undefined;
|
||||
const textEditorWidgets = editorService.visibleTextEditorWidgets;
|
||||
for (const textEditorWidget of textEditorWidgets) {
|
||||
if (isCodeEditor(textEditorWidget)) {
|
||||
const model = textEditorWidget.getModel();
|
||||
if (model?.uri?.toString() === file.resource.toString()) {
|
||||
viewState = textEditorWidget.saveViewState();
|
||||
viewState = withNullAsUndefined(textEditorWidget.saveViewState());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find encoding and mode from text model
|
||||
let encoding: string | undefined = undefined;
|
||||
let mode: string | undefined = undefined;
|
||||
if (untitledTextEditorService.exists(file.resource)) {
|
||||
const model = untitledTextEditorService.createOrGet(file.resource);
|
||||
encoding = model.getEncoding();
|
||||
mode = model.getMode();
|
||||
} else {
|
||||
const model = textFileService.models.get(file.resource);
|
||||
if (model) {
|
||||
encoding = model.getEncoding();
|
||||
mode = model.textEditorModel?.getModeId();
|
||||
}
|
||||
}
|
||||
|
||||
// If the resource is dirty, send over its backup
|
||||
// resource to restore dirty state
|
||||
let backupResource: string | undefined = undefined;
|
||||
if (textFileService.isDirty(file.resource)) {
|
||||
backupResource = backupFileService.toBackupResource(file.resource).toString();
|
||||
}
|
||||
|
||||
// Add as dragged editor
|
||||
draggedEditors.push({
|
||||
resource: file.resource.toString(),
|
||||
backupResource: textFileService.isDirty(file.resource) ? backupFileService.toBackupResource(file.resource).toString() : undefined,
|
||||
viewState
|
||||
});
|
||||
draggedEditors.push({ resource: file.resource.toString(), backupResource, viewState, encoding, mode });
|
||||
});
|
||||
|
||||
if (draggedEditors.length) {
|
||||
|
||||
@@ -109,18 +109,18 @@ body.web {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench.monaco-font-aliasing-antialiased {
|
||||
.monaco-workbench.mac.monaco-font-aliasing-antialiased {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.monaco-workbench.monaco-font-aliasing-none {
|
||||
.monaco-workbench.mac.monaco-font-aliasing-none {
|
||||
-webkit-font-smoothing: none;
|
||||
-moz-osx-font-smoothing: unset;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
|
||||
.monaco-workbench.monaco-font-aliasing-auto {
|
||||
.monaco-workbench.mac.monaco-font-aliasing-auto {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
85
src/vs/workbench/browser/panecomposite.ts
Normal file
85
src/vs/workbench/browser/panecomposite.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
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 { IView } from 'vs/workbench/common/views';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Composite } from 'vs/workbench/browser/composite';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ViewPaneContainer } from './parts/views/viewPaneContainer';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { IAction, IActionViewItem } from 'vs/base/common/actions';
|
||||
|
||||
export class PaneComposite extends Composite implements IPaneComposite {
|
||||
constructor(id: string,
|
||||
protected readonly viewPaneContainer: ViewPaneContainer,
|
||||
@ITelemetryService
|
||||
telemetryService: ITelemetryService,
|
||||
@IStorageService
|
||||
protected storageService: IStorageService,
|
||||
@IInstantiationService
|
||||
protected instantiationService: IInstantiationService,
|
||||
@IThemeService
|
||||
themeService: IThemeService,
|
||||
@IContextMenuService
|
||||
protected contextMenuService: IContextMenuService,
|
||||
@IExtensionService
|
||||
protected extensionService: IExtensionService,
|
||||
@IWorkspaceContextService
|
||||
protected contextService: IWorkspaceContextService) {
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
|
||||
this._register(this.viewPaneContainer.onTitleAreaUpdate(() => this.updateTitleArea()));
|
||||
}
|
||||
create(parent: HTMLElement): void {
|
||||
this.viewPaneContainer.create(parent);
|
||||
}
|
||||
setVisible(visible: boolean): void {
|
||||
super.setVisible(visible);
|
||||
this.viewPaneContainer.setVisible(visible);
|
||||
}
|
||||
layout(dimension: Dimension): void {
|
||||
this.viewPaneContainer.layout(dimension);
|
||||
}
|
||||
getOptimalWidth(): number {
|
||||
return this.viewPaneContainer.getOptimalWidth();
|
||||
}
|
||||
openView(id: string, focus?: boolean): IView {
|
||||
return this.viewPaneContainer.openView(id, focus);
|
||||
}
|
||||
|
||||
getViewPaneContainer(): ViewPaneContainer {
|
||||
return this.viewPaneContainer;
|
||||
}
|
||||
|
||||
getContextMenuActions(): ReadonlyArray<IAction> {
|
||||
return this.viewPaneContainer.getContextMenuActions();
|
||||
}
|
||||
|
||||
getActions(): ReadonlyArray<IAction> {
|
||||
return this.viewPaneContainer.getActions();
|
||||
}
|
||||
|
||||
getSecondaryActions(): ReadonlyArray<IAction> {
|
||||
return this.viewPaneContainer.getSecondaryActions();
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
return this.viewPaneContainer.getActionViewItem(action);
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
return this.viewPaneContainer.getTitle();
|
||||
}
|
||||
|
||||
saveState(): void {
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/binaryeditor';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
@@ -12,11 +13,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Dimension, size, clearNode } from 'vs/base/browser/dom';
|
||||
import { Dimension, size, clearNode, append, addDisposableListener, EventType, $ } from 'vs/base/browser/dom';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||
@@ -31,11 +31,11 @@ export interface IOpenCallbacks {
|
||||
*/
|
||||
export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
private readonly _onMetadataChanged: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onMetadataChanged: Event<void> = this._onMetadataChanged.event;
|
||||
private readonly _onMetadataChanged = this._register(new Emitter<void>());
|
||||
readonly onMetadataChanged = this._onMetadataChanged.event;
|
||||
|
||||
private readonly _onDidOpenInPlace: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidOpenInPlace: Event<void> = this._onDidOpenInPlace.event;
|
||||
private readonly _onDidOpenInPlace = this._register(new Emitter<void>());
|
||||
readonly onDidOpenInPlace = this._onDidOpenInPlace.event;
|
||||
|
||||
private callbacks: IOpenCallbacks;
|
||||
private metadata: string | undefined;
|
||||
@@ -160,3 +160,122 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
export interface IResourceDescriptor {
|
||||
readonly resource: URI;
|
||||
readonly name: string;
|
||||
readonly size?: number;
|
||||
readonly etag?: string;
|
||||
readonly mime: string;
|
||||
}
|
||||
|
||||
class BinarySize {
|
||||
static readonly KB = 1024;
|
||||
static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
|
||||
static formatSize(size: number): string {
|
||||
if (size < BinarySize.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
}
|
||||
|
||||
if (size < BinarySize.MB) {
|
||||
return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.GB) {
|
||||
return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.TB) {
|
||||
return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2));
|
||||
}
|
||||
|
||||
return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
interface ResourceViewerContext extends IDisposable {
|
||||
layout?(dimension: Dimension): void;
|
||||
}
|
||||
|
||||
interface ResourceViewerDelegate {
|
||||
openInternalClb(uri: URI): void;
|
||||
openExternalClb?(uri: URI): void;
|
||||
metadataClb(meta: string): void;
|
||||
}
|
||||
|
||||
class ResourceViewer {
|
||||
|
||||
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
|
||||
|
||||
static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
container: HTMLElement,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate,
|
||||
): ResourceViewerContext {
|
||||
|
||||
// Ensure CSS class
|
||||
container.className = 'monaco-binary-resource-editor';
|
||||
|
||||
// Large Files
|
||||
if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
|
||||
return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate);
|
||||
}
|
||||
|
||||
// Seemingly Binary Files
|
||||
return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, delegate);
|
||||
}
|
||||
}
|
||||
|
||||
class FileTooLargeFileView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptorSize: number,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptorSize);
|
||||
delegate.metadataClb(size);
|
||||
|
||||
clearNode(container);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size);
|
||||
container.appendChild(label);
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
}
|
||||
|
||||
class FileSeemsBinaryFileView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
|
||||
|
||||
clearNode(container);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const label = document.createElement('p');
|
||||
label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.");
|
||||
container.appendChild(label);
|
||||
|
||||
const link = append(label, $('a.embedded-link'));
|
||||
link.setAttribute('role', 'button');
|
||||
link.textContent = nls.localize('openAsText', "Do you want to open it anyway?");
|
||||
|
||||
disposables.add(addDisposableListener(link, EventType.CLICK, () => delegate.openInternalClb(descriptor.resource)));
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return disposables;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +124,6 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
// 'breadcrumbs.useQuickPick': {
|
||||
// description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."),
|
||||
// type: 'boolean',
|
||||
// default: false
|
||||
// },
|
||||
'breadcrumbs.filePath': {
|
||||
description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."),
|
||||
type: 'string',
|
||||
@@ -155,6 +150,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
||||
description: localize('symbolSortOrder', "Controls how symbols are sorted in the breadcrumbs outline view."),
|
||||
type: 'string',
|
||||
default: 'position',
|
||||
overridable: true,
|
||||
enum: ['position', 'name', 'type'],
|
||||
enumDescriptions: [
|
||||
localize('symbolSortOrder.position', "Show symbol outline in file position order."),
|
||||
|
||||
@@ -15,7 +15,7 @@ import { basename, dirname, isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
|
||||
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
@@ -429,6 +429,7 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
|
||||
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
|
||||
protected _outlineComparator: OutlineItemComparator;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@@ -438,6 +439,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
) {
|
||||
super(parent, instantiationService, themeService, configurationService);
|
||||
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
|
||||
this._outlineComparator = new OutlineItemComparator();
|
||||
}
|
||||
|
||||
protected _createTree(container: HTMLElement) {
|
||||
@@ -452,7 +454,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
collapseByDefault: true,
|
||||
expandOnlyOnTwistieClick: true,
|
||||
multipleSelectionSupport: false,
|
||||
sorter: new OutlineItemComparator(this._getOutlineItemCompareType()),
|
||||
sorter: this._outlineComparator,
|
||||
identityProvider: new OutlineIdentityProvider(),
|
||||
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(),
|
||||
filter: this._instantiationService.createInstance(OutlineFilter, 'breadcrumbs')
|
||||
@@ -471,6 +473,13 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
|
||||
tree.setInput(model);
|
||||
|
||||
const textModel = model.textModel;
|
||||
const overrideConfiguration = {
|
||||
resource: textModel.uri,
|
||||
overrideIdentifier: textModel.getLanguageIdentifier().language
|
||||
};
|
||||
this._outlineComparator.type = this._getOutlineItemCompareType(overrideConfiguration);
|
||||
|
||||
if (element !== model) {
|
||||
tree.reveal(element, 0.5);
|
||||
tree.setFocus([element], this._fakeEvent);
|
||||
@@ -486,8 +495,8 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
}
|
||||
}
|
||||
|
||||
private _getOutlineItemCompareType(): OutlineSortOrder {
|
||||
switch (this._symbolSortOrder.getValue()) {
|
||||
private _getOutlineItemCompareType(overrideConfiguration?: IConfigurationOverrides): OutlineSortOrder {
|
||||
switch (this._symbolSortOrder.getValue(overrideConfiguration)) {
|
||||
case 'name':
|
||||
return OutlineSortOrder.ByName;
|
||||
case 'type':
|
||||
|
||||
@@ -422,12 +422,14 @@ editorCommands.setup();
|
||||
if (isMacintosh) {
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } },
|
||||
group: 'navigation'
|
||||
group: 'navigation',
|
||||
order: 0
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.TouchBarContext, {
|
||||
command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } },
|
||||
group: 'navigation'
|
||||
group: 'navigation',
|
||||
order: 1
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./media/editorgroupview';
|
||||
|
||||
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor, SaveReason, SaveContext } from 'vs/workbench/common/editor';
|
||||
import { Event, Emitter, Relay } from 'vs/base/common/event';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom';
|
||||
@@ -73,30 +73,30 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
//#region events
|
||||
|
||||
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
private readonly _onDidFocus = this._register(new Emitter<void>());
|
||||
readonly onDidFocus = this._onDidFocus.event;
|
||||
|
||||
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onWillDispose: Event<void> = this._onWillDispose.event;
|
||||
private readonly _onWillDispose = this._register(new Emitter<void>());
|
||||
readonly onWillDispose = this._onWillDispose.event;
|
||||
|
||||
private readonly _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
|
||||
readonly onDidGroupChange: Event<IGroupChangeEvent> = this._onDidGroupChange.event;
|
||||
private readonly _onDidGroupChange = this._register(new Emitter<IGroupChangeEvent>());
|
||||
readonly onDidGroupChange = this._onDidGroupChange.event;
|
||||
|
||||
private readonly _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
|
||||
readonly onWillOpenEditor: Event<IEditorOpeningEvent> = this._onWillOpenEditor.event;
|
||||
private readonly _onWillOpenEditor = this._register(new Emitter<IEditorOpeningEvent>());
|
||||
readonly onWillOpenEditor = this._onWillOpenEditor.event;
|
||||
|
||||
private readonly _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
|
||||
readonly onDidOpenEditorFail: Event<EditorInput> = this._onDidOpenEditorFail.event;
|
||||
private readonly _onDidOpenEditorFail = this._register(new Emitter<EditorInput>());
|
||||
readonly onDidOpenEditorFail = this._onDidOpenEditorFail.event;
|
||||
|
||||
private readonly _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
readonly onWillCloseEditor: Event<IEditorCloseEvent> = this._onWillCloseEditor.event;
|
||||
private readonly _onWillCloseEditor = this._register(new Emitter<IEditorCloseEvent>());
|
||||
readonly onWillCloseEditor = this._onWillCloseEditor.event;
|
||||
|
||||
private readonly _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
readonly onDidCloseEditor: Event<IEditorCloseEvent> = this._onDidCloseEditor.event;
|
||||
private readonly _onDidCloseEditor = this._register(new Emitter<IEditorCloseEvent>());
|
||||
readonly onDidCloseEditor = this._onDidCloseEditor.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
private _group: EditorGroup;
|
||||
private readonly _group: EditorGroup;
|
||||
private _disposed = false;
|
||||
|
||||
private active: boolean | undefined;
|
||||
@@ -115,9 +115,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
private editorContainer: HTMLElement;
|
||||
private editorControl: EditorControl;
|
||||
|
||||
private disposedEditorsWorker: RunOnceWorker<EditorInput>;
|
||||
private readonly disposedEditorsWorker = this._register(new RunOnceWorker<EditorInput>(editors => this.handleDisposedEditors(editors), 0));
|
||||
|
||||
private mapEditorToPendingConfirmation: Map<EditorInput, Promise<boolean>> = new Map<EditorInput, Promise<boolean>>();
|
||||
private readonly mapEditorToPendingConfirmation = new Map<EditorInput, Promise<boolean>>();
|
||||
|
||||
constructor(
|
||||
private accessor: IEditorGroupsAccessor,
|
||||
@@ -146,8 +146,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._group = this._register(instantiationService.createInstance(EditorGroup, undefined));
|
||||
}
|
||||
|
||||
this.disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0));
|
||||
|
||||
//#region create()
|
||||
{
|
||||
// Container
|
||||
@@ -1312,7 +1310,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Otherwise, handle accordingly
|
||||
switch (res) {
|
||||
case ConfirmResult.SAVE:
|
||||
const result = await editor.save(this._group.id, { reason: SaveReason.EXPLICIT });
|
||||
const result = await editor.save(this._group.id, { reason: SaveReason.EXPLICIT, context: SaveContext.EDITOR_CLOSE });
|
||||
|
||||
return !result;
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
|
||||
@@ -121,7 +121,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
private _partOptions: IEditorPartOptions;
|
||||
|
||||
private groupViews: Map<GroupIdentifier, IEditorGroupView> = new Map<GroupIdentifier, IEditorGroupView>();
|
||||
private readonly groupViews = new Map<GroupIdentifier, IEditorGroupView>();
|
||||
private mostRecentActiveGroups: GroupIdentifier[] = [];
|
||||
|
||||
private container: HTMLElement | undefined;
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-resource-viewer:focus {
|
||||
.monaco-binary-resource-editor:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer {
|
||||
.monaco-binary-resource-editor {
|
||||
padding: 5px 0 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-resource-viewer .embedded-link,
|
||||
.monaco-resource-viewer .embedded-link:hover {
|
||||
.monaco-binary-resource-editor .embedded-link,
|
||||
.monaco-binary-resource-editor .embedded-link:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
margin-left: 5px;
|
||||
@@ -1,144 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/resourceviewer';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ICssStyleCollector, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { IMAGE_PREVIEW_BORDER } from 'vs/workbench/common/theme';
|
||||
|
||||
export interface IResourceDescriptor {
|
||||
readonly resource: URI;
|
||||
readonly name: string;
|
||||
readonly size?: number;
|
||||
readonly etag?: string;
|
||||
readonly mime: string;
|
||||
}
|
||||
|
||||
class BinarySize {
|
||||
static readonly KB = 1024;
|
||||
static readonly MB = BinarySize.KB * BinarySize.KB;
|
||||
static readonly GB = BinarySize.MB * BinarySize.KB;
|
||||
static readonly TB = BinarySize.GB * BinarySize.KB;
|
||||
|
||||
static formatSize(size: number): string {
|
||||
if (size < BinarySize.KB) {
|
||||
return nls.localize('sizeB', "{0}B", size);
|
||||
}
|
||||
|
||||
if (size < BinarySize.MB) {
|
||||
return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.GB) {
|
||||
return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2));
|
||||
}
|
||||
|
||||
if (size < BinarySize.TB) {
|
||||
return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2));
|
||||
}
|
||||
|
||||
return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
export interface ResourceViewerContext extends IDisposable {
|
||||
layout?(dimension: DOM.Dimension): void;
|
||||
}
|
||||
|
||||
interface ResourceViewerDelegate {
|
||||
openInternalClb(uri: URI): void;
|
||||
openExternalClb?(uri: URI): void;
|
||||
metadataClb(meta: string): void;
|
||||
}
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const borderColor = theme.getColor(IMAGE_PREVIEW_BORDER);
|
||||
collector.addRule(`.monaco-resource-viewer.image img { border : 1px solid ${borderColor ? borderColor.toString() : ''}; }`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper to actually render the given resource into the provided container. Will adjust scrollbar (if provided) automatically based on loading
|
||||
* progress of the binary resource.
|
||||
*/
|
||||
export class ResourceViewer {
|
||||
|
||||
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
|
||||
|
||||
static show(
|
||||
descriptor: IResourceDescriptor,
|
||||
container: HTMLElement,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate,
|
||||
): ResourceViewerContext {
|
||||
|
||||
// Ensure CSS class
|
||||
container.className = 'monaco-resource-viewer';
|
||||
|
||||
// Large Files
|
||||
if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
|
||||
return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate);
|
||||
}
|
||||
|
||||
// Seemingly Binary Files
|
||||
else {
|
||||
return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, delegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FileTooLargeFileView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptorSize: number,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptorSize);
|
||||
delegate.metadataClb(size);
|
||||
|
||||
DOM.clearNode(container);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size);
|
||||
container.appendChild(label);
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return Disposable.None;
|
||||
}
|
||||
}
|
||||
|
||||
class FileSeemsBinaryFileView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
|
||||
|
||||
DOM.clearNode(container);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const label = document.createElement('p');
|
||||
label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.");
|
||||
container.appendChild(label);
|
||||
|
||||
const link = DOM.append(label, DOM.$('a.embedded-link'));
|
||||
link.setAttribute('role', 'button');
|
||||
link.textContent = nls.localize('openAsText', "Do you want to open it anyway?");
|
||||
|
||||
disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource)));
|
||||
|
||||
scrollbar.scanDomNode();
|
||||
|
||||
return disposables;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
line-height: 22px;
|
||||
margin: 10px 0; /* 10px top and bottom */
|
||||
word-wrap: break-word; /* never overflow long words, but break to next line */
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate<INotifica
|
||||
if (isNonEmptyArray(notification.actions && notification.actions.secondary)) {
|
||||
actions++; // secondary actions
|
||||
}
|
||||
this.offsetHelper.style.width = `calc(100% - ${10 /* padding */ + 24 /* severity icon */ + (actions * 24) /* 24px per action */}px)`;
|
||||
this.offsetHelper.style.width = `${450 /* notifications container width */ - (10 /* padding */ + 26 /* severity icon */ + (actions * 24) /* 24px per action */)}px`;
|
||||
|
||||
// Render message into offset helper
|
||||
const renderedMessage = NotificationMessageRenderer.render(notification.message);
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
.monaco-workbench .part.titlebar > .menubar {
|
||||
/* move menubar above drag region as negative z-index on drag region cause greyscale AA */
|
||||
z-index: 2000;
|
||||
z-index: 2500;
|
||||
}
|
||||
|
||||
.monaco-workbench.linux .part.titlebar > .window-title {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { dirname, basename } from 'vs/base/common/resources';
|
||||
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService';
|
||||
import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet';
|
||||
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { localize } from 'vs/nls';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
@@ -43,7 +43,7 @@ import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
|
||||
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
export class CustomTreeViewPane extends ViewletPane {
|
||||
export class CustomTreeViewPane extends ViewPane {
|
||||
|
||||
private treeView: ITreeView;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class CustomTreeViewPane extends ViewletPane {
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService);
|
||||
super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService);
|
||||
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
|
||||
this.treeView = treeView;
|
||||
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
|
||||
|
||||
@@ -1,469 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/paneviewlet';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
|
||||
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme';
|
||||
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
import { IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IActionViewItem, 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 { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
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 { PaneView, IPaneViewOptions, IPaneOptions, Pane } from 'vs/base/browser/ui/splitview/paneview';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IView, FocusedViewContext } from 'vs/workbench/common/views';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export interface IPaneColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
headerForeground?: ColorIdentifier;
|
||||
headerBackground?: ColorIdentifier;
|
||||
headerBorder?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export interface IViewletPaneOptions extends IPaneOptions {
|
||||
actionRunner?: IActionRunner;
|
||||
id: string;
|
||||
title: string;
|
||||
showActionsAlways?: boolean;
|
||||
}
|
||||
|
||||
export abstract class ViewletPane extends Pane implements IView {
|
||||
|
||||
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
|
||||
|
||||
private _onDidFocus = this._register(new Emitter<void>());
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
|
||||
private _onDidBlur = this._register(new Emitter<void>());
|
||||
readonly onDidBlur: Event<void> = this._onDidBlur.event;
|
||||
|
||||
private _onDidChangeBodyVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeBodyVisibility: Event<boolean> = this._onDidChangeBodyVisibility.event;
|
||||
|
||||
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
|
||||
|
||||
private focusedViewContextKey: IContextKey<string>;
|
||||
|
||||
private _isVisible: boolean = false;
|
||||
readonly id: string;
|
||||
title: string;
|
||||
|
||||
protected actionRunner?: IActionRunner;
|
||||
protected toolbar?: ToolBar;
|
||||
private readonly showActionsAlways: boolean = false;
|
||||
private headerContainer?: HTMLElement;
|
||||
private titleContainer?: HTMLElement;
|
||||
protected twistiesContainer?: HTMLElement;
|
||||
|
||||
constructor(
|
||||
options: IViewletPaneOptions,
|
||||
@IKeybindingService protected keybindingService: IKeybindingService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(options);
|
||||
|
||||
this.id = options.id;
|
||||
this.title = options.title;
|
||||
this.actionRunner = options.actionRunner;
|
||||
this.showActionsAlways = !!options.showActionsAlways;
|
||||
this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (this._isVisible !== visible) {
|
||||
this._isVisible = visible;
|
||||
|
||||
if (this.isExpanded()) {
|
||||
this._onDidChangeBodyVisibility.fire(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
isBodyVisible(): boolean {
|
||||
return this._isVisible && this.isExpanded();
|
||||
}
|
||||
|
||||
setExpanded(expanded: boolean): boolean {
|
||||
const changed = super.setExpanded(expanded);
|
||||
if (changed) {
|
||||
this._onDidChangeBodyVisibility.fire(expanded);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
render(): void {
|
||||
super.render();
|
||||
|
||||
const focusTracker = trackFocus(this.element);
|
||||
this._register(focusTracker);
|
||||
this._register(focusTracker.onDidFocus(() => {
|
||||
this.focusedViewContextKey.set(this.id);
|
||||
this._onDidFocus.fire();
|
||||
}));
|
||||
this._register(focusTracker.onDidBlur(() => {
|
||||
this.focusedViewContextKey.reset();
|
||||
this._onDidBlur.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
protected renderHeader(container: HTMLElement): void {
|
||||
this.headerContainer = container;
|
||||
|
||||
this.renderTwisties(container);
|
||||
|
||||
this.renderHeaderTitle(container, this.title);
|
||||
|
||||
const actions = append(container, $('.actions'));
|
||||
toggleClass(actions, 'show', this.showActionsAlways);
|
||||
this.toolbar = new ToolBar(actions, this.contextMenuService, {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionViewItemProvider: action => this.getActionViewItem(action),
|
||||
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
|
||||
actionRunner: this.actionRunner
|
||||
});
|
||||
|
||||
this._register(this.toolbar);
|
||||
this.setActions();
|
||||
|
||||
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPane.AlwaysShowActionsConfig));
|
||||
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
|
||||
this.updateActionsVisibility();
|
||||
}
|
||||
|
||||
protected renderTwisties(container: HTMLElement): void {
|
||||
this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right'));
|
||||
}
|
||||
|
||||
protected renderHeaderTitle(container: HTMLElement, title: string): void {
|
||||
this.titleContainer = append(container, $('h3.title', undefined, title));
|
||||
}
|
||||
|
||||
protected updateTitle(title: string): void {
|
||||
if (this.titleContainer) {
|
||||
this.titleContainer.textContent = title;
|
||||
}
|
||||
this.title = title;
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.element) {
|
||||
this.element.focus();
|
||||
this._onDidFocus.fire();
|
||||
}
|
||||
}
|
||||
|
||||
private setActions(): void {
|
||||
if (this.toolbar) {
|
||||
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
|
||||
this.toolbar.context = this.getActionsContext();
|
||||
}
|
||||
}
|
||||
|
||||
private updateActionsVisibility(): void {
|
||||
if (!this.headerContainer) {
|
||||
return;
|
||||
}
|
||||
const shouldAlwaysShowActions = this.configurationService.getValue<boolean>('workbench.view.alwaysShowHeaderActions');
|
||||
toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions);
|
||||
}
|
||||
|
||||
protected updateActions(): void {
|
||||
this.setActions();
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getActionsContext(): unknown {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
saveState(): void {
|
||||
// Subclasses to implement for saving state
|
||||
}
|
||||
}
|
||||
|
||||
export interface IViewsViewletOptions extends IPaneViewOptions {
|
||||
showHeaderInTitleWhenSingleView: boolean;
|
||||
}
|
||||
|
||||
interface IViewletPaneItem {
|
||||
pane: ViewletPane;
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
export class PaneViewlet extends Viewlet {
|
||||
|
||||
private lastFocusedPane: ViewletPane | undefined;
|
||||
private paneItems: IViewletPaneItem[] = [];
|
||||
private paneview?: PaneView;
|
||||
|
||||
get onDidSashChange(): Event<number> {
|
||||
return assertIsDefined(this.paneview).onDidSashChange;
|
||||
}
|
||||
|
||||
protected get panes(): ViewletPane[] {
|
||||
return this.paneItems.map(i => i.pane);
|
||||
}
|
||||
|
||||
protected get length(): number {
|
||||
return this.paneItems.length;
|
||||
}
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
private options: IViewsViewletOptions,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, configurationService, layoutService, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
this.paneview = this._register(new PaneView(parent, this.options));
|
||||
this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewletPane, to as ViewletPane)));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
}
|
||||
|
||||
private showContextMenu(event: StandardMouseEvent): void {
|
||||
for (const paneItem of this.paneItems) {
|
||||
// Do not show context menu if target is coming from inside pane views
|
||||
if (isAncestor(event.target, paneItem.pane.element)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions()
|
||||
});
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
let title = Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
|
||||
|
||||
if (this.isSingleView()) {
|
||||
const paneItemTitle = this.paneItems[0].pane.title;
|
||||
title = paneItemTitle ? `${title}: ${paneItemTitle}` : title;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getActions();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getSecondaryActions();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getActionViewItem(action);
|
||||
}
|
||||
|
||||
return super.getActionViewItem(action);
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
super.focus();
|
||||
|
||||
if (this.lastFocusedPane) {
|
||||
this.lastFocusedPane.focus();
|
||||
} else if (this.paneItems.length > 0) {
|
||||
for (const { pane: pane } of this.paneItems) {
|
||||
if (pane.isExpanded()) {
|
||||
pane.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
if (this.paneview) {
|
||||
this.paneview.layout(dimension.height, dimension.width);
|
||||
}
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
const sizes = this.paneItems
|
||||
.map(paneItem => paneItem.pane.getOptimalWidth() || 0);
|
||||
|
||||
return Math.max(...sizes);
|
||||
}
|
||||
|
||||
addPanes(panes: { pane: ViewletPane, size: number, index?: number; }[]): void {
|
||||
const wasSingleView = this.isSingleView();
|
||||
|
||||
for (const { pane: pane, size, index } of panes) {
|
||||
this.addPane(pane, size, index);
|
||||
}
|
||||
|
||||
this.updateViewHeaders();
|
||||
if (this.isSingleView() !== wasSingleView) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
}
|
||||
|
||||
private addPane(pane: ViewletPane, size: number, index = this.paneItems.length - 1): void {
|
||||
const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane);
|
||||
const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => {
|
||||
if (this.isSingleView()) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
});
|
||||
const onDidChange = pane.onDidChange(() => {
|
||||
if (pane === this.lastFocusedPane && !pane.isExpanded()) {
|
||||
this.lastFocusedPane = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
const paneStyler = attachStyler<IPaneColors>(this.themeService, {
|
||||
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
|
||||
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
|
||||
headerBorder: SIDE_BAR_SECTION_HEADER_BORDER,
|
||||
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
}, pane);
|
||||
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange);
|
||||
const paneItem: IViewletPaneItem = { pane: pane, disposable };
|
||||
|
||||
this.paneItems.splice(index, 0, paneItem);
|
||||
assertIsDefined(this.paneview).addPane(pane, size, index);
|
||||
}
|
||||
|
||||
removePanes(panes: ViewletPane[]): void {
|
||||
const wasSingleView = this.isSingleView();
|
||||
|
||||
panes.forEach(pane => this.removePane(pane));
|
||||
|
||||
this.updateViewHeaders();
|
||||
if (wasSingleView !== this.isSingleView()) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
}
|
||||
|
||||
private removePane(pane: ViewletPane): void {
|
||||
const index = firstIndex(this.paneItems, i => i.pane === pane);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.lastFocusedPane === pane) {
|
||||
this.lastFocusedPane = undefined;
|
||||
}
|
||||
|
||||
assertIsDefined(this.paneview).removePane(pane);
|
||||
const [paneItem] = this.paneItems.splice(index, 1);
|
||||
paneItem.disposable.dispose();
|
||||
|
||||
}
|
||||
|
||||
movePane(from: ViewletPane, to: ViewletPane): void {
|
||||
const fromIndex = firstIndex(this.paneItems, item => item.pane === from);
|
||||
const toIndex = firstIndex(this.paneItems, item => item.pane === to);
|
||||
|
||||
if (fromIndex < 0 || fromIndex >= this.paneItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toIndex < 0 || toIndex >= this.paneItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [paneItem] = this.paneItems.splice(fromIndex, 1);
|
||||
this.paneItems.splice(toIndex, 0, paneItem);
|
||||
|
||||
assertIsDefined(this.paneview).movePane(from, to);
|
||||
}
|
||||
|
||||
resizePane(pane: ViewletPane, size: number): void {
|
||||
assertIsDefined(this.paneview).resizePane(pane, size);
|
||||
}
|
||||
|
||||
getPaneSize(pane: ViewletPane): number {
|
||||
return assertIsDefined(this.paneview).getPaneSize(pane);
|
||||
}
|
||||
|
||||
protected updateViewHeaders(): void {
|
||||
if (this.isSingleView()) {
|
||||
this.paneItems[0].pane.setExpanded(true);
|
||||
this.paneItems[0].pane.headerVisible = false;
|
||||
} else {
|
||||
this.paneItems.forEach(i => i.pane.headerVisible = true);
|
||||
}
|
||||
}
|
||||
|
||||
protected isSingleView(): boolean {
|
||||
return this.options.showHeaderInTitleWhenSingleView && this.paneItems.length === 1;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.paneItems.forEach(i => i.disposable.dispose());
|
||||
if (this.paneview) {
|
||||
this.paneview.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
759
src/vs/workbench/browser/parts/views/viewPaneContainer.ts
Normal file
759
src/vs/workbench/browser/parts/views/viewPaneContainer.ts
Normal file
@@ -0,0 +1,759 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/paneviewlet';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler';
|
||||
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme';
|
||||
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
|
||||
import { IActionViewItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
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 { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndController } from 'vs/base/browser/ui/splitview/paneview';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { PersistentContributableViewsModel, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/browser/parts/views/views';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer';
|
||||
import { Component } from 'vs/workbench/common/component';
|
||||
import { Extensions, ViewletRegistry } from 'vs/workbench/browser/viewlet';
|
||||
|
||||
export interface IPaneColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
headerForeground?: ColorIdentifier;
|
||||
headerBackground?: ColorIdentifier;
|
||||
headerBorder?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export interface IViewPaneOptions extends IPaneOptions {
|
||||
actionRunner?: IActionRunner;
|
||||
id: string;
|
||||
title: string;
|
||||
showActionsAlways?: boolean;
|
||||
}
|
||||
|
||||
export abstract class ViewPane extends Pane implements IView {
|
||||
|
||||
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
|
||||
|
||||
private _onDidFocus = this._register(new Emitter<void>());
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
|
||||
private _onDidBlur = this._register(new Emitter<void>());
|
||||
readonly onDidBlur: Event<void> = this._onDidBlur.event;
|
||||
|
||||
private _onDidChangeBodyVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeBodyVisibility: Event<boolean> = this._onDidChangeBodyVisibility.event;
|
||||
|
||||
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
|
||||
|
||||
private focusedViewContextKey: IContextKey<string>;
|
||||
|
||||
private _isVisible: boolean = false;
|
||||
readonly id: string;
|
||||
title: string;
|
||||
|
||||
protected actionRunner?: IActionRunner;
|
||||
protected toolbar?: ToolBar;
|
||||
private readonly showActionsAlways: boolean = false;
|
||||
private headerContainer?: HTMLElement;
|
||||
private titleContainer?: HTMLElement;
|
||||
protected twistiesContainer?: HTMLElement;
|
||||
|
||||
constructor(
|
||||
options: IViewPaneOptions,
|
||||
@IKeybindingService protected keybindingService: IKeybindingService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
super(options);
|
||||
|
||||
this.id = options.id;
|
||||
this.title = options.title;
|
||||
this.actionRunner = options.actionRunner;
|
||||
this.showActionsAlways = !!options.showActionsAlways;
|
||||
this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (this._isVisible !== visible) {
|
||||
this._isVisible = visible;
|
||||
|
||||
if (this.isExpanded()) {
|
||||
this._onDidChangeBodyVisibility.fire(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
isBodyVisible(): boolean {
|
||||
return this._isVisible && this.isExpanded();
|
||||
}
|
||||
|
||||
setExpanded(expanded: boolean): boolean {
|
||||
const changed = super.setExpanded(expanded);
|
||||
if (changed) {
|
||||
this._onDidChangeBodyVisibility.fire(expanded);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
render(): void {
|
||||
super.render();
|
||||
|
||||
const focusTracker = trackFocus(this.element);
|
||||
this._register(focusTracker);
|
||||
this._register(focusTracker.onDidFocus(() => {
|
||||
this.focusedViewContextKey.set(this.id);
|
||||
this._onDidFocus.fire();
|
||||
}));
|
||||
this._register(focusTracker.onDidBlur(() => {
|
||||
this.focusedViewContextKey.reset();
|
||||
this._onDidBlur.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
protected renderHeader(container: HTMLElement): void {
|
||||
this.headerContainer = container;
|
||||
|
||||
this.renderTwisties(container);
|
||||
|
||||
this.renderHeaderTitle(container, this.title);
|
||||
|
||||
const actions = append(container, $('.actions'));
|
||||
toggleClass(actions, 'show', this.showActionsAlways);
|
||||
this.toolbar = new ToolBar(actions, this.contextMenuService, {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionViewItemProvider: action => this.getActionViewItem(action),
|
||||
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
|
||||
actionRunner: this.actionRunner
|
||||
});
|
||||
|
||||
this._register(this.toolbar);
|
||||
this.setActions();
|
||||
|
||||
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewPane.AlwaysShowActionsConfig));
|
||||
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
|
||||
this.updateActionsVisibility();
|
||||
}
|
||||
|
||||
protected renderTwisties(container: HTMLElement): void {
|
||||
this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right'));
|
||||
}
|
||||
|
||||
protected renderHeaderTitle(container: HTMLElement, title: string): void {
|
||||
this.titleContainer = append(container, $('h3.title', undefined, title));
|
||||
}
|
||||
|
||||
protected updateTitle(title: string): void {
|
||||
if (this.titleContainer) {
|
||||
this.titleContainer.textContent = title;
|
||||
}
|
||||
this.title = title;
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.element) {
|
||||
this.element.focus();
|
||||
this._onDidFocus.fire();
|
||||
}
|
||||
}
|
||||
|
||||
private setActions(): void {
|
||||
if (this.toolbar) {
|
||||
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
|
||||
this.toolbar.context = this.getActionsContext();
|
||||
}
|
||||
}
|
||||
|
||||
private updateActionsVisibility(): void {
|
||||
if (!this.headerContainer) {
|
||||
return;
|
||||
}
|
||||
const shouldAlwaysShowActions = this.configurationService.getValue<boolean>('workbench.view.alwaysShowHeaderActions');
|
||||
toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions);
|
||||
}
|
||||
|
||||
protected updateActions(): void {
|
||||
this.setActions();
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getActionsContext(): unknown {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
saveState(): void {
|
||||
// Subclasses to implement for saving state
|
||||
}
|
||||
}
|
||||
|
||||
export interface IViewPaneContainerOptions extends IPaneViewOptions {
|
||||
showHeaderInTitleWhenSingleView: boolean;
|
||||
}
|
||||
|
||||
interface IViewPaneItem {
|
||||
pane: ViewPane;
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
export class ViewPaneContainer extends Component implements IViewPaneContainer {
|
||||
|
||||
private lastFocusedPane: ViewPane | undefined;
|
||||
private paneItems: IViewPaneItem[] = [];
|
||||
private paneview?: PaneView;
|
||||
|
||||
private visible: boolean = false;
|
||||
|
||||
private areExtensionsReady: boolean = false;
|
||||
|
||||
private didLayout = false;
|
||||
private dimension: Dimension | undefined;
|
||||
|
||||
protected actionRunner: IActionRunner | undefined;
|
||||
|
||||
private readonly visibleViewsCountFromCache: number | undefined;
|
||||
private readonly visibleViewsStorageId: string;
|
||||
protected readonly viewsModel: PersistentContributableViewsModel;
|
||||
private viewDisposables: IDisposable[] = [];
|
||||
|
||||
private readonly _onTitleAreaUpdate: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onTitleAreaUpdate: Event<void> = this._onTitleAreaUpdate.event;
|
||||
|
||||
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
|
||||
get onDidSashChange(): Event<number> {
|
||||
return assertIsDefined(this.paneview).onDidSashChange;
|
||||
}
|
||||
|
||||
protected get panes(): ViewPane[] {
|
||||
return this.paneItems.map(i => i.pane);
|
||||
}
|
||||
|
||||
protected get length(): number {
|
||||
return this.paneItems.length;
|
||||
}
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
viewPaneContainerStateStorageId: string,
|
||||
private options: IViewPaneContainerOptions,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@ITelemetryService protected telemetryService: ITelemetryService,
|
||||
@IExtensionService protected extensionService: IExtensionService,
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IStorageService protected storageService: IStorageService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
|
||||
super(id, themeService, storageService);
|
||||
|
||||
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).get(id);
|
||||
if (!container) {
|
||||
throw new Error('Could not find container');
|
||||
}
|
||||
|
||||
// Use default pane dnd controller if not specified
|
||||
if (!this.options.dnd) {
|
||||
this.options.dnd = new DefaultPaneDndController();
|
||||
}
|
||||
|
||||
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
|
||||
this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined);
|
||||
this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables)));
|
||||
this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewPaneContainerStateStorageId));
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
// super.create(parent);
|
||||
this.paneview = this._register(new PaneView(parent, this.options));
|
||||
this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane)));
|
||||
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e))));
|
||||
|
||||
this._register(this.onDidSashChange(() => this.saveViewSizes()));
|
||||
this.viewsModel.onDidAdd(added => this.onDidAddViews(added));
|
||||
this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed));
|
||||
const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => {
|
||||
const size = this.viewsModel.getSize(viewDescriptor.id);
|
||||
const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id);
|
||||
return ({ viewDescriptor, index, size, collapsed });
|
||||
});
|
||||
if (addedViews.length) {
|
||||
this.onDidAddViews(addedViews);
|
||||
}
|
||||
|
||||
// Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
this.areExtensionsReady = true;
|
||||
if (this.panes.length) {
|
||||
this.updateTitleArea();
|
||||
this.updateViewHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
this.focus();
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
let title = Registry.as<ViewletRegistry>(Extensions.Viewlets).getViewlet(this.getId()).name;
|
||||
|
||||
if (this.isSingleView()) {
|
||||
const paneItemTitle = this.paneItems[0].pane.title;
|
||||
title = paneItemTitle ? `${title}: ${paneItemTitle}` : title;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private showContextMenu(event: StandardMouseEvent): void {
|
||||
for (const paneItem of this.paneItems) {
|
||||
// Do not show context menu if target is coming from inside pane views
|
||||
if (isAncestor(event.target, paneItem.pane.element)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions()
|
||||
});
|
||||
}
|
||||
|
||||
getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] {
|
||||
const result: IAction[] = [];
|
||||
if (viewDescriptor) {
|
||||
result.push(<IAction>{
|
||||
id: `${viewDescriptor.id}.removeView`,
|
||||
label: nls.localize('hideView', "Hide"),
|
||||
enabled: viewDescriptor.canToggleVisibility,
|
||||
run: () => this.toggleViewVisibility(viewDescriptor.id)
|
||||
});
|
||||
}
|
||||
|
||||
const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{
|
||||
id: `${viewDescriptor.id}.toggleVisibility`,
|
||||
label: viewDescriptor.name,
|
||||
checked: this.viewsModel.isVisible(viewDescriptor.id),
|
||||
enabled: viewDescriptor.canToggleVisibility,
|
||||
run: () => this.toggleViewVisibility(viewDescriptor.id)
|
||||
}));
|
||||
|
||||
if (result.length && viewToggleActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
|
||||
result.push(...viewToggleActions);
|
||||
return result;
|
||||
}
|
||||
|
||||
getActions(): IAction[] {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getActions();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getSecondaryActions(): IAction[] {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getSecondaryActions();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
getActionViewItem(action: IAction): IActionViewItem | undefined {
|
||||
if (this.isSingleView()) {
|
||||
return this.paneItems[0].pane.getActionViewItem(action);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.lastFocusedPane) {
|
||||
this.lastFocusedPane.focus();
|
||||
} else if (this.paneItems.length > 0) {
|
||||
for (const { pane: pane } of this.paneItems) {
|
||||
if (pane.isExpanded()) {
|
||||
pane.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
if (this.paneview) {
|
||||
this.paneview.layout(dimension.height, dimension.width);
|
||||
}
|
||||
|
||||
this.dimension = dimension;
|
||||
if (this.didLayout) {
|
||||
this.saveViewSizes();
|
||||
} else {
|
||||
this.didLayout = true;
|
||||
this.restoreViewSizes();
|
||||
}
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
const additionalMargin = 16;
|
||||
const optimalWidth = Math.max(...this.panes.map(view => view.getOptimalWidth() || 0));
|
||||
return optimalWidth + additionalMargin;
|
||||
}
|
||||
|
||||
addPanes(panes: { pane: ViewPane, size: number, index?: number; }[]): void {
|
||||
const wasSingleView = this.isSingleView();
|
||||
|
||||
for (const { pane: pane, size, index } of panes) {
|
||||
this.addPane(pane, size, index);
|
||||
}
|
||||
|
||||
this.updateViewHeaders();
|
||||
if (this.isSingleView() !== wasSingleView) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (this.visible !== !!visible) {
|
||||
this.visible = visible;
|
||||
|
||||
this._onDidChangeVisibility.fire(visible);
|
||||
}
|
||||
|
||||
this.panes.filter(view => view.isVisible() !== visible)
|
||||
.map((view) => view.setVisible(visible));
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
protected updateTitleArea(): void {
|
||||
this._onTitleAreaUpdate.fire();
|
||||
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane {
|
||||
return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewPane;
|
||||
}
|
||||
|
||||
getView(id: string): ViewPane | undefined {
|
||||
return this.panes.filter(view => view.id === id)[0];
|
||||
}
|
||||
|
||||
private saveViewSizes(): void {
|
||||
// Save size only when the layout has happened
|
||||
if (this.didLayout) {
|
||||
for (const view of this.panes) {
|
||||
this.viewsModel.setSize(view.id, this.getPaneSize(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private restoreViewSizes(): void {
|
||||
// Restore sizes only when the layout has happened
|
||||
if (this.didLayout) {
|
||||
let initialSizes;
|
||||
for (let i = 0; i < this.viewsModel.visibleViewDescriptors.length; i++) {
|
||||
const pane = this.panes[i];
|
||||
const viewDescriptor = this.viewsModel.visibleViewDescriptors[i];
|
||||
const size = this.viewsModel.getSize(viewDescriptor.id);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
this.resizePane(pane, size);
|
||||
} else {
|
||||
initialSizes = initialSizes ? initialSizes : this.computeInitialSizes();
|
||||
this.resizePane(pane, initialSizes.get(pane.id) || 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private computeInitialSizes(): Map<string, number> {
|
||||
const sizes: Map<string, number> = new Map<string, number>();
|
||||
if (this.dimension) {
|
||||
const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0);
|
||||
for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) {
|
||||
sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight);
|
||||
}
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
saveState(): void {
|
||||
this.panes.forEach((view) => view.saveState());
|
||||
this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const actions: IAction[] = this.getContextMenuActions(viewDescriptor);
|
||||
|
||||
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => actions
|
||||
});
|
||||
}
|
||||
|
||||
openView(id: string, focus?: boolean): IView {
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
let view = this.getView(id);
|
||||
if (!view) {
|
||||
this.toggleViewVisibility(id);
|
||||
}
|
||||
view = this.getView(id)!;
|
||||
view.setExpanded(true);
|
||||
if (focus) {
|
||||
view.focus();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
const panesToAdd: { pane: ViewPane, size: number, index: number }[] = [];
|
||||
for (const { viewDescriptor, collapsed, index, size } of added) {
|
||||
const pane = this.createView(viewDescriptor,
|
||||
{
|
||||
id: viewDescriptor.id,
|
||||
title: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
expanded: !collapsed
|
||||
});
|
||||
|
||||
pane.render();
|
||||
const contextMenuDisposable = addDisposableListener(pane.draggableElement, 'contextmenu', e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.onContextMenu(new StandardMouseEvent(e), viewDescriptor);
|
||||
});
|
||||
|
||||
const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => {
|
||||
this.viewsModel.setCollapsed(viewDescriptor.id, collapsed);
|
||||
});
|
||||
|
||||
this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable));
|
||||
panesToAdd.push({ pane, size: size || pane.minimumSize, index });
|
||||
}
|
||||
|
||||
this.addPanes(panesToAdd);
|
||||
this.restoreViewSizes();
|
||||
|
||||
const panes: ViewPane[] = [];
|
||||
for (const { pane } of panesToAdd) {
|
||||
pane.setVisible(this.isVisible());
|
||||
panes.push(pane);
|
||||
}
|
||||
return panes;
|
||||
}
|
||||
|
||||
getActionRunner(): IActionRunner {
|
||||
if (!this.actionRunner) {
|
||||
this.actionRunner = new ActionRunner();
|
||||
}
|
||||
|
||||
return this.actionRunner;
|
||||
}
|
||||
|
||||
private onDidRemoveViews(removed: IViewDescriptorRef[]): void {
|
||||
removed = removed.sort((a, b) => b.index - a.index);
|
||||
const panesToRemove: ViewPane[] = [];
|
||||
for (const { index } of removed) {
|
||||
const [disposable] = this.viewDisposables.splice(index, 1);
|
||||
disposable.dispose();
|
||||
panesToRemove.push(this.panes[index]);
|
||||
}
|
||||
this.removePanes(panesToRemove);
|
||||
dispose(panesToRemove);
|
||||
}
|
||||
|
||||
protected toggleViewVisibility(viewId: string): void {
|
||||
const visible = !this.viewsModel.isVisible(viewId);
|
||||
type ViewsToggleVisibilityClassification = {
|
||||
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible });
|
||||
this.viewsModel.setVisible(viewId, visible);
|
||||
}
|
||||
|
||||
|
||||
private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void {
|
||||
const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane);
|
||||
const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => {
|
||||
if (this.isSingleView()) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
});
|
||||
const onDidChange = pane.onDidChange(() => {
|
||||
if (pane === this.lastFocusedPane && !pane.isExpanded()) {
|
||||
this.lastFocusedPane = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO@sbatten Styling is viewlet specific, must fix
|
||||
const paneStyler = attachStyler<IPaneColors>(this.themeService, {
|
||||
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
|
||||
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
|
||||
headerBorder: SIDE_BAR_SECTION_HEADER_BORDER,
|
||||
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
|
||||
}, pane);
|
||||
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange);
|
||||
const paneItem: IViewPaneItem = { pane: pane, disposable };
|
||||
|
||||
this.paneItems.splice(index, 0, paneItem);
|
||||
assertIsDefined(this.paneview).addPane(pane, size, index);
|
||||
}
|
||||
|
||||
removePanes(panes: ViewPane[]): void {
|
||||
const wasSingleView = this.isSingleView();
|
||||
|
||||
panes.forEach(pane => this.removePane(pane));
|
||||
|
||||
this.updateViewHeaders();
|
||||
if (wasSingleView !== this.isSingleView()) {
|
||||
this.updateTitleArea();
|
||||
}
|
||||
}
|
||||
|
||||
private removePane(pane: ViewPane): void {
|
||||
const index = firstIndex(this.paneItems, i => i.pane === pane);
|
||||
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.lastFocusedPane === pane) {
|
||||
this.lastFocusedPane = undefined;
|
||||
}
|
||||
|
||||
assertIsDefined(this.paneview).removePane(pane);
|
||||
const [paneItem] = this.paneItems.splice(index, 1);
|
||||
paneItem.disposable.dispose();
|
||||
|
||||
}
|
||||
|
||||
movePane(from: ViewPane, to: ViewPane): void {
|
||||
const fromIndex = firstIndex(this.paneItems, item => item.pane === from);
|
||||
const toIndex = firstIndex(this.paneItems, item => item.pane === to);
|
||||
|
||||
const fromViewDescriptor = this.viewsModel.visibleViewDescriptors[fromIndex];
|
||||
const toViewDescriptor = this.viewsModel.visibleViewDescriptors[toIndex];
|
||||
|
||||
if (fromIndex < 0 || fromIndex >= this.paneItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (toIndex < 0 || toIndex >= this.paneItems.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [paneItem] = this.paneItems.splice(fromIndex, 1);
|
||||
this.paneItems.splice(toIndex, 0, paneItem);
|
||||
|
||||
assertIsDefined(this.paneview).movePane(from, to);
|
||||
|
||||
this.viewsModel.move(fromViewDescriptor.id, toViewDescriptor.id);
|
||||
}
|
||||
|
||||
resizePane(pane: ViewPane, size: number): void {
|
||||
assertIsDefined(this.paneview).resizePane(pane, size);
|
||||
}
|
||||
|
||||
getPaneSize(pane: ViewPane): number {
|
||||
return assertIsDefined(this.paneview).getPaneSize(pane);
|
||||
}
|
||||
|
||||
protected updateViewHeaders(): void {
|
||||
if (this.isSingleView()) {
|
||||
this.paneItems[0].pane.setExpanded(true);
|
||||
this.paneItems[0].pane.headerVisible = false;
|
||||
} else {
|
||||
this.paneItems.forEach(i => i.pane.headerVisible = true);
|
||||
}
|
||||
}
|
||||
|
||||
protected isSingleView(): boolean {
|
||||
if (!(this.options.showHeaderInTitleWhenSingleView && this.paneItems.length === 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.areExtensionsReady) {
|
||||
if (this.visibleViewsCountFromCache === undefined) {
|
||||
return false;
|
||||
}
|
||||
// Check in cache so that view do not jump. See #29609
|
||||
return this.visibleViewsCountFromCache === 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.paneItems.forEach(i => i.disposable.dispose());
|
||||
if (this.paneview) {
|
||||
this.paneview.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,330 +4,30 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { dispose, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IViewDescriptor, IViewsViewlet, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView } from 'vs/workbench/common/views';
|
||||
import { IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { PaneViewlet, ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet';
|
||||
import { DefaultPaneDndController } from 'vs/base/browser/ui/splitview/paneview';
|
||||
import { ViewPaneContainer, ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
|
||||
import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { MementoObject } from 'vs/workbench/common/memento';
|
||||
import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views';
|
||||
|
||||
export interface IViewletViewOptions extends IViewletPaneOptions {
|
||||
viewletState: MementoObject;
|
||||
export interface IViewletViewOptions extends IViewPaneOptions {
|
||||
}
|
||||
|
||||
export abstract class ViewContainerViewlet extends PaneViewlet implements IViewsViewlet {
|
||||
|
||||
private readonly viewletState: MementoObject;
|
||||
private didLayout = false;
|
||||
private dimension: DOM.Dimension | undefined;
|
||||
private areExtensionsReady: boolean = false;
|
||||
|
||||
private readonly visibleViewsCountFromCache: number | undefined;
|
||||
private readonly visibleViewsStorageId: string;
|
||||
protected readonly viewsModel: PersistentContributableViewsModel;
|
||||
private viewDisposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
viewletStateStorageId: string,
|
||||
showHeaderInTitleWhenSingleView: boolean,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService protected storageService: IStorageService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IExtensionService protected extensionService: IExtensionService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPaneDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService);
|
||||
|
||||
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).get(id);
|
||||
if (!container) {
|
||||
throw new Error('Could not find container');
|
||||
}
|
||||
this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId));
|
||||
this.viewletState = this.getMemento(StorageScope.WORKSPACE);
|
||||
|
||||
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
|
||||
this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined);
|
||||
this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables)));
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
super.create(parent);
|
||||
this._register(this.onDidSashChange(() => this.saveViewSizes()));
|
||||
this.viewsModel.onDidAdd(added => this.onDidAddViews(added));
|
||||
this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed));
|
||||
const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => {
|
||||
const size = this.viewsModel.getSize(viewDescriptor.id);
|
||||
const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id);
|
||||
return ({ viewDescriptor, index, size, collapsed });
|
||||
});
|
||||
if (addedViews.length) {
|
||||
this.onDidAddViews(addedViews);
|
||||
}
|
||||
|
||||
// Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609
|
||||
this.extensionService.whenInstalledExtensionsRegistered().then(() => {
|
||||
this.areExtensionsReady = true;
|
||||
if (this.panes.length) {
|
||||
this.updateTitleArea();
|
||||
this.updateViewHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
this.focus();
|
||||
}
|
||||
|
||||
getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] {
|
||||
const result: IAction[] = [];
|
||||
if (viewDescriptor) {
|
||||
result.push(<IAction>{
|
||||
id: `${viewDescriptor.id}.removeView`,
|
||||
label: localize('hideView', "Hide"),
|
||||
enabled: viewDescriptor.canToggleVisibility,
|
||||
run: () => this.toggleViewVisibility(viewDescriptor.id)
|
||||
});
|
||||
}
|
||||
const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => (<IAction>{
|
||||
id: `${viewDescriptor.id}.toggleVisibility`,
|
||||
label: viewDescriptor.name,
|
||||
checked: this.viewsModel.isVisible(viewDescriptor.id),
|
||||
enabled: viewDescriptor.canToggleVisibility,
|
||||
run: () => this.toggleViewVisibility(viewDescriptor.id)
|
||||
}));
|
||||
|
||||
if (result.length && viewToggleActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
result.push(...viewToggleActions);
|
||||
|
||||
const parentActions = this.getViewletContextMenuActions();
|
||||
if (result.length && parentActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
result.push(...parentActions);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected getViewletContextMenuActions() {
|
||||
return super.getContextMenuActions();
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
super.setVisible(visible);
|
||||
this.panes.filter(view => view.isVisible() !== visible)
|
||||
.map((view) => view.setVisible(visible));
|
||||
}
|
||||
|
||||
openView(id: string, focus?: boolean): IView {
|
||||
if (focus) {
|
||||
this.focus();
|
||||
}
|
||||
let view = this.getView(id);
|
||||
if (!view) {
|
||||
this.toggleViewVisibility(id);
|
||||
}
|
||||
view = this.getView(id)!;
|
||||
view.setExpanded(true);
|
||||
if (focus) {
|
||||
view.focus();
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
movePane(from: ViewletPane, to: ViewletPane): void {
|
||||
const fromIndex = firstIndex(this.panes, pane => pane === from);
|
||||
const toIndex = firstIndex(this.panes, pane => pane === to);
|
||||
const fromViewDescriptor = this.viewsModel.visibleViewDescriptors[fromIndex];
|
||||
const toViewDescriptor = this.viewsModel.visibleViewDescriptors[toIndex];
|
||||
|
||||
super.movePane(from, to);
|
||||
this.viewsModel.move(fromViewDescriptor.id, toViewDescriptor.id);
|
||||
}
|
||||
|
||||
layout(dimension: DOM.Dimension): void {
|
||||
super.layout(dimension);
|
||||
this.dimension = dimension;
|
||||
if (this.didLayout) {
|
||||
this.saveViewSizes();
|
||||
} else {
|
||||
this.didLayout = true;
|
||||
this.restoreViewSizes();
|
||||
}
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
const additionalMargin = 16;
|
||||
const optimalWidth = Math.max(...this.panes.map(view => view.getOptimalWidth() || 0));
|
||||
return optimalWidth + additionalMargin;
|
||||
}
|
||||
|
||||
protected isSingleView(): boolean {
|
||||
if (!super.isSingleView()) {
|
||||
return false;
|
||||
}
|
||||
if (!this.areExtensionsReady) {
|
||||
if (this.visibleViewsCountFromCache === undefined) {
|
||||
return false;
|
||||
}
|
||||
// Check in cache so that view do not jump. See #29609
|
||||
return this.visibleViewsCountFromCache === 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane {
|
||||
return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPane;
|
||||
}
|
||||
|
||||
protected getView(id: string): ViewletPane | undefined {
|
||||
return this.panes.filter(view => view.id === id)[0];
|
||||
}
|
||||
|
||||
protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] {
|
||||
const panesToAdd: { pane: ViewletPane, size: number, index: number }[] = [];
|
||||
for (const { viewDescriptor, collapsed, index, size } of added) {
|
||||
const pane = this.createView(viewDescriptor,
|
||||
{
|
||||
id: viewDescriptor.id,
|
||||
title: viewDescriptor.name,
|
||||
actionRunner: this.getActionRunner(),
|
||||
expanded: !collapsed,
|
||||
viewletState: this.viewletState
|
||||
});
|
||||
pane.render();
|
||||
const contextMenuDisposable = DOM.addDisposableListener(pane.draggableElement, 'contextmenu', e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.onContextMenu(new StandardMouseEvent(e), viewDescriptor);
|
||||
});
|
||||
|
||||
const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => {
|
||||
this.viewsModel.setCollapsed(viewDescriptor.id, collapsed);
|
||||
});
|
||||
|
||||
this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable));
|
||||
panesToAdd.push({ pane, size: size || pane.minimumSize, index });
|
||||
}
|
||||
|
||||
this.addPanes(panesToAdd);
|
||||
this.restoreViewSizes();
|
||||
|
||||
const panes: ViewletPane[] = [];
|
||||
for (const { pane } of panesToAdd) {
|
||||
pane.setVisible(this.isVisible());
|
||||
panes.push(pane);
|
||||
}
|
||||
return panes;
|
||||
}
|
||||
|
||||
private onDidRemoveViews(removed: IViewDescriptorRef[]): void {
|
||||
removed = removed.sort((a, b) => b.index - a.index);
|
||||
const panesToRemove: ViewletPane[] = [];
|
||||
for (const { index } of removed) {
|
||||
const [disposable] = this.viewDisposables.splice(index, 1);
|
||||
disposable.dispose();
|
||||
panesToRemove.push(this.panes[index]);
|
||||
}
|
||||
this.removePanes(panesToRemove);
|
||||
dispose(panesToRemove);
|
||||
}
|
||||
|
||||
private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const actions: IAction[] = this.getContextMenuActions(viewDescriptor);
|
||||
|
||||
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => actions
|
||||
});
|
||||
}
|
||||
|
||||
protected toggleViewVisibility(viewId: string): void {
|
||||
const visible = !this.viewsModel.isVisible(viewId);
|
||||
type ViewsToggleVisibilityClassification = {
|
||||
viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
};
|
||||
this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible });
|
||||
this.viewsModel.setVisible(viewId, visible);
|
||||
}
|
||||
|
||||
private saveViewSizes(): void {
|
||||
// Save size only when the layout has happened
|
||||
if (this.didLayout) {
|
||||
for (const view of this.panes) {
|
||||
this.viewsModel.setSize(view.id, this.getPaneSize(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private restoreViewSizes(): void {
|
||||
// Restore sizes only when the layout has happened
|
||||
if (this.didLayout) {
|
||||
let initialSizes;
|
||||
for (let i = 0; i < this.viewsModel.visibleViewDescriptors.length; i++) {
|
||||
const pane = this.panes[i];
|
||||
const viewDescriptor = this.viewsModel.visibleViewDescriptors[i];
|
||||
const size = this.viewsModel.getSize(viewDescriptor.id);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
this.resizePane(pane, size);
|
||||
} else {
|
||||
initialSizes = initialSizes ? initialSizes : this.computeInitialSizes();
|
||||
this.resizePane(pane, initialSizes.get(pane.id) || 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private computeInitialSizes(): Map<string, number> {
|
||||
const sizes: Map<string, number> = new Map<string, number>();
|
||||
if (this.dimension) {
|
||||
const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0);
|
||||
for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) {
|
||||
sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight);
|
||||
}
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
protected saveState(): void {
|
||||
this.panes.forEach((view) => view.saveState());
|
||||
this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE);
|
||||
|
||||
super.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
|
||||
export abstract class FilterViewPaneContainer extends ViewPaneContainer {
|
||||
private constantViewDescriptors: Map<string, IViewDescriptor> = new Map();
|
||||
private allViews: Map<string, Map<string, IViewDescriptor>> = new Map();
|
||||
private filterValue: string | undefined;
|
||||
@@ -345,7 +45,8 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(viewletId, `${viewletId}.state`, false, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
|
||||
super(viewletId, `${viewletId}.state`, { showHeaderInTitleWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService);
|
||||
this._register(onDidChangeFilterValue(newFilterValue => {
|
||||
this.filterValue = newFilterValue;
|
||||
this.onFilterChanged(newFilterValue);
|
||||
@@ -397,7 +98,7 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
|
||||
}));
|
||||
|
||||
result.push(...viewToggleActions);
|
||||
const parentActions = this.getViewletContextMenuActions();
|
||||
const parentActions = super.getContextMenuActions();
|
||||
if (viewToggleActions.length && parentActions.length) {
|
||||
result.push(new Separator());
|
||||
}
|
||||
@@ -423,8 +124,8 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet {
|
||||
return views;
|
||||
}
|
||||
|
||||
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] {
|
||||
const panes: ViewletPane[] = super.onDidAddViews(added);
|
||||
onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] {
|
||||
const panes: ViewPane[] = super.onDidAddViews(added);
|
||||
for (let i = 0; i < added.length; i++) {
|
||||
if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) {
|
||||
panes[i].setExpanded(false);
|
||||
|
||||
@@ -9,45 +9,57 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { AbstractTree } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { PaneComposite } from 'vs/workbench/browser/panecomposite';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
|
||||
export abstract class Viewlet extends Composite implements IViewlet {
|
||||
export abstract class Viewlet extends PaneComposite implements IViewlet {
|
||||
|
||||
constructor(id: string,
|
||||
protected configurationService: IConfigurationService,
|
||||
private layoutService: IWorkbenchLayoutService,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
viewPaneContainer: ViewPaneContainer,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService protected storageService: IStorageService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@IExtensionService protected extensionService: IExtensionService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
|
||||
@IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService,
|
||||
@IConfigurationService protected configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
getOptimalWidth(): number | undefined {
|
||||
return undefined;
|
||||
super(id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
const parentActions = [...super.getContextMenuActions()];
|
||||
if (parentActions.length) {
|
||||
parentActions.push(new Separator());
|
||||
}
|
||||
|
||||
const toggleSidebarPositionAction = new ToggleSidebarPositionAction(ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService), this.layoutService, this.configurationService);
|
||||
return [toggleSidebarPositionAction,
|
||||
<IAction>{
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
enabled: true,
|
||||
run: () => this.layoutService.setSideBarHidden(true)
|
||||
}];
|
||||
return [...parentActions, toggleSidebarPositionAction,
|
||||
<IAction>{
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
enabled: true,
|
||||
run: () => this.layoutService.setSideBarHidden(true)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +76,7 @@ export class ViewletDescriptor extends CompositeDescriptor<Viewlet> {
|
||||
order?: number,
|
||||
iconUrl?: URI
|
||||
): ViewletDescriptor {
|
||||
|
||||
return new ViewletDescriptor(ctor as IConstructorSignature0<Viewlet>, id, name, cssClass, order, iconUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { getZoomLevel, isFirefox, isSafari, isChrome } from 'vs/base/browser/bro
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform';
|
||||
import { isWindows, isLinux, isWeb, isNative, isMacintosh } from 'vs/base/common/platform';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
|
||||
@@ -252,6 +252,10 @@ export class Workbench extends Layout {
|
||||
|
||||
private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined;
|
||||
private setFontAliasing(configurationService: IConfigurationService) {
|
||||
if (!isMacintosh) {
|
||||
return; // macOS only
|
||||
}
|
||||
|
||||
const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing');
|
||||
if (this.fontAliasing === aliasing) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user