mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -57,13 +57,7 @@ export class ContributableActionProvider implements IActionProvider {
|
||||
const context = this.toContext(tree, element);
|
||||
|
||||
const contributors = this.registry.getActionBarContributors(Scope.VIEWER);
|
||||
for (const contributor of contributors) {
|
||||
if (contributor.hasActions(context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return contributors.some(contributor => contributor.hasActions(context));
|
||||
}
|
||||
|
||||
getActions(tree: ITree, element: unknown): ReadonlyArray<IAction> {
|
||||
@@ -156,7 +150,7 @@ export interface IActionBarRegistry {
|
||||
class ActionBarRegistry implements IActionBarRegistry {
|
||||
private readonly actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0<ActionBarContributor>; }[] = [];
|
||||
private readonly actionBarContributorInstances: Map<string, ActionBarContributor[]> = new Map();
|
||||
private instantiationService!: IInstantiationService;
|
||||
private instantiationService: IInstantiationService | undefined;
|
||||
|
||||
start(accessor: ServicesAccessor): void {
|
||||
this.instantiationService = accessor.get(IInstantiationService);
|
||||
@@ -168,13 +162,15 @@ class ActionBarRegistry implements IActionBarRegistry {
|
||||
}
|
||||
|
||||
private createActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void {
|
||||
const instance = this.instantiationService.createInstance(ctor);
|
||||
let target = this.actionBarContributorInstances.get(scope);
|
||||
if (!target) {
|
||||
target = [];
|
||||
this.actionBarContributorInstances.set(scope, target);
|
||||
if (this.instantiationService) {
|
||||
const instance = this.instantiationService.createInstance(ctor);
|
||||
let target = this.actionBarContributorInstances.get(scope);
|
||||
if (!target) {
|
||||
target = [];
|
||||
this.actionBarContributorInstances.set(scope, target);
|
||||
}
|
||||
target.push(instance);
|
||||
}
|
||||
target.push(instance);
|
||||
}
|
||||
|
||||
private getContributors(scope: string): ActionBarContributor[] {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'v
|
||||
class InspectContextKeysAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.inspectContextKeys';
|
||||
static LABEL = nls.localize('inspect context keys', "Inspect Context Keys");
|
||||
static readonly LABEL = nls.localize('inspect context keys', "Inspect Context Keys");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -91,7 +91,7 @@ class InspectContextKeysAction extends Action {
|
||||
class ToggleScreencastModeAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleScreencastMode';
|
||||
static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode");
|
||||
static readonly LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode");
|
||||
|
||||
static disposable: IDisposable | undefined;
|
||||
|
||||
@@ -195,7 +195,7 @@ class ToggleScreencastModeAction extends Action {
|
||||
class LogStorageAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.logStorage';
|
||||
static LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents");
|
||||
static readonly LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -16,13 +16,14 @@ import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/ed
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { MenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { isWindows, isLinux, isWeb } from 'vs/base/common/platform';
|
||||
import { IsMacNativeContext } from 'vs/workbench/browser/contextkeys';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { InEditorZenModeContext, IsCenteredLayoutContext } from 'vs/workbench/common/editor';
|
||||
import { InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext } from 'vs/workbench/common/editor';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
const viewCategory = nls.localize('view', "View");
|
||||
@@ -232,6 +233,16 @@ export class ToggleEditorVisibilityAction extends Action {
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory);
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
group: '2_workbench_layout',
|
||||
command: {
|
||||
id: ToggleEditorVisibilityAction.ID,
|
||||
title: nls.localize({ key: 'miShowEditorArea', comment: ['&& denotes a mnemonic'] }, "Show &&Editor Area"),
|
||||
toggled: EditorAreaVisibleContext
|
||||
},
|
||||
order: 5
|
||||
});
|
||||
|
||||
export class ToggleSidebarVisibilityAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleSidebarVisibility';
|
||||
@@ -340,7 +351,7 @@ class ToggleTabsVisibilityAction extends Action {
|
||||
}
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, {
|
||||
primary: undefined!,
|
||||
primary: undefined,
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, },
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }
|
||||
}), 'View: Toggle Tab Visibility', viewCategory);
|
||||
@@ -396,20 +407,21 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
export class ToggleMenuBarAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleMenuBar';
|
||||
static LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
|
||||
static readonly LABEL = nls.localize('toggleMenuBar', "Toggle Menu Bar");
|
||||
|
||||
private static readonly menuBarVisibilityKey = 'window.menuBarVisibility';
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
let currentVisibilityValue = this.configurationService.getValue<MenuBarVisibility>(ToggleMenuBarAction.menuBarVisibilityKey);
|
||||
let currentVisibilityValue = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
if (typeof currentVisibilityValue !== 'string') {
|
||||
currentVisibilityValue = 'default';
|
||||
}
|
||||
@@ -417,8 +429,10 @@ export class ToggleMenuBarAction extends Action {
|
||||
let newVisibilityValue: string;
|
||||
if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'default') {
|
||||
newVisibilityValue = 'toggle';
|
||||
} else if (currentVisibilityValue === 'compact') {
|
||||
newVisibilityValue = 'hidden';
|
||||
} else {
|
||||
newVisibilityValue = 'default';
|
||||
newVisibilityValue = (isWeb && currentVisibilityValue === 'hidden') ? 'compact' : 'default';
|
||||
}
|
||||
|
||||
this.configurationService.updateValue(ToggleMenuBarAction.menuBarVisibilityKey, newVisibilityValue, ConfigurationTarget.USER);
|
||||
@@ -446,7 +460,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
|
||||
|
||||
export abstract class BaseResizeViewAction extends Action {
|
||||
|
||||
protected static RESIZE_INCREMENT = 6.5; // This is a media-size percentage
|
||||
protected static readonly RESIZE_INCREMENT = 6.5; // This is a media-size percentage
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -17,6 +17,11 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
|
||||
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
|
||||
function isLegacyTree(widget: ListWidget): widget is ITree {
|
||||
return widget instanceof Tree;
|
||||
}
|
||||
|
||||
function ensureDOMFocus(widget: ListWidget | undefined): void {
|
||||
// it can happen that one of the commands is executed while
|
||||
@@ -552,7 +557,7 @@ function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boo
|
||||
else if (focused) {
|
||||
const tree = focused;
|
||||
|
||||
tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined);
|
||||
tree.focusFirst({ origin: 'keyboard' }, options?.fromFocused ? tree.getFocus() : undefined);
|
||||
tree.reveal(tree.getFocus());
|
||||
}
|
||||
}
|
||||
@@ -604,7 +609,7 @@ function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: bool
|
||||
else if (focused) {
|
||||
const tree = focused;
|
||||
|
||||
tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : undefined);
|
||||
tree.focusLast({ origin: 'keyboard' }, options?.fromFocused ? tree.getFocus() : undefined);
|
||||
tree.reveal(tree.getFocus());
|
||||
}
|
||||
}
|
||||
@@ -833,3 +838,67 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.scrollUp',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.UpArrow,
|
||||
handler: accessor => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (!focused || isLegacyTree(focused)) {
|
||||
return;
|
||||
}
|
||||
|
||||
focused.scrollTop -= 10;
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.scrollDown',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
|
||||
handler: accessor => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (!focused || isLegacyTree(focused)) {
|
||||
return;
|
||||
}
|
||||
|
||||
focused.scrollTop += 10;
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.scrollLeft',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.LeftArrow,
|
||||
handler: accessor => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (!focused || isLegacyTree(focused)) {
|
||||
return;
|
||||
}
|
||||
|
||||
focused.scrollLeft -= 10;
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'list.scrollRight',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: WorkbenchListFocusContextKey,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.RightArrow,
|
||||
handler: accessor => {
|
||||
const focused = accessor.get(IListService).lastFocusedList;
|
||||
|
||||
if (!focused || isLegacyTree(focused)) {
|
||||
return;
|
||||
}
|
||||
|
||||
focused.scrollLeft += 10;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,9 +68,19 @@ abstract class BaseNavigationAction extends Action {
|
||||
return false;
|
||||
}
|
||||
|
||||
const activePanelId = this.panelService.getActivePanel()!.getId();
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
if (!activePanel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.panelService.openPanel(activePanelId, true)!;
|
||||
const activePanelId = activePanel.getId();
|
||||
|
||||
const res = this.panelService.openPanel(activePanelId, true);
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
protected async navigateToSidebar(): Promise<IViewlet | boolean> {
|
||||
@@ -84,8 +94,8 @@ abstract class BaseNavigationAction extends Action {
|
||||
}
|
||||
const activeViewletId = activeViewlet.getId();
|
||||
|
||||
const value = await this.viewletService.openViewlet(activeViewletId, true);
|
||||
return value === null ? false : value;
|
||||
const viewlet = await this.viewletService.openViewlet(activeViewletId, true);
|
||||
return !!viewlet;
|
||||
}
|
||||
|
||||
protected navigateAcrossEditorGroup(direction: GroupDirection): boolean {
|
||||
|
||||
@@ -37,7 +37,7 @@ export const inRecentFilesPickerContextKey = 'inRecentFilesPicker';
|
||||
abstract class BaseOpenRecentAction extends Action {
|
||||
|
||||
private removeFromRecentlyOpened: IQuickInputButton = {
|
||||
iconClass: 'action-remove-from-recently-opened',
|
||||
iconClass: 'codicon-close',
|
||||
tooltip: nls.localize('remove', "Remove from Recently Opened")
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ abstract class BaseOpenRecentAction extends Action {
|
||||
});
|
||||
|
||||
if (pick) {
|
||||
return this.hostService.openWindow([pick.openable], { forceNewWindow: keyMods && keyMods.ctrlCmd });
|
||||
return this.hostService.openWindow([pick.openable], { forceNewWindow: keyMods?.ctrlCmd });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ class QuickOpenRecentAction extends BaseOpenRecentAction {
|
||||
class ToggleFullScreenAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleFullScreen';
|
||||
static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");
|
||||
static readonly LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -211,7 +211,7 @@ class ToggleFullScreenAction extends Action {
|
||||
export class ReloadWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.reloadWindow';
|
||||
static LABEL = nls.localize('reloadWindow', "Reload Window");
|
||||
static readonly LABEL = nls.localize('reloadWindow', "Reload Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -249,7 +249,7 @@ class ShowAboutDialogAction extends Action {
|
||||
export class NewWindowAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.newWindow';
|
||||
static LABEL = nls.localize('newWindow', "New Window");
|
||||
static readonly LABEL = nls.localize('newWindow', "New Window");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -21,11 +21,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFile';
|
||||
static LABEL = nls.localize('openFile', "Open File...");
|
||||
static readonly LABEL = nls.localize('openFile', "Open File...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -43,7 +44,7 @@ export class OpenFileAction extends Action {
|
||||
export class OpenFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFolder';
|
||||
static LABEL = nls.localize('openFolder', "Open Folder...");
|
||||
static readonly LABEL = nls.localize('openFolder', "Open Folder...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -61,7 +62,7 @@ export class OpenFolderAction extends Action {
|
||||
export class OpenFileFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFileFolder';
|
||||
static LABEL = nls.localize('openFileFolder', "Open...");
|
||||
static readonly LABEL = nls.localize('openFileFolder', "Open...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -79,7 +80,7 @@ export class OpenFileFolderAction extends Action {
|
||||
export class OpenWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.openWorkspace';
|
||||
static LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
|
||||
static readonly LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -97,14 +98,15 @@ export class OpenWorkspaceAction extends Action {
|
||||
export class CloseWorkspaceAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closeFolder';
|
||||
static LABEL = nls.localize('closeWorkspace', "Close Workspace");
|
||||
static readonly LABEL = nls.localize('closeWorkspace', "Close Workspace");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IHostService private readonly hostService: IHostService
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -116,7 +118,7 @@ export class CloseWorkspaceAction extends Action {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this.hostService.closeWorkspace();
|
||||
return this.hostService.openWindow({ forceReuseWindow: true, remoteAuthority: this.environmentService.configuration.remoteAuthority });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +150,7 @@ export class OpenWorkspaceConfigFileAction extends Action {
|
||||
export class AddRootFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.addRootFolder';
|
||||
static LABEL = ADD_ROOT_FOLDER_LABEL;
|
||||
static readonly LABEL = ADD_ROOT_FOLDER_LABEL;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -166,7 +168,7 @@ export class AddRootFolderAction extends Action {
|
||||
export class GlobalRemoveRootFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.removeRootFolder';
|
||||
static LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace...");
|
||||
static readonly LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -14,6 +14,8 @@ import { IConstructorSignature0, IInstantiationService } from 'vs/platform/insta
|
||||
import { trackFocus, Dimension } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
/**
|
||||
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
|
||||
@@ -35,10 +37,10 @@ export abstract class Composite extends Component implements IComposite {
|
||||
private readonly _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility: Event<boolean> = this._onDidChangeVisibility.event;
|
||||
|
||||
private _onDidFocus!: Emitter<void>;
|
||||
private _onDidFocus: Emitter<void> | undefined;
|
||||
get onDidFocus(): Event<void> {
|
||||
if (!this._onDidFocus) {
|
||||
this.registerFocusTrackEvents();
|
||||
this._onDidFocus = this.registerFocusTrackEvents().onDidFocus;
|
||||
}
|
||||
|
||||
return this._onDidFocus.event;
|
||||
@@ -50,28 +52,32 @@ export abstract class Composite extends Component implements IComposite {
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidBlur!: Emitter<void>;
|
||||
private _onDidBlur: Emitter<void> | undefined;
|
||||
get onDidBlur(): Event<void> {
|
||||
if (!this._onDidBlur) {
|
||||
this.registerFocusTrackEvents();
|
||||
this._onDidBlur = this.registerFocusTrackEvents().onDidBlur;
|
||||
}
|
||||
|
||||
return this._onDidBlur.event;
|
||||
}
|
||||
|
||||
private registerFocusTrackEvents(): void {
|
||||
this._onDidFocus = this._register(new Emitter<void>());
|
||||
this._onDidBlur = this._register(new Emitter<void>());
|
||||
private registerFocusTrackEvents(): { onDidFocus: Emitter<void>, onDidBlur: Emitter<void> } {
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
const focusTracker = this._register(trackFocus(container));
|
||||
|
||||
const focusTracker = this._register(trackFocus(this.getContainer()));
|
||||
this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
|
||||
this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
|
||||
const onDidFocus = this._onDidFocus = this._register(new Emitter<void>());
|
||||
this._register(focusTracker.onDidFocus(() => onDidFocus.fire()));
|
||||
|
||||
const onDidBlur = this._onDidBlur = this._register(new Emitter<void>());
|
||||
this._register(focusTracker.onDidBlur(() => onDidBlur.fire()));
|
||||
|
||||
return { onDidFocus, onDidBlur };
|
||||
}
|
||||
|
||||
protected actionRunner: IActionRunner | undefined;
|
||||
|
||||
private visible: boolean;
|
||||
private parent!: HTMLElement;
|
||||
private parent: HTMLElement | undefined;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -112,7 +118,7 @@ export abstract class Composite extends Component implements IComposite {
|
||||
/**
|
||||
* Returns the container this composite is being build in.
|
||||
*/
|
||||
getContainer(): HTMLElement {
|
||||
getContainer(): HTMLElement | undefined {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@@ -253,7 +259,7 @@ export abstract class CompositeRegistry<T extends Composite> extends Disposable
|
||||
private composites: CompositeDescriptor<T>[] = [];
|
||||
|
||||
protected registerComposite(descriptor: CompositeDescriptor<T>): void {
|
||||
if (this.compositeById(descriptor.id) !== null) {
|
||||
if (this.compositeById(descriptor.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,7 +277,7 @@ export abstract class CompositeRegistry<T extends Composite> extends Disposable
|
||||
this._onDidDeregister.fire(descriptor);
|
||||
}
|
||||
|
||||
getComposite(id: string): CompositeDescriptor<T> | null {
|
||||
getComposite(id: string): CompositeDescriptor<T> | undefined {
|
||||
return this.compositeById(id);
|
||||
}
|
||||
|
||||
@@ -279,13 +285,7 @@ export abstract class CompositeRegistry<T extends Composite> extends Disposable
|
||||
return this.composites.slice(0);
|
||||
}
|
||||
|
||||
private compositeById(id: string): CompositeDescriptor<T> | null {
|
||||
for (const composite of this.composites) {
|
||||
if (composite.id === id) {
|
||||
return composite;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
private compositeById(id: string): CompositeDescriptor<T> | undefined {
|
||||
return find(this.composites, composite => composite.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IWindowsConfiguration } from 'vs/platform/windows/common/windows';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsSaveableContext, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext, IsCenteredLayoutContext, ActiveEditorGroupIndexContext, ActiveEditorGroupLastContext, ActiveEditorIsSaveableContext, toResource, SideBySideEditor, EditorAreaVisibleContext } from 'vs/workbench/common/editor';
|
||||
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { preferredSideBySideGroupDirection, GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -73,6 +73,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private isFullscreenContext: IContextKey<boolean>;
|
||||
private isCenteredLayoutContext: IContextKey<boolean>;
|
||||
private sideBarVisibleContext: IContextKey<boolean>;
|
||||
private editorAreaVisibleContext: IContextKey<boolean>;
|
||||
private panelPositionContext: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
@@ -88,41 +89,6 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
) {
|
||||
super();
|
||||
|
||||
this.initContextKeys();
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.editorGroupService.whenRestored.then(() => this.updateEditorContextKeys());
|
||||
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateEditorContextKeys()));
|
||||
|
||||
this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorGroupService.onDidGroupIndexChange(() => this.updateEditorContextKeys()));
|
||||
|
||||
this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true));
|
||||
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateWorkspaceFolderCountContextKey()));
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {
|
||||
this.updateSplitEditorsVerticallyContext();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.layoutService.onZenModeChange(enabled => this.inZenModeContext.set(enabled)));
|
||||
this._register(this.layoutService.onFullscreenChange(fullscreen => this.isFullscreenContext.set(fullscreen)));
|
||||
this._register(this.layoutService.onCenteredLayoutChange(centered => this.isCenteredLayoutContext.set(centered)));
|
||||
this._register(this.layoutService.onPanelPositionChange(position => this.panelPositionContext.set(position)));
|
||||
|
||||
this._register(this.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys()));
|
||||
this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys()));
|
||||
}
|
||||
|
||||
private initContextKeys(): void {
|
||||
|
||||
// Platform
|
||||
IsMacContext.bindTo(this.contextKeyService);
|
||||
@@ -136,7 +102,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
// macOS Native Tabs
|
||||
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
|
||||
HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs);
|
||||
HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig?.window?.nativeTabs);
|
||||
|
||||
// Development
|
||||
IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment);
|
||||
@@ -181,12 +147,49 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
// Centered Layout
|
||||
this.isCenteredLayoutContext = IsCenteredLayoutContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Editor Area
|
||||
this.editorAreaVisibleContext = EditorAreaVisibleContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Sidebar
|
||||
this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Panel Position
|
||||
this.panelPositionContext = PanelPositionContext.bindTo(this.contextKeyService);
|
||||
this.panelPositionContext.set(this.layoutService.getPanelPosition() === Position.RIGHT ? 'right' : 'bottom');
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.editorGroupService.whenRestored.then(() => this.updateEditorContextKeys());
|
||||
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateEditorContextKeys()));
|
||||
|
||||
this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorContextKeys()));
|
||||
this._register(this.editorGroupService.onDidGroupIndexChange(() => this.updateEditorContextKeys()));
|
||||
|
||||
this._register(addDisposableListener(window, EventType.FOCUS_IN, () => this.updateInputContextKeys(), true));
|
||||
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateWorkbenchStateContextKey()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateWorkspaceFolderCountContextKey()));
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) {
|
||||
this.updateSplitEditorsVerticallyContext();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.layoutService.onZenModeChange(enabled => this.inZenModeContext.set(enabled)));
|
||||
this._register(this.layoutService.onFullscreenChange(fullscreen => this.isFullscreenContext.set(fullscreen)));
|
||||
this._register(this.layoutService.onCenteredLayoutChange(centered => this.isCenteredLayoutContext.set(centered)));
|
||||
this._register(this.layoutService.onPanelPositionChange(position => this.panelPositionContext.set(position)));
|
||||
|
||||
this._register(this.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys()));
|
||||
this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys()));
|
||||
|
||||
this._register(this.layoutService.onPartVisibilityChange(() => this.editorAreaVisibleContext.set(this.layoutService.isVisible(Parts.EDITOR_PART))));
|
||||
}
|
||||
|
||||
private updateEditorContextKeys(): void {
|
||||
@@ -194,7 +197,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
const visibleEditors = this.editorService.visibleControls;
|
||||
|
||||
this.textCompareEditorActiveContext.set(!!activeControl && activeControl.getId() === TEXT_DIFF_EDITOR_ID);
|
||||
this.textCompareEditorActiveContext.set(activeControl?.getId() === TEXT_DIFF_EDITOR_ID);
|
||||
this.textCompareEditorVisibleContext.set(visibleEditors.some(control => control.getId() === TEXT_DIFF_EDITOR_ID));
|
||||
|
||||
if (visibleEditors.length > 0) {
|
||||
|
||||
@@ -113,7 +113,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
|
||||
if (e.dataTransfer && e.dataTransfer.files) {
|
||||
for (let i = 0; i < e.dataTransfer.files.length; i++) {
|
||||
const file = e.dataTransfer.files[i];
|
||||
if (file && file.path /* Electron only */ && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) {
|
||||
if (file?.path /* Electron only */ && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) {
|
||||
try {
|
||||
resources.push({ resource: URI.file(file.path), isExternal: true });
|
||||
} catch (error) {
|
||||
@@ -243,11 +243,13 @@ export class ResourcesDropHandler {
|
||||
}
|
||||
|
||||
// Resolve the contents of the dropped dirty resource from source
|
||||
try {
|
||||
const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource!));
|
||||
await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true));
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
if (droppedDirtyEditor.backupResource) {
|
||||
try {
|
||||
const content = await this.backupFileService.resolveBackupContent((droppedDirtyEditor.backupResource));
|
||||
await this.backupFileService.backupResource(droppedDirtyEditor.resource, content.value.create(this.getDefaultEOL()).createSnapshot(true));
|
||||
} catch (e) {
|
||||
// Ignore error
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -358,7 +360,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
for (const textEditorWidget of textEditorWidgets) {
|
||||
if (isCodeEditor(textEditorWidget)) {
|
||||
const model = textEditorWidget.getModel();
|
||||
if (model && model.uri && model.uri.toString() === file.resource.toString()) {
|
||||
if (model?.uri?.toString() === file.resource.toString()) {
|
||||
viewState = textEditorWidget.saveViewState();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
export interface IEditorDescriptor {
|
||||
instantiate(instantiationService: IInstantiationService): BaseEditor;
|
||||
@@ -140,13 +141,7 @@ class EditorRegistry implements IEditorRegistry {
|
||||
}
|
||||
|
||||
getEditorById(editorId: string): EditorDescriptor | undefined {
|
||||
for (const editor of this.editors) {
|
||||
if (editor.getId() === editorId) {
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return find(this.editors, editor => editor.getId() === editorId);
|
||||
}
|
||||
|
||||
getEditors(): readonly EditorDescriptor[] {
|
||||
|
||||
@@ -211,7 +211,7 @@ export class ResourceLabel extends ResourceLabels {
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
options: IIconLabelCreationOptions,
|
||||
options: IIconLabelCreationOptions | undefined,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@@ -252,7 +252,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
options: IIconLabelCreationOptions,
|
||||
options: IIconLabelCreationOptions | undefined,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IDecorationsService private readonly decorationsService: IDecorationsService,
|
||||
@@ -434,7 +434,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
return;
|
||||
}
|
||||
|
||||
const iconLabelOptions: IIconLabelValueOptions = {
|
||||
const iconLabelOptions: IIconLabelValueOptions & { extraClasses: string[] } = {
|
||||
title: '',
|
||||
italic: this.options && this.options.italic,
|
||||
matches: this.options && this.options.matches,
|
||||
@@ -462,7 +462,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
|
||||
if (this.options && this.options.extraClasses) {
|
||||
iconLabelOptions.extraClasses!.push(...this.options.extraClasses);
|
||||
iconLabelOptions.extraClasses.push(...this.options.extraClasses);
|
||||
}
|
||||
|
||||
if (this.options && this.options.fileDecorations && resource) {
|
||||
@@ -477,11 +477,11 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
|
||||
if (this.options.fileDecorations.colors) {
|
||||
iconLabelOptions.extraClasses!.push(deco.labelClassName);
|
||||
iconLabelOptions.extraClasses.push(deco.labelClassName);
|
||||
}
|
||||
|
||||
if (this.options.fileDecorations.badges) {
|
||||
iconLabelOptions.extraClasses!.push(deco.badgeClassName);
|
||||
iconLabelOptions.extraClasses.push(deco.badgeClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { LifecyclePhase, StartupKind, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -36,9 +36,10 @@ import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/a
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { INotificationService, NotificationsFilter } from 'vs/platform/notification/common/notification';
|
||||
|
||||
enum Settings {
|
||||
MENUBAR_VISIBLE = 'window.menuBarVisibility',
|
||||
ACTIVITYBAR_VISIBLE = 'workbench.activityBar.visible',
|
||||
STATUSBAR_VISIBLE = 'workbench.statusBar.visible',
|
||||
|
||||
@@ -46,7 +47,6 @@ enum Settings {
|
||||
PANEL_POSITION = 'workbench.panel.defaultLocation',
|
||||
|
||||
ZEN_MODE_RESTORE = 'zenMode.restore',
|
||||
|
||||
}
|
||||
|
||||
enum Storage {
|
||||
@@ -83,9 +83,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
//#region Events
|
||||
|
||||
private readonly _onTitleBarVisibilityChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onTitleBarVisibilityChange: Event<void> = this._onTitleBarVisibilityChange.event;
|
||||
|
||||
private readonly _onZenModeChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onZenModeChange: Event<boolean> = this._onZenModeChange.event;
|
||||
|
||||
@@ -98,12 +95,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
private readonly _onPanelPositionChange: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onPanelPositionChange: Event<string> = this._onPanelPositionChange.event;
|
||||
|
||||
private readonly _onPartVisibilityChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onPartVisibilityChange: Event<void> = this._onPartVisibilityChange.event;
|
||||
|
||||
private readonly _onLayout = this._register(new Emitter<IDimension>());
|
||||
readonly onLayout: Event<IDimension> = this._onLayout.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
private _dimension: IDimension;
|
||||
private _dimension!: IDimension;
|
||||
get dimension(): IDimension { return this._dimension; }
|
||||
|
||||
private _container: HTMLElement = document.createElement('div');
|
||||
@@ -111,29 +111,30 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
private parts: Map<string, Part> = new Map<string, Part>();
|
||||
|
||||
private workbenchGrid: SerializableGrid<ISerializableView>;
|
||||
private workbenchGrid!: SerializableGrid<ISerializableView>;
|
||||
|
||||
private disposed: boolean;
|
||||
private disposed: boolean | undefined;
|
||||
|
||||
private titleBarPartView: ISerializableView;
|
||||
private activityBarPartView: ISerializableView;
|
||||
private sideBarPartView: ISerializableView;
|
||||
private panelPartView: ISerializableView;
|
||||
private editorPartView: ISerializableView;
|
||||
private statusBarPartView: ISerializableView;
|
||||
private titleBarPartView!: ISerializableView;
|
||||
private activityBarPartView!: ISerializableView;
|
||||
private sideBarPartView!: ISerializableView;
|
||||
private panelPartView!: ISerializableView;
|
||||
private editorPartView!: ISerializableView;
|
||||
private statusBarPartView!: ISerializableView;
|
||||
|
||||
private environmentService: IWorkbenchEnvironmentService;
|
||||
private configurationService: IConfigurationService;
|
||||
private lifecycleService: ILifecycleService;
|
||||
private storageService: IStorageService;
|
||||
private hostService: IHostService;
|
||||
private editorService: IEditorService;
|
||||
private editorGroupService: IEditorGroupsService;
|
||||
private panelService: IPanelService;
|
||||
private titleService: ITitleService;
|
||||
private viewletService: IViewletService;
|
||||
private contextService: IWorkspaceContextService;
|
||||
private backupFileService: IBackupFileService;
|
||||
private environmentService!: IWorkbenchEnvironmentService;
|
||||
private configurationService!: IConfigurationService;
|
||||
private lifecycleService!: ILifecycleService;
|
||||
private storageService!: IStorageService;
|
||||
private hostService!: IHostService;
|
||||
private editorService!: IEditorService;
|
||||
private editorGroupService!: IEditorGroupsService;
|
||||
private panelService!: IPanelService;
|
||||
private titleService!: ITitleService;
|
||||
private viewletService!: IViewletService;
|
||||
private contextService!: IWorkspaceContextService;
|
||||
private backupFileService!: IBackupFileService;
|
||||
private notificationService!: INotificationService;
|
||||
|
||||
protected readonly state = {
|
||||
fullscreen: false,
|
||||
@@ -181,7 +182,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
transitionedToCenteredEditorLayout: false,
|
||||
wasSideBarVisible: false,
|
||||
wasPanelVisible: false,
|
||||
transitionDisposables: new DisposableStore()
|
||||
transitionDisposables: new DisposableStore(),
|
||||
setNotificationsFilter: false
|
||||
},
|
||||
|
||||
};
|
||||
@@ -209,6 +211,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.panelService = accessor.get(IPanelService);
|
||||
this.viewletService = accessor.get(IViewletService);
|
||||
this.titleService = accessor.get(ITitleService);
|
||||
this.notificationService = accessor.get(INotificationService);
|
||||
accessor.get(IStatusbarService); // not used, but called to ensure instantiated
|
||||
accessor.get(IActivityBarService); // not used, but called to ensure instantiated
|
||||
|
||||
@@ -261,8 +264,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.menuBar.toggled = visible;
|
||||
|
||||
if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) {
|
||||
this._onTitleBarVisibilityChange.fire();
|
||||
|
||||
// Propagate to grid
|
||||
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
|
||||
|
||||
@@ -287,8 +288,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
this._onTitleBarVisibilityChange.fire();
|
||||
|
||||
// Propagate to grid
|
||||
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
|
||||
|
||||
@@ -326,7 +325,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
|
||||
// Menubar visibility
|
||||
const newMenubarVisibility = this.configurationService.getValue<MenuBarVisibility>(Settings.MENUBAR_VISIBLE);
|
||||
const newMenubarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
this.setMenubarVisibility(newMenubarVisibility, !!skipLayout);
|
||||
|
||||
}
|
||||
@@ -340,10 +339,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.sideBar.position = position;
|
||||
|
||||
// Adjust CSS
|
||||
removeClass(activityBar.getContainer(), oldPositionValue);
|
||||
removeClass(sideBar.getContainer(), oldPositionValue);
|
||||
addClass(activityBar.getContainer(), newPositionValue);
|
||||
addClass(sideBar.getContainer(), newPositionValue);
|
||||
const activityBarContainer = assertIsDefined(activityBar.getContainer());
|
||||
const sideBarContainer = assertIsDefined(sideBar.getContainer());
|
||||
removeClass(activityBarContainer, oldPositionValue);
|
||||
removeClass(sideBarContainer, oldPositionValue);
|
||||
addClass(activityBarContainer, newPositionValue);
|
||||
addClass(sideBarContainer, newPositionValue);
|
||||
|
||||
// Update Styles
|
||||
activityBar.updateStyles();
|
||||
@@ -371,7 +372,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.fullscreen = isFullscreen();
|
||||
|
||||
// Menubar visibility
|
||||
this.state.menuBar.visibility = this.configurationService.getValue<MenuBarVisibility>(Settings.MENUBAR_VISIBLE);
|
||||
this.state.menuBar.visibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
|
||||
// Activity bar visibility
|
||||
this.state.activityBar.hidden = !this.configurationService.getValue<string>(Settings.ACTIVITYBAR_VISIBLE);
|
||||
@@ -455,7 +456,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Files to diff is exclusive
|
||||
return pathsToEditors(configuration.filesToDiff, fileService).then(filesToDiff => {
|
||||
if (filesToDiff && filesToDiff.length === 2) {
|
||||
if (filesToDiff?.length === 2) {
|
||||
return [{
|
||||
leftResource: filesToDiff[0].resource,
|
||||
rightResource: filesToDiff[1].resource,
|
||||
@@ -528,10 +529,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
const container = this.getContainer(part);
|
||||
|
||||
return isAncestor(activeElement, container);
|
||||
return !!container && isAncestor(activeElement, container);
|
||||
}
|
||||
|
||||
getContainer(part: Parts): HTMLElement {
|
||||
getContainer(part: Parts): HTMLElement | undefined {
|
||||
switch (part) {
|
||||
case Parts.TITLEBAR_PART:
|
||||
return this.getPart(Parts.TITLEBAR_PART).getContainer();
|
||||
@@ -579,7 +580,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
return true; // any other part cannot be hidden
|
||||
}
|
||||
|
||||
getDimension(part: Parts): Dimension {
|
||||
getDimension(part: Parts): Dimension | undefined {
|
||||
return this.getPart(part).dimension;
|
||||
}
|
||||
|
||||
@@ -647,6 +648,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
hideActivityBar: boolean;
|
||||
hideStatusBar: boolean;
|
||||
hideLineNumbers: boolean;
|
||||
silentNotifications: boolean;
|
||||
} = this.configurationService.getValue('zenMode');
|
||||
|
||||
toggleFullScreen = !this.state.fullscreen && config.fullScreen;
|
||||
@@ -676,6 +678,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.state.zenMode.transitionDisposables.add(this.editorGroupService.enforcePartOptions({ showTabs: false }));
|
||||
}
|
||||
|
||||
this.state.zenMode.setNotificationsFilter = config.silentNotifications;
|
||||
if (config.silentNotifications) {
|
||||
this.notificationService.setFilter(NotificationsFilter.ERROR);
|
||||
}
|
||||
|
||||
if (config.centerLayout) {
|
||||
this.centerEditorLayout(true, true);
|
||||
}
|
||||
@@ -701,6 +708,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.doUpdateLayoutConfiguration(true);
|
||||
|
||||
this.editorGroupService.activeGroup.focus();
|
||||
if (this.state.zenMode.setNotificationsFilter) {
|
||||
this.notificationService.setFilter(NotificationsFilter.OFF);
|
||||
}
|
||||
|
||||
toggleFullScreen = this.state.zenMode.transitionedToFullScreen && this.state.fullscreen;
|
||||
}
|
||||
@@ -747,7 +757,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.workbenchGrid.setViewVisible(this.statusBarPartView, !hidden);
|
||||
}
|
||||
|
||||
protected createWorkbenchLayout(instantiationService: IInstantiationService): void {
|
||||
protected createWorkbenchLayout(): void {
|
||||
const titleBar = this.getPart(Parts.TITLEBAR_PART);
|
||||
const editorPart = this.getPart(Parts.EDITOR_PART);
|
||||
const activityBar = this.getPart(Parts.ACTIVITYBAR_PART);
|
||||
@@ -782,17 +792,18 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.container.prepend(workbenchGrid.element);
|
||||
this.workbenchGrid = workbenchGrid;
|
||||
|
||||
this._register((this.sideBarPartView as SidebarPart).onDidVisibilityChange((visible) => {
|
||||
this.setSideBarHidden(!visible, true);
|
||||
}));
|
||||
|
||||
this._register((this.panelPartView as PanelPart).onDidVisibilityChange((visible) => {
|
||||
this.setPanelHidden(!visible, true);
|
||||
}));
|
||||
|
||||
this._register((this.editorPartView as PanelPart).onDidVisibilityChange((visible) => {
|
||||
this.setEditorHidden(!visible, true);
|
||||
}));
|
||||
[titleBar, editorPart, activityBar, panelPart, sideBar, statusBar].forEach((part: Part) => {
|
||||
this._register(part.onDidVisibilityChange((visible) => {
|
||||
this._onPartVisibilityChange.fire();
|
||||
if (part === sideBar) {
|
||||
this.setSideBarHidden(!visible, true);
|
||||
} else if (part === panelPart) {
|
||||
this.setPanelHidden(!visible, true);
|
||||
} else if (part === editorPart) {
|
||||
this.setEditorHidden(!visible, true);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
this._register(this.storageService.onWillSaveState(() => {
|
||||
const grid = this.workbenchGrid as SerializableGrid<ISerializableView>;
|
||||
@@ -1121,8 +1132,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
this.storageService.store(Storage.PANEL_POSITION, positionToString(this.state.panel.position), StorageScope.WORKSPACE);
|
||||
|
||||
// Adjust CSS
|
||||
removeClass(panelPart.getContainer(), oldPositionValue);
|
||||
addClass(panelPart.getContainer(), newPositionValue);
|
||||
const panelContainer = assertIsDefined(panelPart.getContainer());
|
||||
removeClass(panelContainer, oldPositionValue);
|
||||
addClass(panelContainer, newPositionValue);
|
||||
|
||||
// Update Styles
|
||||
panelPart.updateStyles();
|
||||
@@ -1161,7 +1173,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width);
|
||||
const height = this.storageService.getNumber(Storage.GRID_HEIGHT, StorageScope.GLOBAL, workbenchDimensions.height);
|
||||
// At some point, we will not fall back to old keys from legacy layout, but for now, let's migrate the keys
|
||||
const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300))!);
|
||||
const sideBarSize = this.storageService.getNumber(Storage.SIDEBAR_SIZE, StorageScope.GLOBAL, this.storageService.getNumber('workbench.sidebar.width', StorageScope.GLOBAL, Math.min(workbenchDimensions.width / 4, 300)));
|
||||
const panelSize = this.storageService.getNumber(Storage.PANEL_SIZE, StorageScope.GLOBAL, this.storageService.getNumber(this.state.panel.position === Position.BOTTOM ? 'workbench.panel.height' : 'workbench.panel.width', StorageScope.GLOBAL, workbenchDimensions.height / 3));
|
||||
|
||||
const titleBarHeight = this.titleBarPartView.minimumHeight;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isAncestor } from 'vs/base/browser/dom';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export abstract class Panel extends Composite implements IPanel { }
|
||||
|
||||
@@ -25,7 +26,7 @@ export class PanelDescriptor extends CompositeDescriptor<Panel> {
|
||||
}
|
||||
|
||||
export class PanelRegistry extends CompositeRegistry<Panel> {
|
||||
private defaultPanelId!: string;
|
||||
private defaultPanelId: string | undefined;
|
||||
|
||||
/**
|
||||
* Registers a panel to the platform.
|
||||
@@ -44,7 +45,7 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
|
||||
/**
|
||||
* Returns a panel by id.
|
||||
*/
|
||||
getPanel(id: string): PanelDescriptor | null {
|
||||
getPanel(id: string): PanelDescriptor | undefined {
|
||||
return this.getComposite(id);
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
|
||||
* Gets the id of the panel that should open on startup by default.
|
||||
*/
|
||||
getDefaultPanelId(): string {
|
||||
return this.defaultPanelId;
|
||||
return assertIsDefined(this.defaultPanelId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,13 +107,14 @@ export abstract class TogglePanelAction extends Action {
|
||||
private isPanelActive(): boolean {
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
|
||||
return !!activePanel && activePanel.getId() === this.panelId;
|
||||
return activePanel?.getId() === this.panelId;
|
||||
}
|
||||
|
||||
private isPanelFocused(): boolean {
|
||||
const activeElement = document.activeElement;
|
||||
const panelPart = this.layoutService.getContainer(Parts.PANEL_PART);
|
||||
|
||||
return !!(this.isPanelActive() && activeElement && isAncestor(activeElement, this.layoutService.getContainer(Parts.PANEL_PART)));
|
||||
return !!(this.isPanelActive() && activeElement && panelPart && isAncestor(activeElement, panelPart));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { IDimension } from 'vs/platform/layout/browser/layoutService';
|
||||
import { ISerializableView, IViewSize } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export interface IPartOptions {
|
||||
hasTitle?: boolean;
|
||||
@@ -29,13 +30,16 @@ export interface ILayoutContentResult {
|
||||
*/
|
||||
export abstract class Part extends Component implements ISerializableView {
|
||||
|
||||
private _dimension: Dimension;
|
||||
get dimension(): Dimension { return this._dimension; }
|
||||
private _dimension: Dimension | undefined;
|
||||
get dimension(): Dimension | undefined { return this._dimension; }
|
||||
|
||||
private parent: HTMLElement;
|
||||
private titleArea: HTMLElement | null = null;
|
||||
private contentArea: HTMLElement | null = null;
|
||||
private partLayout: PartLayout;
|
||||
protected _onDidVisibilityChange = this._register(new Emitter<boolean>());
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
|
||||
private parent: HTMLElement | undefined;
|
||||
private titleArea: HTMLElement | undefined;
|
||||
private contentArea: HTMLElement | undefined;
|
||||
private partLayout: PartLayout | undefined;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -80,35 +84,35 @@ export abstract class Part extends Component implements ISerializableView {
|
||||
/**
|
||||
* Returns the overall part container.
|
||||
*/
|
||||
getContainer(): HTMLElement {
|
||||
getContainer(): HTMLElement | undefined {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses override to provide a title area implementation.
|
||||
*/
|
||||
protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | null {
|
||||
return null;
|
||||
protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title area container.
|
||||
*/
|
||||
protected getTitleArea(): HTMLElement | null {
|
||||
protected getTitleArea(): HTMLElement | undefined {
|
||||
return this.titleArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses override to provide a content area implementation.
|
||||
*/
|
||||
protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | null {
|
||||
return null;
|
||||
protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content area container.
|
||||
*/
|
||||
protected getContentArea(): HTMLElement | null {
|
||||
protected getContentArea(): HTMLElement | undefined {
|
||||
return this.contentArea;
|
||||
}
|
||||
|
||||
@@ -116,7 +120,9 @@ export abstract class Part extends Component implements ISerializableView {
|
||||
* Layout title and content area in the given dimension.
|
||||
*/
|
||||
protected layoutContents(width: number, height: number): ILayoutContentResult {
|
||||
return this.partLayout.layout(width, height);
|
||||
const partLayout = assertIsDefined(this.partLayout);
|
||||
|
||||
return partLayout.layout(width, height);
|
||||
}
|
||||
|
||||
//#region ISerializableView
|
||||
@@ -124,7 +130,7 @@ export abstract class Part extends Component implements ISerializableView {
|
||||
private _onDidChange = this._register(new Emitter<IViewSize | undefined>());
|
||||
get onDidChange(): Event<IViewSize | undefined> { return this._onDidChange.event; }
|
||||
|
||||
element: HTMLElement;
|
||||
element!: HTMLElement;
|
||||
|
||||
abstract minimumWidth: number;
|
||||
abstract maximumWidth: number;
|
||||
@@ -135,6 +141,10 @@ export abstract class Part extends Component implements ISerializableView {
|
||||
this._dimension = new Dimension(width, height);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean) {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
|
||||
abstract toJSON(): object;
|
||||
|
||||
//#endregion
|
||||
@@ -144,7 +154,7 @@ class PartLayout {
|
||||
|
||||
private static readonly TITLE_HEIGHT = 35;
|
||||
|
||||
constructor(private options: IPartOptions, private contentArea: HTMLElement | null) { }
|
||||
constructor(private options: IPartOptions, private contentArea: HTMLElement | undefined) { }
|
||||
|
||||
layout(width: number, height: number): ILayoutContentResult {
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarCol
|
||||
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
@@ -60,7 +60,7 @@ export class ViewletActivityAction extends ActivityAction {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
|
||||
// Hide sidebar if selected viewlet already visible
|
||||
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) {
|
||||
if (sideBarVisible && activeViewlet?.getId() === this.activity.id) {
|
||||
this.logAction('hide');
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
return true;
|
||||
@@ -95,7 +95,7 @@ export class ToggleViewletAction extends Action {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
|
||||
// Hide sidebar if selected viewlet already visible
|
||||
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) {
|
||||
if (sideBarVisible && activeViewlet?.getId() === this._viewlet.id) {
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -163,15 +163,19 @@ export class GlobalActivityActionViewItem extends ActivityActionViewItem {
|
||||
export class PlaceHolderViewletActivityAction extends ViewletActivityAction {
|
||||
|
||||
constructor(
|
||||
id: string, name: string, iconUrl: URI,
|
||||
id: string,
|
||||
name: string,
|
||||
iconUrl: URI | undefined,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService
|
||||
) {
|
||||
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService);
|
||||
|
||||
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar
|
||||
DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
if (iconUrl) {
|
||||
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar
|
||||
DOM.createCSSRule(iconClass, `-webkit-mask: ${DOM.asCSSUrl(iconUrl)} no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
}
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
@@ -222,7 +226,7 @@ class SwitchSideBarViewAction extends Action {
|
||||
export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.previousSideBarView';
|
||||
static LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View');
|
||||
static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -241,7 +245,7 @@ export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
|
||||
export class NextSideBarViewAction extends SwitchSideBarViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.nextSideBarView';
|
||||
static LABEL = nls.localize('nextSideBarView', 'Next Side Bar View');
|
||||
static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -274,6 +278,25 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
`);
|
||||
}
|
||||
|
||||
const activeBorderColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER);
|
||||
if (activeBorderColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before {
|
||||
border-left-color: ${activeBorderColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const activeBackgroundColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND);
|
||||
if (activeBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator {
|
||||
z-index: 0;
|
||||
background-color: ${activeBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Styling with Outline color (e.g. high contrast theme)
|
||||
const outline = theme.getColor(activeContrastBorder);
|
||||
if (outline) {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import 'vs/css!./media/activitybarpart';
|
||||
import * as nls from 'vs/nls';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -15,10 +14,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CompositeBar, ICompositeBarItem } from 'vs/workbench/browser/parts/compositeBar';
|
||||
import { Dimension, addClass, removeNode } from 'vs/base/browser/dom';
|
||||
@@ -30,14 +29,16 @@ import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views';
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { MenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
interface ICachedViewlet {
|
||||
id: string;
|
||||
@@ -65,17 +66,17 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
//#endregion
|
||||
|
||||
private globalActivityAction: ActivityAction;
|
||||
private globalActivityActionBar: ActionBar;
|
||||
private globalActivityAction: ActivityAction | undefined;
|
||||
private globalActivityActionBar: ActionBar | undefined;
|
||||
|
||||
private customMenubar: CustomMenubarControl | undefined;
|
||||
private menubar: HTMLElement | undefined;
|
||||
private content: HTMLElement;
|
||||
private content: HTMLElement | undefined;
|
||||
|
||||
private cachedViewlets: ICachedViewlet[] = [];
|
||||
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: Map<string, { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction }> = new Map();
|
||||
private readonly compositeActions: Map<string, { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction }> = new Map();
|
||||
|
||||
private readonly viewletDisposables: Map<string, IDisposable> = new Map<string, IDisposable>();
|
||||
|
||||
@@ -89,7 +90,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
@IViewsService private readonly viewsService: IViewsService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
) {
|
||||
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
@@ -110,8 +112,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
openComposite: (compositeId: string) => this.viewletService.openViewlet(compositeId, true),
|
||||
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)),
|
||||
getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))],
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, assertIsDefined(this.viewletService.getViewlet(compositeId))),
|
||||
getContextMenuActions: () => {
|
||||
const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
const actions = [];
|
||||
|
||||
if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) {
|
||||
actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu")));
|
||||
}
|
||||
|
||||
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
|
||||
return actions;
|
||||
},
|
||||
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
|
||||
hidePart: () => this.layoutService.setSideBarHidden(true),
|
||||
compositeSize: 50,
|
||||
@@ -147,7 +159,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
// Register for configuration changes
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('window.menuBarVisibility')) {
|
||||
if (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'compact') {
|
||||
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
|
||||
this.installMenubar();
|
||||
} else {
|
||||
this.uninstallMenubar();
|
||||
@@ -169,13 +181,15 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (foundViewlet) {
|
||||
this.compositeBar.addComposite(foundViewlet);
|
||||
}
|
||||
|
||||
this.compositeBar.activateComposite(viewlet.getId());
|
||||
|
||||
const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId());
|
||||
if (viewletDescriptor) {
|
||||
const viewContainer = this.getViewContainer(viewletDescriptor.id);
|
||||
if (viewContainer && viewContainer.hideIfEmpty) {
|
||||
if (viewContainer?.hideIfEmpty) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
|
||||
if (viewDescriptors?.activeViewDescriptors.length === 0) {
|
||||
this.hideComposite(viewletDescriptor.id); // Update the composite bar by hiding
|
||||
}
|
||||
}
|
||||
@@ -191,13 +205,15 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
return this.showGlobalActivity(badge, clazz);
|
||||
}
|
||||
|
||||
throw illegalArgument('globalActivityId');
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
private showGlobalActivity(badge: IBadge, clazz?: string): IDisposable {
|
||||
this.globalActivityAction.setBadge(badge, clazz);
|
||||
const globalActivityAction = assertIsDefined(this.globalActivityAction);
|
||||
|
||||
return toDisposable(() => this.globalActivityAction.setBadge(undefined));
|
||||
globalActivityAction.setBadge(badge, clazz);
|
||||
|
||||
return toDisposable(() => globalActivityAction.setBadge(undefined));
|
||||
}
|
||||
|
||||
private uninstallMenubar() {
|
||||
@@ -213,12 +229,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
private installMenubar() {
|
||||
this.menubar = document.createElement('div');
|
||||
addClass(this.menubar, 'menubar');
|
||||
this.content.prepend(this.menubar);
|
||||
|
||||
const content = assertIsDefined(this.content);
|
||||
content.prepend(this.menubar);
|
||||
|
||||
// Menubar: install a custom menu bar depending on configuration
|
||||
this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl));
|
||||
this.customMenubar.create(this.menubar);
|
||||
|
||||
}
|
||||
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
@@ -229,7 +246,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
parent.appendChild(this.content);
|
||||
|
||||
// Install menubar if compact
|
||||
if (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'compact') {
|
||||
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
|
||||
this.installMenubar();
|
||||
}
|
||||
|
||||
@@ -243,7 +260,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
this.createGlobalActivityActionBar(globalActivities);
|
||||
|
||||
this.element.style.display = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? null : 'none';
|
||||
this.element.style.display = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART) ? '' : 'none';
|
||||
|
||||
return this.content;
|
||||
}
|
||||
@@ -252,25 +269,27 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
super.updateStyles();
|
||||
|
||||
// Part container
|
||||
const container = this.getContainer();
|
||||
const background = this.getColor(ACTIVITY_BAR_BACKGROUND);
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || '';
|
||||
container.style.backgroundColor = background;
|
||||
|
||||
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder);
|
||||
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
|
||||
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
|
||||
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : '';
|
||||
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
|
||||
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
|
||||
container.style.borderRightColor = isPositionLeft ? borderColor : null;
|
||||
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null;
|
||||
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null;
|
||||
container.style.borderLeftColor = !isPositionLeft ? borderColor : null;
|
||||
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '';
|
||||
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : '';
|
||||
container.style.borderRightColor = isPositionLeft ? borderColor : '';
|
||||
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '';
|
||||
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : '';
|
||||
container.style.borderLeftColor = !isPositionLeft ? borderColor : '';
|
||||
}
|
||||
|
||||
private getActivitybarItemColors(theme: ITheme): ICompositeBarColors {
|
||||
return {
|
||||
activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND),
|
||||
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND),
|
||||
activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER),
|
||||
activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND),
|
||||
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
|
||||
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
|
||||
dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND),
|
||||
@@ -280,7 +299,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private createGlobalActivityActionBar(container: HTMLElement): void {
|
||||
this.globalActivityActionBar = this._register(new ActionBar(container, {
|
||||
actionViewItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionViewItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)),
|
||||
actionViewItemProvider: action => this.instantiationService.createInstance(GlobalActivityActionViewItem, action as ActivityAction, (theme: ITheme) => this.getActivitybarItemColors(theme)),
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
ariaLabel: nls.localize('manage', "Manage"),
|
||||
animated: false
|
||||
@@ -291,6 +310,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
name: nls.localize('manage', "Manage"),
|
||||
cssClass: 'update-activity'
|
||||
});
|
||||
|
||||
this.globalActivityActionBar.push(this.globalActivityAction);
|
||||
}
|
||||
|
||||
@@ -306,7 +326,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
} else {
|
||||
const cachedComposite = this.cachedViewlets.filter(c => c.id === compositeId)[0];
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite && cachedComposite.name ? cachedComposite.name : compositeId, cachedComposite && cachedComposite.iconUrl ? URI.revive(cachedComposite.iconUrl) : undefined),
|
||||
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite?.name || compositeId, cachedComposite?.iconUrl ? URI.revive(cachedComposite.iconUrl) : undefined),
|
||||
pinnedAction: new PlaceHolderToggleCompositePinnedAction(compositeId, this.compositeBar)
|
||||
};
|
||||
}
|
||||
@@ -321,7 +341,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
for (const viewlet of viewlets) {
|
||||
const cachedViewlet = this.cachedViewlets.filter(({ id }) => id === viewlet.id)[0];
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
const isActive = activeViewlet && activeViewlet.getId() === viewlet.id;
|
||||
const isActive = activeViewlet?.getId() === viewlet.id;
|
||||
|
||||
if (isActive || !this.shouldBeHidden(viewlet.id, cachedViewlet)) {
|
||||
this.compositeBar.addComposite(viewlet);
|
||||
@@ -336,10 +356,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const viewlet of viewlets) {
|
||||
this.enableCompositeActions(viewlet);
|
||||
const viewContainer = this.getViewContainer(viewlet.id);
|
||||
if (viewContainer && viewContainer.hideIfEmpty) {
|
||||
if (viewContainer?.hideIfEmpty) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors) {
|
||||
this.onDidChangeActiveViews(viewlet, viewDescriptors);
|
||||
@@ -354,6 +375,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
}
|
||||
|
||||
this.viewletDisposables.delete(viewletId);
|
||||
this.hideComposite(viewletId);
|
||||
}
|
||||
@@ -371,7 +393,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
if (!viewContainer || !viewContainer.hideIfEmpty) {
|
||||
return false;
|
||||
}
|
||||
return cachedViewlet && cachedViewlet.views && cachedViewlet.views.length
|
||||
|
||||
return cachedViewlet?.views && cachedViewlet.views.length
|
||||
? cachedViewlet.views.every(({ when }) => !!when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when)))
|
||||
: viewletId === TEST_VIEW_CONTAINER_ID /* Hide Test viewlet for the first time or it had no views registered before */;
|
||||
}
|
||||
@@ -387,6 +410,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
private hideComposite(compositeId: string): void {
|
||||
this.compositeBar.hideComposite(compositeId);
|
||||
|
||||
const compositeActions = this.compositeActions.get(compositeId);
|
||||
if (compositeActions) {
|
||||
compositeActions.activityAction.dispose();
|
||||
@@ -415,12 +439,6 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
.map(v => v.id);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
if (this.element) {
|
||||
this.element.style.display = visible ? null : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
return;
|
||||
@@ -440,7 +458,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
|
||||
if (e.key === ActivitybarPart.PINNED_VIEWLETS && e.scope === StorageScope.GLOBAL
|
||||
&& this.cachedViewletsValue !== this.getStoredCachedViewletsValue() /* This checks if current window changed the value or not */) {
|
||||
this._cachedViewletsValue = null;
|
||||
this._cachedViewletsValue = undefined;
|
||||
const newCompositeItems: ICompositeBarItem[] = [];
|
||||
const compositeItems = this.compositeBar.getCompositeBarItems();
|
||||
const cachedViewlets = this.getCachedViewlets();
|
||||
@@ -525,7 +543,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private _cachedViewletsValue: string | null;
|
||||
private _cachedViewletsValue: string | undefined;
|
||||
private get cachedViewletsValue(): string {
|
||||
if (!this._cachedViewletsValue) {
|
||||
this._cachedViewletsValue = this.getStoredCachedViewletsValue();
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 40px;
|
||||
@@ -20,19 +22,41 @@
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before,
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
height: 32px;
|
||||
z-index: 1;
|
||||
top: 5px;
|
||||
height: 40px;
|
||||
width: 0;
|
||||
border-left: 2px solid;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before {
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:focus .active-item-indicator:before {
|
||||
border-left: none; /* don't show active border + focus at the same time, focus takes priority */
|
||||
}
|
||||
|
||||
/* Hides active elements in high contrast mode */
|
||||
.hc-black .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.clicked:focus:before {
|
||||
border-left: none !important; /* no focus feedback when using mouse */
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before{
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
left: 1px;
|
||||
}
|
||||
@@ -41,8 +65,20 @@
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before {
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
/* Hides outline on HC as focus is handled by border */
|
||||
.hc-black .monaco-workbench .activitybar.left > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before,
|
||||
.hc-black .monaco-workbench .activitybar.right > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus:before {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .active-item-indicator,
|
||||
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .badge {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 5px;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -30,11 +30,12 @@ export interface ICompositeBarItem {
|
||||
}
|
||||
|
||||
export interface ICompositeBarOptions {
|
||||
icon: boolean;
|
||||
orientation: ActionsOrientation;
|
||||
colors: (theme: ITheme) => ICompositeBarColors;
|
||||
compositeSize: number;
|
||||
overflowActionSize: number;
|
||||
readonly icon: boolean;
|
||||
readonly orientation: ActionsOrientation;
|
||||
readonly colors: (theme: ITheme) => ICompositeBarColors;
|
||||
readonly compositeSize: number;
|
||||
readonly overflowActionSize: number;
|
||||
|
||||
getActivityAction: (compositeId: string) => ActivityAction;
|
||||
getCompositePinnedAction: (compositeId: string) => Action;
|
||||
getOnCompositeClickAction: (compositeId: string) => Action;
|
||||
@@ -48,7 +49,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
private dimension: Dimension | undefined;
|
||||
|
||||
private compositeSwitcherBar!: ActionBar;
|
||||
private compositeSwitcherBar: ActionBar | undefined;
|
||||
private compositeOverflowAction: CompositeOverflowActivityAction | undefined;
|
||||
private compositeOverflowActionViewItem: CompositeOverflowActivityActionViewItem | undefined;
|
||||
|
||||
@@ -92,13 +93,14 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
create(parent: HTMLElement): HTMLElement {
|
||||
const actionBarDiv = parent.appendChild($('.composite-bar'));
|
||||
|
||||
this.compositeSwitcherBar = this._register(new ActionBar(actionBarDiv, {
|
||||
actionViewItemProvider: (action: Action) => {
|
||||
if (action instanceof CompositeOverflowActivityAction) {
|
||||
return this.compositeOverflowActionViewItem;
|
||||
}
|
||||
const item = this.model.findItem(action.id);
|
||||
return item && this.instantiationService.createInstance(CompositeActionViewItem, action, item.pinnedAction, () => this.getContextMenuActions(), this.options.colors, this.options.icon, this);
|
||||
return item && this.instantiationService.createInstance(CompositeActionViewItem, action as ActivityAction, item.pinnedAction, () => this.getContextMenuActions() as Action[], this.options.colors, this.options.icon, this);
|
||||
},
|
||||
orientation: this.options.orientation,
|
||||
ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"),
|
||||
@@ -113,12 +115,15 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
|
||||
const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1];
|
||||
if (targetItem && targetItem.id !== draggedCompositeId) {
|
||||
this.move(draggedCompositeId, targetItem.id);
|
||||
const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1];
|
||||
if (targetItem && targetItem.id !== draggedCompositeId) {
|
||||
this.move(draggedCompositeId, targetItem.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -258,7 +263,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
isPinned(compositeId: string): boolean {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.pinned;
|
||||
return item?.pinned;
|
||||
}
|
||||
|
||||
move(compositeId: string, toCompositeId: string): void {
|
||||
@@ -270,7 +275,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
getAction(compositeId: string): ActivityAction {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.activityAction;
|
||||
return item?.activityAction;
|
||||
}
|
||||
|
||||
private computeSizes(items: ICompositeBarModelItem[]): void {
|
||||
@@ -278,21 +283,23 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
if (size) {
|
||||
items.forEach(composite => this.compositeSizeInBar.set(composite.id, size));
|
||||
} else {
|
||||
if (this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) {
|
||||
const compositeSwitcherBar = this.compositeSwitcherBar;
|
||||
if (compositeSwitcherBar && this.dimension && this.dimension.height !== 0 && this.dimension.width !== 0) {
|
||||
// Compute sizes only if visible. Otherwise the size measurment would be computed wrongly.
|
||||
const currentItemsLength = this.compositeSwitcherBar.viewItems.length;
|
||||
this.compositeSwitcherBar.push(items.map(composite => composite.activityAction));
|
||||
const currentItemsLength = compositeSwitcherBar.viewItems.length;
|
||||
compositeSwitcherBar.push(items.map(composite => composite.activityAction));
|
||||
items.map((composite, index) => this.compositeSizeInBar.set(composite.id, this.options.orientation === ActionsOrientation.VERTICAL
|
||||
? this.compositeSwitcherBar.getHeight(currentItemsLength + index)
|
||||
: this.compositeSwitcherBar.getWidth(currentItemsLength + index)
|
||||
? compositeSwitcherBar.getHeight(currentItemsLength + index)
|
||||
: compositeSwitcherBar.getWidth(currentItemsLength + index)
|
||||
));
|
||||
items.forEach(() => this.compositeSwitcherBar.pull(this.compositeSwitcherBar.viewItems.length - 1));
|
||||
items.forEach(() => compositeSwitcherBar.pull(compositeSwitcherBar.viewItems.length - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateCompositeSwitcher(): void {
|
||||
if (!this.compositeSwitcherBar || !this.dimension) {
|
||||
const compositeSwitcherBar = this.compositeSwitcherBar;
|
||||
if (!compositeSwitcherBar || !this.dimension) {
|
||||
return; // We have not been rendered yet so there is nothing to update.
|
||||
}
|
||||
|
||||
@@ -340,7 +347,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
// Pull out overflow action if there is a composite change so that we can add it to the end later
|
||||
if (this.compositeOverflowAction && visibleCompositesChange) {
|
||||
this.compositeSwitcherBar.pull(this.compositeSwitcherBar.length() - 1);
|
||||
compositeSwitcherBar.pull(compositeSwitcherBar.length() - 1);
|
||||
|
||||
this.compositeOverflowAction.dispose();
|
||||
this.compositeOverflowAction = undefined;
|
||||
@@ -359,8 +366,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
}
|
||||
});
|
||||
compositesToRemove.reverse().forEach(index => {
|
||||
const actionViewItem = this.compositeSwitcherBar.viewItems[index];
|
||||
this.compositeSwitcherBar.pull(index);
|
||||
const actionViewItem = compositeSwitcherBar.viewItems[index];
|
||||
compositeSwitcherBar.pull(index);
|
||||
actionViewItem.dispose();
|
||||
this.visibleComposites.splice(index, 1);
|
||||
});
|
||||
@@ -370,19 +377,19 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
const currentIndex = this.visibleComposites.indexOf(compositeId);
|
||||
if (newIndex !== currentIndex) {
|
||||
if (currentIndex !== -1) {
|
||||
const actionViewItem = this.compositeSwitcherBar.viewItems[currentIndex];
|
||||
this.compositeSwitcherBar.pull(currentIndex);
|
||||
const actionViewItem = compositeSwitcherBar.viewItems[currentIndex];
|
||||
compositeSwitcherBar.pull(currentIndex);
|
||||
actionViewItem.dispose();
|
||||
this.visibleComposites.splice(currentIndex, 1);
|
||||
}
|
||||
|
||||
this.compositeSwitcherBar.push(this.model.findItem(compositeId).activityAction, { label: true, icon: this.options.icon, index: newIndex });
|
||||
compositeSwitcherBar.push(this.model.findItem(compositeId).activityAction, { label: true, icon: this.options.icon, index: newIndex });
|
||||
this.visibleComposites.splice(newIndex, 0, compositeId);
|
||||
}
|
||||
});
|
||||
|
||||
// Add overflow action as needed
|
||||
if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) {
|
||||
if ((visibleCompositesChange && overflows) || compositeSwitcherBar.length() === 0) {
|
||||
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => {
|
||||
if (this.compositeOverflowActionViewItem) {
|
||||
this.compositeOverflowActionViewItem.showMenu();
|
||||
@@ -395,13 +402,13 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
() => this.model.activeItem ? this.model.activeItem.id : undefined,
|
||||
(compositeId: string) => {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.activity[0] && item.activity[0].badge;
|
||||
return item?.activity?.[0].badge;
|
||||
},
|
||||
this.options.getOnCompositeClickAction,
|
||||
this.options.colors
|
||||
);
|
||||
|
||||
this.compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true });
|
||||
compositeSwitcherBar.push(this.compositeOverflowAction, { label: false, icon: true });
|
||||
}
|
||||
|
||||
this._onDidChange.fire();
|
||||
|
||||
@@ -57,7 +57,7 @@ export class ActivityAction extends Action {
|
||||
private readonly _onDidChangeBadge = new Emitter<this>();
|
||||
readonly onDidChangeBadge: Event<this> = this._onDidChangeBadge.event;
|
||||
|
||||
private badge?: IBadge;
|
||||
private badge: IBadge | undefined;
|
||||
private clazz: string | undefined;
|
||||
|
||||
constructor(private _activity: IActivity) {
|
||||
@@ -110,6 +110,8 @@ export class ActivityAction extends Action {
|
||||
export interface ICompositeBarColors {
|
||||
activeBackgroundColor?: Color;
|
||||
inactiveBackgroundColor?: Color;
|
||||
activeBorderColor?: Color;
|
||||
activeBackground?: Color;
|
||||
activeBorderBottomColor?: Color;
|
||||
activeForegroundColor?: Color;
|
||||
inactiveForegroundColor?: Color;
|
||||
@@ -156,12 +158,12 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
||||
if (this.label) {
|
||||
if (this.options.icon) {
|
||||
const foreground = this._action.checked ? colors.activeBackgroundColor || colors.activeForegroundColor : colors.inactiveBackgroundColor || colors.inactiveForegroundColor;
|
||||
this.label.style.backgroundColor = foreground ? foreground.toString() : null;
|
||||
this.label.style.backgroundColor = foreground ? foreground.toString() : '';
|
||||
} else {
|
||||
const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor;
|
||||
const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null;
|
||||
this.label.style.color = foreground ? foreground.toString() : null;
|
||||
this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : null;
|
||||
this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,11 +174,11 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
||||
const contrastBorderColor = theme.getColor(contrastBorder);
|
||||
|
||||
this.badgeContent.style.color = badgeForeground ? badgeForeground.toString() : null;
|
||||
this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : null;
|
||||
this.badgeContent.style.backgroundColor = badgeBackground ? badgeBackground.toString() : '';
|
||||
|
||||
this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : null;
|
||||
this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : null;
|
||||
this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : null;
|
||||
this.badgeContent.style.borderStyle = contrastBorderColor ? 'solid' : '';
|
||||
this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : '';
|
||||
this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,12 +207,18 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
||||
}));
|
||||
|
||||
// Label
|
||||
this.label = dom.append(this.element!, dom.$('a'));
|
||||
this.label = dom.append(container, dom.$('a'));
|
||||
|
||||
// Badge
|
||||
this.badge = dom.append(this.element!, dom.$('.badge'));
|
||||
this.badge = dom.append(container, dom.$('.badge'));
|
||||
this.badgeContent = dom.append(this.badge, dom.$('.badge-content'));
|
||||
|
||||
// Activity bar active border + background
|
||||
const isActivityBarItem = this.options.icon;
|
||||
if (isActivityBarItem) {
|
||||
dom.append(container, dom.$('.active-item-indicator'));
|
||||
}
|
||||
|
||||
dom.hide(this.badge);
|
||||
|
||||
this.updateActivity();
|
||||
@@ -285,7 +293,7 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
||||
|
||||
// Title
|
||||
let title: string;
|
||||
if (badge && badge.getDescription()) {
|
||||
if (badge?.getDescription()) {
|
||||
if (this.activity.name) {
|
||||
title = nls.localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription());
|
||||
} else {
|
||||
@@ -294,14 +302,17 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
||||
} else {
|
||||
title = this.activity.name;
|
||||
}
|
||||
|
||||
this.updateTitle(title);
|
||||
}
|
||||
|
||||
protected updateLabel(): void {
|
||||
this.label.className = 'action-label';
|
||||
|
||||
if (this.activity.cssClass) {
|
||||
dom.addClass(this.label, this.activity.cssClass);
|
||||
}
|
||||
|
||||
if (!this.options.icon) {
|
||||
this.label.textContent = this.getAction().label;
|
||||
}
|
||||
@@ -347,12 +358,12 @@ export class CompositeOverflowActivityAction extends ActivityAction {
|
||||
}
|
||||
|
||||
export class CompositeOverflowActivityActionViewItem extends ActivityActionViewItem {
|
||||
private actions: Action[] | undefined;
|
||||
private actions: Action[] = [];
|
||||
|
||||
constructor(
|
||||
action: ActivityAction,
|
||||
private getOverflowingComposites: () => { id: string, name: string }[],
|
||||
private getActiveCompositeId: () => string,
|
||||
private getOverflowingComposites: () => { id: string, name?: string }[],
|
||||
private getActiveCompositeId: () => string | undefined,
|
||||
private getBadge: (compositeId: string) => IBadge,
|
||||
private getCompositeOpenAction: (compositeId: string) => Action,
|
||||
colors: (theme: ITheme) => ICompositeBarColors,
|
||||
@@ -370,16 +381,17 @@ export class CompositeOverflowActivityActionViewItem extends ActivityActionViewI
|
||||
this.actions = this.getActions();
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => this.element!,
|
||||
getActions: () => this.actions!,
|
||||
onHide: () => dispose(this.actions!)
|
||||
getAnchor: () => this.container,
|
||||
getActions: () => this.actions,
|
||||
getCheckedActionsRepresentation: () => 'radio',
|
||||
onHide: () => dispose(this.actions)
|
||||
});
|
||||
}
|
||||
|
||||
private getActions(): Action[] {
|
||||
return this.getOverflowingComposites().map(composite => {
|
||||
const action = this.getCompositeOpenAction(composite.id);
|
||||
action.radio = this.getActiveCompositeId() === action.id;
|
||||
action.checked = this.getActiveCompositeId() === action.id;
|
||||
|
||||
const badge = this.getBadge(composite.id);
|
||||
let suffix: string | number | undefined;
|
||||
@@ -392,7 +404,7 @@ export class CompositeOverflowActivityActionViewItem extends ActivityActionViewI
|
||||
if (suffix) {
|
||||
action.label = nls.localize('numberBadge', "{0} ({1})", composite.name, suffix);
|
||||
} else {
|
||||
action.label = composite.name;
|
||||
action.label = composite.name || '';
|
||||
}
|
||||
|
||||
return action;
|
||||
@@ -439,7 +451,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
constructor(
|
||||
private compositeActivityAction: ActivityAction,
|
||||
private toggleCompositePinnedAction: Action,
|
||||
private contextMenuActionsProvider: () => Action[],
|
||||
private contextMenuActionsProvider: () => ReadonlyArray<Action>,
|
||||
colors: (theme: ITheme) => ICompositeBarColors,
|
||||
icon: boolean,
|
||||
private compositeBar: ICompositeBar,
|
||||
@@ -502,7 +514,9 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
|
||||
// Allow to drag
|
||||
this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => {
|
||||
e.dataTransfer!.effectAllowed = 'move';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
|
||||
// Registe as dragged to local transfer
|
||||
this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype);
|
||||
@@ -515,8 +529,11 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
|
||||
this._register(new DragAndDropObserver(this.container, {
|
||||
onDragEnter: e => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id !== this.activity.id) {
|
||||
this.updateFromDragging(container, true);
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data) && data[0].id !== this.activity.id) {
|
||||
this.updateFromDragging(container, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -538,12 +555,15 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
dom.EventHelper.stop(e, true);
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
|
||||
if (draggedCompositeId !== this.activity.id) {
|
||||
this.updateFromDragging(container, false);
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
const data = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedCompositeId = data[0].id;
|
||||
if (draggedCompositeId !== this.activity.id) {
|
||||
this.updateFromDragging(container, false);
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
|
||||
this.compositeBar.move(draggedCompositeId, this.activity.id);
|
||||
this.compositeBar.move(draggedCompositeId, this.activity.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -563,7 +583,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
const theme = this.themeService.getTheme();
|
||||
const dragBackground = this.options.colors(theme).dragAndDropBackground;
|
||||
|
||||
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null;
|
||||
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : '';
|
||||
}
|
||||
|
||||
private showContextMenu(container: HTMLElement): void {
|
||||
@@ -595,8 +615,8 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActionsContext: () => this.activity.id,
|
||||
getActions: () => actions
|
||||
getActions: () => actions,
|
||||
getActionsContext: () => this.activity.id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Dimension, append, $, addClass, hide, show, addClasses } from 'vs/base/browser/dom';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export interface ICompositeTitleLabel {
|
||||
|
||||
@@ -57,15 +58,15 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
protected readonly onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>());
|
||||
protected readonly onDidCompositeClose = this._register(new Emitter<IComposite>());
|
||||
|
||||
protected toolBar!: ToolBar;
|
||||
protected toolBar: ToolBar | undefined;
|
||||
|
||||
private mapCompositeToCompositeContainer = new Map<string, HTMLElement>();
|
||||
private mapActionsBindingToComposite = new Map<string, () => void>();
|
||||
private activeComposite: Composite | null;
|
||||
private activeComposite: Composite | undefined;
|
||||
private lastActiveCompositeId: string;
|
||||
private instantiatedCompositeItems: Map<string, CompositeItem>;
|
||||
private titleLabel!: ICompositeTitleLabel;
|
||||
private progressBar!: ProgressBar;
|
||||
private titleLabel: ICompositeTitleLabel | undefined;
|
||||
private progressBar: ProgressBar | undefined;
|
||||
private contentAreaSize: Dimension | undefined;
|
||||
private readonly telemetryActionsListener = this._register(new MutableDisposable());
|
||||
private currentCompositeOpenToken: string | undefined;
|
||||
@@ -90,7 +91,6 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
) {
|
||||
super(id, options, themeService, storageService, layoutService);
|
||||
|
||||
this.activeComposite = null;
|
||||
this.instantiatedCompositeItems = new Map<string, CompositeItem>();
|
||||
this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId);
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
// Instantiate composite from registry otherwise
|
||||
const compositeDescriptor = this.registry.getComposite(id);
|
||||
if (compositeDescriptor) {
|
||||
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, this.progressBar, compositeDescriptor.id, isActive);
|
||||
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, this.progressBar, compositeDescriptor.id, !!isActive);
|
||||
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IEditorProgressService, compositeProgressIndicator] // provide the editor progress service for any editors instantiated within the composite
|
||||
));
|
||||
@@ -234,7 +234,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
show(compositeContainer);
|
||||
|
||||
// Setup action runner
|
||||
this.toolBar.actionRunner = composite.getActionRunner();
|
||||
const toolBar = assertIsDefined(this.toolBar);
|
||||
toolBar.actionRunner = composite.getActionRunner();
|
||||
|
||||
// Update title with composite title if it differs from descriptor
|
||||
const descriptor = this.registry.getComposite(composite.getId());
|
||||
@@ -251,7 +252,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
actionsBinding();
|
||||
|
||||
// Action Run Handling
|
||||
this.telemetryActionsListener.value = this.toolBar.actionRunner.onDidRun(e => {
|
||||
this.telemetryActionsListener.value = toolBar.actionRunner.onDidRun(e => {
|
||||
|
||||
// Check for Error
|
||||
if (e.error && !errors.isPromiseCanceledError(e.error)) {
|
||||
@@ -310,9 +311,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
const keybinding = this.keybindingService.lookupKeybinding(compositeId);
|
||||
|
||||
this.titleLabel.updateTitle(compositeId, compositeTitle, (keybinding && keybinding.getLabel()) || undefined);
|
||||
this.titleLabel.updateTitle(compositeId, compositeTitle, withNullAsUndefined(keybinding?.getLabel()));
|
||||
|
||||
this.toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle));
|
||||
const toolBar = assertIsDefined(this.toolBar);
|
||||
toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle));
|
||||
}
|
||||
|
||||
private collectCompositeActions(composite: Composite): () => void {
|
||||
@@ -326,13 +328,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
secondaryActions.push(...this.getSecondaryActions());
|
||||
|
||||
// Update context
|
||||
this.toolBar.context = this.actionsContextProvider();
|
||||
const toolBar = assertIsDefined(this.toolBar);
|
||||
toolBar.context = this.actionsContextProvider();
|
||||
|
||||
// Return fn to set into toolbar
|
||||
return this.toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
|
||||
return toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
|
||||
}
|
||||
|
||||
protected getActiveComposite(): IComposite | null {
|
||||
protected getActiveComposite(): IComposite | undefined {
|
||||
return this.activeComposite;
|
||||
}
|
||||
|
||||
@@ -346,7 +349,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
}
|
||||
|
||||
const composite = this.activeComposite;
|
||||
this.activeComposite = null;
|
||||
this.activeComposite = undefined;
|
||||
|
||||
const compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId());
|
||||
|
||||
@@ -360,10 +363,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
}
|
||||
|
||||
// Clear any running Progress
|
||||
this.progressBar.stop().hide();
|
||||
if (this.progressBar) {
|
||||
this.progressBar.stop().hide();
|
||||
}
|
||||
|
||||
// Empty Actions
|
||||
this.toolBar.setActions([])();
|
||||
if (this.toolBar) {
|
||||
this.toolBar.setActions([])();
|
||||
}
|
||||
this.onDidCompositeClose.fire(composite);
|
||||
|
||||
return composite;
|
||||
@@ -413,7 +420,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
super.updateStyles();
|
||||
|
||||
// Forward to title label
|
||||
this.titleLabel.updateStyles();
|
||||
const titleLabel = assertIsDefined(this.titleLabel);
|
||||
titleLabel.updateStyles();
|
||||
}
|
||||
|
||||
protected actionViewItemProvider(action: IAction): IActionViewItem | undefined {
|
||||
@@ -446,10 +454,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return contentContainer;
|
||||
}
|
||||
|
||||
getProgressIndicator(id: string): IProgressIndicator | null {
|
||||
getProgressIndicator(id: string): IProgressIndicator | undefined {
|
||||
const compositeItem = this.instantiatedCompositeItems.get(id);
|
||||
|
||||
return compositeItem ? compositeItem.progress : null;
|
||||
return compositeItem ? compositeItem.progress : undefined;
|
||||
}
|
||||
|
||||
protected getActions(): ReadonlyArray<IAction> {
|
||||
|
||||
@@ -19,6 +19,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { dispose } 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';
|
||||
|
||||
export interface IOpenCallbacks {
|
||||
openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise<void>;
|
||||
@@ -38,8 +39,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
|
||||
private callbacks: IOpenCallbacks;
|
||||
private metadata: string | undefined;
|
||||
private binaryContainer: HTMLElement;
|
||||
private scrollbar: DomScrollableElement;
|
||||
private binaryContainer: HTMLElement | undefined;
|
||||
private scrollbar: DomScrollableElement | undefined;
|
||||
private resourceViewerContext: ResourceViewerContext | undefined;
|
||||
|
||||
constructor(
|
||||
@@ -91,7 +92,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
this.resourceViewerContext.dispose();
|
||||
}
|
||||
|
||||
this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, this.binaryContainer, this.scrollbar, {
|
||||
const [binaryContainer, scrollbar] = assertAllDefined(this.binaryContainer, this.scrollbar);
|
||||
this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, binaryContainer, scrollbar, {
|
||||
openInternalClb: () => this.handleOpenInternalCallback(input, options),
|
||||
openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource),
|
||||
metadataClb: meta => this.handleMetadataChanged(meta)
|
||||
@@ -120,8 +122,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
// Clear Meta
|
||||
this.handleMetadataChanged(undefined);
|
||||
|
||||
// Clear Resource Viewer
|
||||
clearNode(this.binaryContainer);
|
||||
// Clear the rest
|
||||
if (this.binaryContainer) {
|
||||
clearNode(this.binaryContainer);
|
||||
}
|
||||
dispose(this.resourceViewerContext);
|
||||
this.resourceViewerContext = undefined;
|
||||
|
||||
@@ -131,19 +135,24 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
||||
layout(dimension: Dimension): void {
|
||||
|
||||
// Pass on to Binary Container
|
||||
size(this.binaryContainer, dimension.width, dimension.height);
|
||||
this.scrollbar.scanDomNode();
|
||||
const [binaryContainer, scrollbar] = assertAllDefined(this.binaryContainer, this.scrollbar);
|
||||
size(binaryContainer, dimension.width, dimension.height);
|
||||
scrollbar.scanDomNode();
|
||||
if (this.resourceViewerContext && this.resourceViewerContext.layout) {
|
||||
this.resourceViewerContext.layout(dimension);
|
||||
}
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.binaryContainer.focus();
|
||||
const binaryContainer = assertIsDefined(this.binaryContainer);
|
||||
|
||||
binaryContainer.focus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.binaryContainer.remove();
|
||||
if (this.binaryContainer) {
|
||||
this.binaryContainer.remove();
|
||||
}
|
||||
|
||||
dispose(this.resourceViewerContext);
|
||||
this.resourceViewerContext = undefined;
|
||||
|
||||
@@ -66,14 +66,14 @@ export abstract class BreadcrumbsConfig<T> {
|
||||
// internal
|
||||
}
|
||||
|
||||
static IsEnabled = BreadcrumbsConfig._stub<boolean>('breadcrumbs.enabled');
|
||||
static UseQuickPick = BreadcrumbsConfig._stub<boolean>('breadcrumbs.useQuickPick');
|
||||
static FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath');
|
||||
static SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath');
|
||||
static SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder');
|
||||
static Icons = BreadcrumbsConfig._stub<boolean>('breadcrumbs.icons');
|
||||
static readonly IsEnabled = BreadcrumbsConfig._stub<boolean>('breadcrumbs.enabled');
|
||||
static readonly UseQuickPick = BreadcrumbsConfig._stub<boolean>('breadcrumbs.useQuickPick');
|
||||
static readonly FilePath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.filePath');
|
||||
static readonly SymbolPath = BreadcrumbsConfig._stub<'on' | 'off' | 'last'>('breadcrumbs.symbolPath');
|
||||
static readonly SymbolSortOrder = BreadcrumbsConfig._stub<'position' | 'name' | 'type'>('breadcrumbs.symbolSortOrder');
|
||||
static readonly Icons = BreadcrumbsConfig._stub<boolean>('breadcrumbs.icons');
|
||||
|
||||
static FileExcludes = BreadcrumbsConfig._stub<glob.IExpression>('files.exclude');
|
||||
static readonly FileExcludes = BreadcrumbsConfig._stub<glob.IExpression>('files.exclude');
|
||||
|
||||
private static _stub<T>(name: string): { bindTo(service: IConfigurationService): BreadcrumbsConfig<T> } {
|
||||
return {
|
||||
@@ -166,6 +166,136 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
|
||||
description: localize('icons', "Render breadcrumb items with icons."),
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
'breadcrumbs.filteredTypes.file': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.file', "When set to `false` breadcrumbs never show `file`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.module': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.module', "When set to `false` breadcrumbs never show `module`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.namespace': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.namespace', "When set to `false` breadcrumbs never show `namespace`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.package': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.package', "When set to `false` breadcrumbs never show `package`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.class': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.class', "When set to `false` breadcrumbs never show `class`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.method': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.method', "When set to `false` breadcrumbs never show `method`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.property': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.property', "When set to `false` breadcrumbs never show `property`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.field': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.field', "When set to `false` breadcrumbs never show `field`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.constructor': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.constructor', "When set to `false` breadcrumbs never show `constructor`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.enum': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.enum', "When set to `false` breadcrumbs never show `enum`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.interface': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.interface', "When set to `false` breadcrumbs never show `interface`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.function': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.function', "When set to `false` breadcrumbs never show `function`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.variable': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.variable', "When set to `false` breadcrumbs never show `variable`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.constant': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.constant', "When set to `false` breadcrumbs never show `constant`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.string': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.string', "When set to `false` breadcrumbs never show `string`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.number': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.number', "When set to `false` breadcrumbs never show `number`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.boolean': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.boolean', "When set to `false` breadcrumbs never show `boolean`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.array': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.array', "When set to `false` breadcrumbs never show `array`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.object': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.object', "When set to `false` breadcrumbs never show `object`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.key': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.key', "When set to `false` breadcrumbs never show `key`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.null': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.null', "When set to `false` breadcrumbs never show `null`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.enumMember': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.enumMember', "When set to `false` breadcrumbs never show `enumMember`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.struct': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.struct', "When set to `false` breadcrumbs never show `struct`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.event': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.event', "When set to `false` breadcrumbs never show `event`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.operator': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.operator', "When set to `false` breadcrumbs never show `operator`-symbols.")
|
||||
},
|
||||
'breadcrumbs.filteredTypes.typeParameter': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: localize('filteredTypes.typeParameter', "When set to `false` breadcrumbs never show `typeParameter`-symbols.")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { symbolKindToCssClass } from 'vs/editor/common/modes';
|
||||
import { SymbolKinds } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
@@ -45,7 +45,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
@@ -109,7 +109,7 @@ class Item extends BreadcrumbsItem {
|
||||
// symbol
|
||||
if (this.options.showSymbolIcons) {
|
||||
let icon = document.createElement('div');
|
||||
icon.className = symbolKindToCssClass(this.element.symbol.kind);
|
||||
icon.className = SymbolKinds.toCssClassName(this.element.symbol.kind);
|
||||
container.appendChild(icon);
|
||||
dom.addClass(container, 'shows-symbol-icon');
|
||||
}
|
||||
@@ -130,15 +130,15 @@ export interface IBreadcrumbsControlOptions {
|
||||
|
||||
export class BreadcrumbsControl {
|
||||
|
||||
static HEIGHT = 22;
|
||||
static readonly HEIGHT = 22;
|
||||
|
||||
static readonly Payload_Reveal = {};
|
||||
static readonly Payload_RevealAside = {};
|
||||
static readonly Payload_Pick = {};
|
||||
|
||||
static CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false);
|
||||
static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
|
||||
static CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
|
||||
static readonly CK_BreadcrumbsPossible = new RawContextKey('breadcrumbsPossible', false);
|
||||
static readonly CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false);
|
||||
static readonly CK_BreadcrumbsActive = new RawContextKey('breadcrumbsActive', false);
|
||||
|
||||
private readonly _ckBreadcrumbsPossible: IContextKey<boolean>;
|
||||
private readonly _ckBreadcrumbsVisible: IContextKey<boolean>;
|
||||
@@ -246,7 +246,7 @@ export class BreadcrumbsControl {
|
||||
|
||||
const uri = input.getResource()!;
|
||||
const editor = this._getActiveCodeEditor();
|
||||
const model = new EditorBreadcrumbsModel(uri, editor, this._workspaceService, this._configurationService);
|
||||
const model = new EditorBreadcrumbsModel(uri, editor, this._configurationService, this._workspaceService);
|
||||
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
|
||||
dom.toggleClass(this.domNode, 'backslash-path', this._labelService.getSeparator(uri.scheme, uri.authority) === '\\');
|
||||
|
||||
@@ -485,7 +485,7 @@ export class BreadcrumbsControl {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
}
|
||||
}, this._getActiveCodeEditor(), group === SIDE_GROUP);
|
||||
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { isEqual, dirname } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { DocumentSymbolProviderRegistry, SymbolKinds } from 'vs/editor/common/modes';
|
||||
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
@@ -51,16 +51,15 @@ export class EditorBreadcrumbsModel {
|
||||
constructor(
|
||||
private readonly _uri: URI,
|
||||
private readonly _editor: ICodeEditor | undefined,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IWorkspaceContextService workspaceService: IWorkspaceContextService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
|
||||
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(configurationService);
|
||||
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(configurationService);
|
||||
this._cfgFilePath = BreadcrumbsConfig.FilePath.bindTo(_configurationService);
|
||||
this._cfgSymbolPath = BreadcrumbsConfig.SymbolPath.bindTo(_configurationService);
|
||||
|
||||
this._disposables.add(this._cfgFilePath.onDidChange(_ => this._onDidUpdate.fire(this)));
|
||||
this._disposables.add(this._cfgSymbolPath.onDidChange(_ => this._onDidUpdate.fire(this)));
|
||||
|
||||
this._fileInfo = EditorBreadcrumbsModel._initFilePathInfo(this._uri, workspaceService);
|
||||
this._bindToEditor();
|
||||
this._onDidUpdate.fire(this);
|
||||
@@ -138,6 +137,14 @@ export class EditorBreadcrumbsModel {
|
||||
this._disposables.add(this._editor.onDidChangeModel(_ => this._updateOutline()));
|
||||
this._disposables.add(this._editor.onDidChangeModelLanguage(_ => this._updateOutline()));
|
||||
|
||||
// update when config changes (re-render)
|
||||
this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('breadcrumbs.filteredTypes')) {
|
||||
this._updateOutline(true);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
// update soon'ish as model content change
|
||||
const updateSoon = new TimeoutTimer();
|
||||
this._disposables.add(updateSoon);
|
||||
@@ -221,7 +228,18 @@ export class EditorBreadcrumbsModel {
|
||||
}
|
||||
item = parent;
|
||||
}
|
||||
return chain.reverse();
|
||||
let result: Array<OutlineGroup | OutlineElement> = [];
|
||||
for (let i = chain.length - 1; i >= 0; i--) {
|
||||
let element = chain[i];
|
||||
if (
|
||||
element instanceof OutlineElement
|
||||
&& !this._configurationService.getValue<boolean>(`breadcrumbs.filteredTypes.${SymbolKinds.toString(element.symbol.kind)}`)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
result.push(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _updateOutlineElements(elements: Array<OutlineModel | OutlineGroup | OutlineElement>): void {
|
||||
|
||||
@@ -26,7 +26,7 @@ import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs
|
||||
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
|
||||
|
||||
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
|
||||
@@ -240,7 +240,7 @@ class FileRenderer implements ITreeRenderer<IFileStat | IWorkspaceFolder, FuzzyS
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<IWorkspaceFolder | IFileStat, [number, number, number]>, index: number, templateData: IResourceLabel): void {
|
||||
const fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
|
||||
const fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean; }>('explorer.decorations');
|
||||
const { element } = node;
|
||||
let resource: URI;
|
||||
let fileKind: FileKind;
|
||||
@@ -446,11 +446,13 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
[new OutlineGroupRenderer(), this._instantiationService.createInstance(OutlineElementRenderer)],
|
||||
new OutlineDataSource(),
|
||||
{
|
||||
collapseByDefault: true,
|
||||
expandOnlyOnTwistieClick: true,
|
||||
multipleSelectionSupport: false,
|
||||
sorter: new OutlineItemComparator(this._getOutlineItemCompareType()),
|
||||
identityProvider: new OutlineIdentityProvider(),
|
||||
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider()
|
||||
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(),
|
||||
filter: this._instantiationService.createInstance(OutlineFilter, 'breadcrumbs.filteredTypes')
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorIn
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileService, SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
|
||||
import { ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
@@ -106,7 +106,7 @@ interface ISerializedUntitledEditorInput {
|
||||
resource: string;
|
||||
resourceJSON: object;
|
||||
modeId: string | undefined;
|
||||
encoding: string;
|
||||
encoding: string | undefined;
|
||||
}
|
||||
|
||||
// Register Editor Input Factory
|
||||
@@ -223,7 +223,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory);
|
||||
|
||||
// Register Editor Contributions
|
||||
registerEditorContribution(OpenWorkspaceButtonContribution);
|
||||
registerEditorContribution(OpenWorkspaceButtonContribution.ID, OpenWorkspaceButtonContribution);
|
||||
|
||||
// Register Editor Status
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready);
|
||||
@@ -232,7 +232,10 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
|
||||
|
||||
if (Object.keys(SUPPORTED_ENCODINGS).length > 1) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
|
||||
}
|
||||
|
||||
export class QuickOpenActionContributor extends ActionBarContributor {
|
||||
private openToSideActionInstance: OpenToSideFromQuickOpenAction | undefined;
|
||||
@@ -590,7 +593,7 @@ appendEditorToolItem(
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Trim Whitespace"),
|
||||
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"),
|
||||
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-dark.svg')),
|
||||
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-light.svg'))
|
||||
},
|
||||
@@ -602,7 +605,7 @@ appendEditorToolItem(
|
||||
appendEditorToolItem(
|
||||
{
|
||||
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
|
||||
title: nls.localize('showTrimWhitespace.label', "Show Trim Whitespace"),
|
||||
title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"),
|
||||
iconDark: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-dark.svg')),
|
||||
iconLight: URI.parse(registerAndGetAmdImageURL('vs/workbench/browser/parts/editor/media/paragraph-disabled-light.svg'))
|
||||
},
|
||||
|
||||
@@ -37,7 +37,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
openSideBySideDirection: 'right',
|
||||
closeEmptyGroups: true,
|
||||
labelFormat: 'default',
|
||||
iconTheme: 'vs-seti'
|
||||
iconTheme: 'vs-seti',
|
||||
splitSizing: 'distribute'
|
||||
};
|
||||
|
||||
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
|
||||
|
||||
@@ -495,7 +495,7 @@ export class CloseOneEditorAction extends Action {
|
||||
group = this.editorGroupService.getGroup(context.groupId);
|
||||
|
||||
if (group) {
|
||||
editorIndex = context.editorIndex!; // only allow editor at index if group is valid
|
||||
editorIndex = context.editorIndex; // only allow editor at index if group is valid
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -606,6 +606,10 @@ function registerCloseEditorCommands() {
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
|
||||
|
||||
if (group.activeEditor) {
|
||||
group.pinEditor(group.activeEditor);
|
||||
}
|
||||
|
||||
return group.closeEditors(editorsToClose);
|
||||
}
|
||||
|
||||
@@ -624,6 +628,10 @@ function registerCloseEditorCommands() {
|
||||
|
||||
const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
|
||||
if (group && editor) {
|
||||
if (group.activeEditor) {
|
||||
group.pinEditor(group.activeEditor);
|
||||
}
|
||||
|
||||
return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
|
||||
}
|
||||
|
||||
@@ -744,7 +752,7 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
|
||||
const selection: Array<IEditorIdentifier | IEditorGroup> = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
|
||||
|
||||
// Only respect selection if it contains focused element
|
||||
if (selection && selection.some(s => {
|
||||
if (selection?.some(s => {
|
||||
if (isEditorGroup(s)) {
|
||||
return s.id === focus.groupId;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IEditorProgressService, LongRunningOperation } from 'vs/platform/progre
|
||||
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export interface IOpenEditorResult {
|
||||
readonly control: BaseEditor;
|
||||
@@ -88,8 +89,9 @@ export class EditorControl extends Disposable {
|
||||
this.doSetActiveControl(control);
|
||||
|
||||
// Show editor
|
||||
this.parent.appendChild(control.getContainer());
|
||||
show(control.getContainer());
|
||||
const container = assertIsDefined(control.getContainer());
|
||||
this.parent.appendChild(container);
|
||||
show(container);
|
||||
|
||||
// Indicate to editor that it is now visible
|
||||
control.setVisible(true, this.groupView);
|
||||
@@ -154,7 +156,7 @@ export class EditorControl extends Disposable {
|
||||
|
||||
// If the input did not change, return early and only apply the options
|
||||
// unless the options instruct us to force open it even if it is the same
|
||||
const forceReload = options && options.forceReload;
|
||||
const forceReload = options?.forceReload;
|
||||
const inputMatches = control.input && control.input.matches(editor);
|
||||
if (inputMatches && !forceReload) {
|
||||
|
||||
@@ -203,8 +205,10 @@ export class EditorControl extends Disposable {
|
||||
|
||||
// Remove control from parent and hide
|
||||
const controlInstanceContainer = this._activeControl.getContainer();
|
||||
this.parent.removeChild(controlInstanceContainer);
|
||||
hide(controlInstanceContainer);
|
||||
if (controlInstanceContainer) {
|
||||
this.parent.removeChild(controlInstanceContainer);
|
||||
hide(controlInstanceContainer);
|
||||
}
|
||||
|
||||
// Indicate to editor control
|
||||
this._activeControl.clearInput();
|
||||
|
||||
@@ -16,6 +16,7 @@ import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/com
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
interface IDropOperation {
|
||||
splitDirection?: GroupDirection;
|
||||
@@ -23,7 +24,7 @@ interface IDropOperation {
|
||||
|
||||
class DropOverlay extends Themable {
|
||||
|
||||
private static OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
|
||||
private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
|
||||
|
||||
private container!: HTMLElement;
|
||||
private overlay!: HTMLElement;
|
||||
@@ -84,7 +85,7 @@ class DropOverlay extends Themable {
|
||||
protected updateStyles(): void {
|
||||
|
||||
// Overlay drop background
|
||||
this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
|
||||
this.overlay.style.backgroundColor = this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) || '';
|
||||
|
||||
// Overlay contrast border (if any)
|
||||
const activeContrastBorderColor = this.getColor(activeContrastBorder);
|
||||
@@ -108,7 +109,16 @@ class DropOverlay extends Themable {
|
||||
}
|
||||
|
||||
// Find out if operation is valid
|
||||
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true;
|
||||
let isCopy = true;
|
||||
if (isDraggingGroup) {
|
||||
isCopy = this.isCopyOperation(e);
|
||||
} else if (isDraggingEditor) {
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
isCopy = this.isCopyOperation(e, data[0].identifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCopy) {
|
||||
const sourceGroupView = this.findSourceGroupView();
|
||||
if (sourceGroupView === this.groupView) {
|
||||
@@ -162,12 +172,18 @@ class DropOverlay extends Themable {
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
|
||||
const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
return this.accessor.getGroup(data[0].identifier);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for editor transfer
|
||||
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId);
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
return this.accessor.getGroup(data[0].identifier.groupId);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -189,69 +205,75 @@ class DropOverlay extends Themable {
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier;
|
||||
const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedEditorGroup = data[0].identifier;
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
|
||||
if (sourceGroup) {
|
||||
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
|
||||
return;
|
||||
}
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
|
||||
if (sourceGroup) {
|
||||
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Split to new group
|
||||
let targetGroup: IEditorGroupView | undefined;
|
||||
if (typeof splitDirection === 'number') {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
|
||||
} else {
|
||||
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
|
||||
// Split to new group
|
||||
let targetGroup: IEditorGroupView | undefined;
|
||||
if (typeof splitDirection === 'number') {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
|
||||
} else {
|
||||
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge into existing group
|
||||
else {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
|
||||
} else {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetGroup) {
|
||||
this.accessor.activateGroup(targetGroup);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge into existing group
|
||||
else {
|
||||
if (this.isCopyOperation(event)) {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
|
||||
} else {
|
||||
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetGroup) {
|
||||
this.accessor.activateGroup(targetGroup);
|
||||
}
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}
|
||||
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}
|
||||
|
||||
// Check for editor transfer
|
||||
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
const targetGroup = ensureTargetGroup();
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedEditor = data[0].identifier;
|
||||
const targetGroup = ensureTargetGroup();
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
if (sourceGroup) {
|
||||
if (sourceGroup === targetGroup) {
|
||||
return;
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
if (sourceGroup) {
|
||||
if (sourceGroup === targetGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open in target group
|
||||
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
|
||||
targetGroup.openEditor(draggedEditor.editor, options);
|
||||
|
||||
// Ensure target has focus
|
||||
targetGroup.focus();
|
||||
|
||||
// Close in source group unless we copy
|
||||
const copyEditor = this.isCopyOperation(event, draggedEditor);
|
||||
if (!copyEditor) {
|
||||
sourceGroup.closeEditor(draggedEditor.editor);
|
||||
}
|
||||
}
|
||||
|
||||
// Open in target group
|
||||
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
|
||||
targetGroup.openEditor(draggedEditor.editor, options);
|
||||
|
||||
// Ensure target has focus
|
||||
targetGroup.focus();
|
||||
|
||||
// Close in source group unless we copy
|
||||
const copyEditor = this.isCopyOperation(event, draggedEditor);
|
||||
if (!copyEditor) {
|
||||
sourceGroup.closeEditor(draggedEditor.editor);
|
||||
}
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
}
|
||||
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
}
|
||||
|
||||
// Check for URI transfer
|
||||
@@ -266,7 +288,7 @@ class DropOverlay extends Themable {
|
||||
}
|
||||
|
||||
private isCopyOperation(e: DragEvent, draggedEditor?: IEditorIdentifier): boolean {
|
||||
if (draggedEditor && draggedEditor.editor instanceof EditorInput && !draggedEditor.editor.supportsSplitEditor()) {
|
||||
if (draggedEditor?.editor instanceof EditorInput && !draggedEditor.editor.supportsSplitEditor()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -438,6 +460,10 @@ class DropOverlay extends Themable {
|
||||
}
|
||||
}
|
||||
|
||||
export interface EditorDropTargetDelegate {
|
||||
groupContainsPredicate?(groupView: IEditorGroupView): boolean;
|
||||
}
|
||||
|
||||
export class EditorDropTarget extends Themable {
|
||||
|
||||
private _overlay?: DropOverlay;
|
||||
@@ -450,6 +476,7 @@ export class EditorDropTarget extends Themable {
|
||||
constructor(
|
||||
private accessor: IEditorGroupsAccessor,
|
||||
private container: HTMLElement,
|
||||
private readonly delegate: EditorDropTargetDelegate,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
@@ -523,13 +550,7 @@ export class EditorDropTarget extends Themable {
|
||||
|
||||
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
|
||||
const groups = this.accessor.groups;
|
||||
for (const groupView of groups) {
|
||||
if (isAncestor(child, groupView.element)) {
|
||||
return groupView;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return find(groups, groupView => isAncestor(child, groupView.element) || this.delegate.groupContainsPredicate?.(groupView));
|
||||
}
|
||||
|
||||
private updateContainer(isDraggedOver: boolean): void {
|
||||
|
||||
@@ -53,7 +53,8 @@ import { hash } from 'vs/base/common/hash';
|
||||
import { guessMimeTypes } from 'vs/base/common/mime';
|
||||
import { extname } from 'vs/base/common/resources';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { EditorActivation } from 'vs/platform/editor/common/editor';
|
||||
import { EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
@@ -99,13 +100,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
//#endregion
|
||||
|
||||
private _group: EditorGroup;
|
||||
private _disposed: boolean;
|
||||
private _disposed = false;
|
||||
|
||||
private active: boolean;
|
||||
private dimension: Dimension;
|
||||
private active: boolean | undefined;
|
||||
private dimension: Dimension | undefined;
|
||||
|
||||
private _whenRestored: Promise<void>;
|
||||
private isRestored: boolean;
|
||||
private isRestored = false;
|
||||
|
||||
private scopedInstantiationService: IInstantiationService;
|
||||
|
||||
@@ -123,12 +124,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
constructor(
|
||||
private accessor: IEditorGroupsAccessor,
|
||||
from: IEditorGroupView | ISerializedEditorGroup,
|
||||
from: IEditorGroupView | ISerializedEditorGroup | null,
|
||||
private _index: number,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@@ -149,7 +151,68 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
this.disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0));
|
||||
|
||||
this.create();
|
||||
//#region create()
|
||||
{
|
||||
// Container
|
||||
addClasses(this.element, 'editor-group-container');
|
||||
|
||||
// Container listeners
|
||||
this.registerContainerListeners();
|
||||
|
||||
// Container toolbar
|
||||
this.createContainerToolbar();
|
||||
|
||||
// Container context menu
|
||||
this.createContainerContextMenu();
|
||||
|
||||
// Letterpress container
|
||||
const letterpressContainer = document.createElement('div');
|
||||
addClass(letterpressContainer, 'editor-group-letterpress');
|
||||
this.element.appendChild(letterpressContainer);
|
||||
|
||||
// Progress bar
|
||||
this.progressBar = this._register(new ProgressBar(this.element));
|
||||
this._register(attachProgressBarStyler(this.progressBar, this.themeService));
|
||||
this.progressBar.hide();
|
||||
|
||||
// Scoped services
|
||||
const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element));
|
||||
this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, scopedContextKeyService],
|
||||
[IEditorProgressService, new EditorProgressService(this.progressBar)]
|
||||
));
|
||||
|
||||
// Context keys
|
||||
this.handleGroupContextKeys(scopedContextKeyService);
|
||||
|
||||
// Title container
|
||||
this.titleContainer = document.createElement('div');
|
||||
addClass(this.titleContainer, 'title');
|
||||
this.element.appendChild(this.titleContainer);
|
||||
|
||||
// Title control
|
||||
this.titleAreaControl = this.createTitleAreaControl();
|
||||
|
||||
// Editor container
|
||||
this.editorContainer = document.createElement('div');
|
||||
addClass(this.editorContainer, 'editor-container');
|
||||
this.element.appendChild(this.editorContainer);
|
||||
|
||||
// Editor control
|
||||
this.editorControl = this._register(this.scopedInstantiationService.createInstance(EditorControl, this.editorContainer, this));
|
||||
this._onDidChange.input = this.editorControl.onDidSizeConstraintsChange;
|
||||
|
||||
// Track Focus
|
||||
this.doTrackFocus();
|
||||
|
||||
// Update containers
|
||||
this.updateTitleContainer();
|
||||
this.updateContainer();
|
||||
|
||||
// Update styles
|
||||
this.updateStyles();
|
||||
}
|
||||
//#endregion
|
||||
|
||||
this._whenRestored = this.restoreEditors(from);
|
||||
this._whenRestored.then(() => this.isRestored = true);
|
||||
@@ -157,68 +220,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private create(): void {
|
||||
|
||||
// Container
|
||||
addClasses(this.element, 'editor-group-container');
|
||||
|
||||
// Container listeners
|
||||
this.registerContainerListeners();
|
||||
|
||||
// Container toolbar
|
||||
this.createContainerToolbar();
|
||||
|
||||
// Container context menu
|
||||
this.createContainerContextMenu();
|
||||
|
||||
// Letterpress container
|
||||
const letterpressContainer = document.createElement('div');
|
||||
addClass(letterpressContainer, 'editor-group-letterpress');
|
||||
this.element.appendChild(letterpressContainer);
|
||||
|
||||
// Progress bar
|
||||
this.progressBar = this._register(new ProgressBar(this.element));
|
||||
this._register(attachProgressBarStyler(this.progressBar, this.themeService));
|
||||
this.progressBar.hide();
|
||||
|
||||
// Scoped services
|
||||
const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element));
|
||||
this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
|
||||
[IContextKeyService, scopedContextKeyService],
|
||||
[IEditorProgressService, new EditorProgressService(this.progressBar)]
|
||||
));
|
||||
|
||||
// Context keys
|
||||
this.handleGroupContextKeys(scopedContextKeyService);
|
||||
|
||||
// Title container
|
||||
this.titleContainer = document.createElement('div');
|
||||
addClass(this.titleContainer, 'title');
|
||||
this.element.appendChild(this.titleContainer);
|
||||
|
||||
// Title control
|
||||
this.createTitleAreaControl();
|
||||
|
||||
// Editor container
|
||||
this.editorContainer = document.createElement('div');
|
||||
addClass(this.editorContainer, 'editor-container');
|
||||
this.element.appendChild(this.editorContainer);
|
||||
|
||||
// Editor control
|
||||
this.editorControl = this._register(this.scopedInstantiationService.createInstance(EditorControl, this.editorContainer, this));
|
||||
this._onDidChange.input = this.editorControl.onDidSizeConstraintsChange;
|
||||
|
||||
// Track Focus
|
||||
this.doTrackFocus();
|
||||
|
||||
// Update containers
|
||||
this.updateTitleContainer();
|
||||
this.updateContainer();
|
||||
|
||||
// Update styles
|
||||
this.updateStyles();
|
||||
}
|
||||
|
||||
private handleGroupContextKeys(contextKeyService: IContextKeyService): void {
|
||||
const groupActiveEditorDirtyContextKey = EditorGroupActiveEditorDirtyContext.bindTo(contextKeyService);
|
||||
const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyService);
|
||||
@@ -295,7 +296,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
const removeGroupAction = this._register(new Action(
|
||||
CLOSE_EDITOR_GROUP_COMMAND_ID,
|
||||
localize('closeGroupAction', "Close"),
|
||||
'close-editor-group',
|
||||
'codicon-close',
|
||||
true,
|
||||
() => {
|
||||
this.accessor.removeGroup(this);
|
||||
@@ -410,7 +411,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
toggleClass(this.titleContainer, 'show-file-icons', this.accessor.partOptions.showIcons);
|
||||
}
|
||||
|
||||
private createTitleAreaControl(): void {
|
||||
private createTitleAreaControl(): TitleControl {
|
||||
|
||||
// Clear old if existing
|
||||
if (this.titleAreaControl) {
|
||||
@@ -424,15 +425,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
} else {
|
||||
this.titleAreaControl = this.scopedInstantiationService.createInstance(NoTabsTitleControl, this.titleContainer, this.accessor, this);
|
||||
}
|
||||
|
||||
return this.titleAreaControl;
|
||||
}
|
||||
|
||||
private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Promise<void> {
|
||||
await this._group.removeNonExitingEditor(); // {{SQL CARBON EDIT}} @udeeshagautam perform async correction for non-existing files
|
||||
|
||||
private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup | null): Promise<void> {
|
||||
if (this._group.count === 0) {
|
||||
return; // nothing to show
|
||||
}
|
||||
|
||||
await this._group.removeNonExitingEditor(); // {{SQL CARBON EDIT}} @udeeshagautam perform async correction for non-existing files
|
||||
|
||||
// Determine editor options
|
||||
let options: EditorOptions;
|
||||
if (from instanceof EditorGroupView) {
|
||||
@@ -604,7 +607,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// Recreate and layout control
|
||||
this.createTitleAreaControl();
|
||||
this.layoutTitleAreaControl();
|
||||
if (this.dimension) {
|
||||
this.layoutTitleAreaControl(this.dimension.width);
|
||||
}
|
||||
|
||||
// Ensure to show active editor if any
|
||||
if (this._group.activeEditor) {
|
||||
@@ -829,7 +834,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Determine options
|
||||
const openEditorOptions: IEditorOpenOptions = {
|
||||
index: options ? options.index : undefined,
|
||||
pinned: !this.accessor.partOptions.enablePreview || editor.isDirty() || (options && options.pinned) || (options && typeof options.index === 'number'),
|
||||
pinned: !this.accessor.partOptions.enablePreview || editor.isDirty() || options?.pinned || typeof options?.index === 'number',
|
||||
active: this._group.count === 0 || !options || !options.inactive
|
||||
};
|
||||
|
||||
@@ -843,13 +848,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
let activateGroup = false;
|
||||
let restoreGroup = false;
|
||||
|
||||
if (options && options.activation === EditorActivation.ACTIVATE) {
|
||||
if (options?.activation === EditorActivation.ACTIVATE) {
|
||||
// Respect option to force activate an editor group.
|
||||
activateGroup = true;
|
||||
} else if (options && options.activation === EditorActivation.RESTORE) {
|
||||
} else if (options?.activation === EditorActivation.RESTORE) {
|
||||
// Respect option to force restore an editor group.
|
||||
restoreGroup = true;
|
||||
} else if (options && options.activation === EditorActivation.PRESERVE) {
|
||||
} else if (options?.activation === EditorActivation.PRESERVE) {
|
||||
// Respect option to preserve active editor group.
|
||||
activateGroup = false;
|
||||
restoreGroup = false;
|
||||
@@ -921,23 +926,67 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return openEditorPromise;
|
||||
}
|
||||
|
||||
private doHandleOpenEditorError(error: Error, editor: EditorInput, options?: EditorOptions): void {
|
||||
private async doHandleOpenEditorError(error: Error, editor: EditorInput, options?: EditorOptions): Promise<void> {
|
||||
|
||||
// Report error only if this was not us restoring previous error state or
|
||||
// we are told to ignore errors that occur from opening an editor
|
||||
if (this.isRestored && !isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
|
||||
// Extract possible error actions from the error
|
||||
let errorActions: ReadonlyArray<IAction> | undefined = undefined;
|
||||
if (isErrorWithActions(error)) {
|
||||
actions.primary = (error as IErrorWithActions).actions;
|
||||
errorActions = (error as IErrorWithActions).actions;
|
||||
}
|
||||
|
||||
const handle = this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('editorOpenError', "Unable to open '{0}': {1}.", editor.getName(), toErrorMessage(error)),
|
||||
actions
|
||||
});
|
||||
// If the context is USER, we try to show a modal dialog instead of a background notification
|
||||
if (options?.context === EditorOpenContext.USER) {
|
||||
const buttons: string[] = [];
|
||||
if (Array.isArray(errorActions) && errorActions.length > 0) {
|
||||
errorActions.forEach(action => buttons.push(action.label));
|
||||
} else {
|
||||
buttons.push(localize('ok', 'OK'));
|
||||
}
|
||||
|
||||
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
|
||||
let cancelId: number | undefined = undefined;
|
||||
if (buttons.length === 1) {
|
||||
buttons.push(localize('cancel', "Cancel"));
|
||||
cancelId = 1;
|
||||
}
|
||||
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Error,
|
||||
localize('editorOpenErrorDialog', "Unable to open '{0}'", editor.getName()),
|
||||
buttons,
|
||||
{
|
||||
detail: toErrorMessage(error),
|
||||
cancelId
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure to run any error action if present
|
||||
if (result.choice !== cancelId && Array.isArray(errorActions)) {
|
||||
const errorAction = errorActions[result.choice];
|
||||
if (errorAction) {
|
||||
errorAction.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, show a background notification.
|
||||
else {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
if (Array.isArray(errorActions)) {
|
||||
actions.primary = errorActions;
|
||||
}
|
||||
|
||||
const handle = this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('editorOpenError', "Unable to open '{0}': {1}.", editor.getName(), toErrorMessage(error)),
|
||||
actions
|
||||
});
|
||||
|
||||
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
|
||||
}
|
||||
}
|
||||
|
||||
// Event
|
||||
@@ -1073,7 +1122,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
// Do close
|
||||
this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined);
|
||||
this.doCloseEditor(editor, options?.preserveFocus ? false : undefined);
|
||||
}
|
||||
|
||||
private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
|
||||
@@ -1322,7 +1371,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// Close active editor last if contained in editors list to close
|
||||
if (closeActiveEditor) {
|
||||
this.doCloseActiveEditor(options && options.preserveFocus ? false : undefined);
|
||||
this.doCloseActiveEditor(options?.preserveFocus ? false : undefined);
|
||||
}
|
||||
|
||||
// Forward to title control
|
||||
@@ -1450,9 +1499,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// Container
|
||||
if (isEmpty) {
|
||||
this.element.style.backgroundColor = this.getColor(EDITOR_GROUP_EMPTY_BACKGROUND);
|
||||
this.element.style.backgroundColor = this.getColor(EDITOR_GROUP_EMPTY_BACKGROUND) || '';
|
||||
} else {
|
||||
this.element.style.backgroundColor = null;
|
||||
this.element.style.backgroundColor = '';
|
||||
}
|
||||
|
||||
// Title control
|
||||
@@ -1467,10 +1516,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.titleContainer.style.removeProperty('--title-border-bottom-color');
|
||||
}
|
||||
|
||||
this.titleContainer.style.backgroundColor = this.getColor(showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND);
|
||||
this.titleContainer.style.backgroundColor = this.getColor(showTabs ? EDITOR_GROUP_HEADER_TABS_BACKGROUND : EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND) || '';
|
||||
|
||||
// Editor container
|
||||
this.editorContainer.style.backgroundColor = this.getColor(editorBackground);
|
||||
this.editorContainer.style.backgroundColor = this.getColor(editorBackground) || '';
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -1495,12 +1544,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.editorContainer.style.height = `calc(100% - ${this.titleAreaControl.getPreferredHeight()}px)`;
|
||||
|
||||
// Forward to controls
|
||||
this.layoutTitleAreaControl();
|
||||
this.layoutTitleAreaControl(width);
|
||||
this.editorControl.layout(new Dimension(this.dimension.width, this.dimension.height - this.titleAreaControl.getPreferredHeight()));
|
||||
}
|
||||
|
||||
private layoutTitleAreaControl(): void {
|
||||
this.titleAreaControl.layout(new Dimension(this.dimension.width, this.titleAreaControl.getPreferredHeight()));
|
||||
private layoutTitleAreaControl(width: number): void {
|
||||
this.titleAreaControl.layout(new Dimension(width, this.titleAreaControl.getPreferredHeight()));
|
||||
}
|
||||
|
||||
relayout(): void {
|
||||
@@ -1528,7 +1577,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
private override: () => Promise<IEditor | undefined>;
|
||||
private override: (() => Promise<IEditor | undefined>) | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private _group: GroupIdentifier,
|
||||
@@ -1553,7 +1602,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
this.override = callback;
|
||||
}
|
||||
|
||||
isPrevented(): () => Promise<IEditor | undefined> {
|
||||
isPrevented(): (() => Promise<IEditor | undefined>) | undefined {
|
||||
return this.override;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget';
|
||||
import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { MementoObject } from 'vs/workbench/common/memento';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
interface IEditorPartUIState {
|
||||
serializedGrid: ISerializedGrid;
|
||||
@@ -49,13 +50,13 @@ class GridWidgetView<T extends IView> implements IView {
|
||||
private _onDidChange = new Relay<{ width: number; height: number; } | undefined>();
|
||||
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
|
||||
|
||||
private _gridWidget: Grid<T>;
|
||||
private _gridWidget: Grid<T> | undefined;
|
||||
|
||||
get gridWidget(): Grid<T> {
|
||||
get gridWidget(): Grid<T> | undefined {
|
||||
return this._gridWidget;
|
||||
}
|
||||
|
||||
set gridWidget(grid: Grid<T>) {
|
||||
set gridWidget(grid: Grid<T> | undefined) {
|
||||
this.element.innerHTML = '';
|
||||
|
||||
if (grid) {
|
||||
@@ -113,9 +114,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
|
||||
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
|
||||
|
||||
private _onDidVisibilityChange = this._register(new Emitter<boolean>());
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
private readonly workspaceMemento: MementoObject;
|
||||
@@ -123,17 +121,18 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
private _partOptions: IEditorPartOptions;
|
||||
|
||||
private _activeGroup: IEditorGroupView;
|
||||
private groupViews: Map<GroupIdentifier, IEditorGroupView> = new Map<GroupIdentifier, IEditorGroupView>();
|
||||
private mostRecentActiveGroups: GroupIdentifier[] = [];
|
||||
|
||||
private container: HTMLElement;
|
||||
private centeredLayoutWidget: CenteredViewLayout;
|
||||
private gridWidget: SerializableGrid<IEditorGroupView>;
|
||||
private container: HTMLElement | undefined;
|
||||
|
||||
private centeredLayoutWidget!: CenteredViewLayout;
|
||||
|
||||
private gridWidget!: SerializableGrid<IEditorGroupView>;
|
||||
private gridWidgetView: GridWidgetView<IEditorGroupView>;
|
||||
|
||||
private _whenRestored: Promise<void>;
|
||||
private whenRestoredResolve: () => void;
|
||||
private whenRestoredResolve: (() => void) | undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -204,9 +203,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
//#region IEditorGroupsService
|
||||
|
||||
private _contentDimension: Dimension;
|
||||
private _contentDimension!: Dimension;
|
||||
get contentDimension(): Dimension { return this._contentDimension; }
|
||||
|
||||
private _activeGroup!: IEditorGroupView;
|
||||
get activeGroup(): IEditorGroupView {
|
||||
return this._activeGroup;
|
||||
}
|
||||
@@ -463,7 +463,11 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
}
|
||||
}
|
||||
|
||||
private shouldRestoreFocus(target: Element): boolean {
|
||||
private shouldRestoreFocus(target: Element | undefined): boolean {
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
if (activeElement === document.body) {
|
||||
@@ -490,7 +494,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
const group = this.doAddGroup(locationView, direction);
|
||||
|
||||
if (options && options.activate) {
|
||||
if (options?.activate) {
|
||||
this.doSetGroupActive(group);
|
||||
}
|
||||
|
||||
@@ -503,7 +507,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
// Add to grid widget
|
||||
this.gridWidget.addView(
|
||||
newGroupView,
|
||||
Sizing.Distribute,
|
||||
this.getSplitSizingStyle(),
|
||||
locationView,
|
||||
this.toGridViewDirection(direction),
|
||||
);
|
||||
@@ -520,6 +524,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
return newGroupView;
|
||||
}
|
||||
|
||||
private getSplitSizingStyle(): Sizing {
|
||||
return this._partOptions.splitSizing === 'split' ? Sizing.Split : Sizing.Distribute;
|
||||
}
|
||||
|
||||
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup | null): IEditorGroupView {
|
||||
|
||||
// Create group view
|
||||
@@ -670,7 +678,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
}
|
||||
|
||||
// Remove from grid widget & dispose
|
||||
this.gridWidget.removeView(groupView, Sizing.Distribute);
|
||||
this.gridWidget.removeView(groupView, this.getSplitSizingStyle());
|
||||
groupView.dispose();
|
||||
|
||||
// Restore focus if we had it previously (we run this after gridWidget.removeView() is called
|
||||
@@ -700,7 +708,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
const restoreFocus = this.shouldRestoreFocus(sourceView.element);
|
||||
|
||||
// Move through grid widget API
|
||||
this.gridWidget.moveView(sourceView, Sizing.Distribute, targetView, this.toGridViewDirection(direction));
|
||||
this.gridWidget.moveView(sourceView, this.getSplitSizingStyle(), targetView, this.toGridViewDirection(direction));
|
||||
|
||||
// Restore focus if we had it previously (we run this after gridWidget.removeView() is called
|
||||
// because removing a view can mean to reparent it and thus focus would be removed otherwise)
|
||||
@@ -744,7 +752,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
const inactive = !sourceView.isActive(editor) || this._activeGroup !== sourceView;
|
||||
const copyOptions: ICopyEditorOptions = { index, inactive, preserveFocus: inactive };
|
||||
|
||||
if (options && options.mode === MergeGroupMode.COPY_EDITORS) {
|
||||
if (options?.mode === MergeGroupMode.COPY_EDITORS) {
|
||||
sourceView.copyEditor(editor, targetView, copyOptions);
|
||||
} else {
|
||||
sourceView.moveEditor(editor, targetView, copyOptions);
|
||||
@@ -796,7 +804,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
this.container.style.backgroundColor = this.getColor(editorBackground);
|
||||
const container = assertIsDefined(this.container);
|
||||
container.style.backgroundColor = this.getColor(editorBackground) || '';
|
||||
|
||||
const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder, background: this.theme.getColor(EDITOR_PANE_BACKGROUND) || Color.transparent };
|
||||
this.gridWidget.style(separatorBorderStyle);
|
||||
@@ -817,7 +826,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
|
||||
|
||||
// Drop support
|
||||
this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container));
|
||||
this._register(this.createEditorDropTarget(this.container, {}));
|
||||
|
||||
return this.container;
|
||||
}
|
||||
@@ -850,7 +859,11 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
}
|
||||
|
||||
// Signal restored
|
||||
Promise.all(this.groups.map(group => group.whenRestored)).finally(() => this.whenRestoredResolve());
|
||||
Promise.all(this.groups.map(group => group.whenRestored)).finally(() => {
|
||||
if (this.whenRestoredResolve) {
|
||||
this.whenRestoredResolve();
|
||||
}
|
||||
});
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
@@ -861,7 +874,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
private doCreateGridControlWithPreviousState(): boolean {
|
||||
const uiState: IEditorPartUIState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
|
||||
if (uiState && uiState.serializedGrid) {
|
||||
if (uiState?.serializedGrid) {
|
||||
try {
|
||||
|
||||
// MRU
|
||||
@@ -950,7 +963,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
}
|
||||
|
||||
private updateContainer(): void {
|
||||
toggleClass(this.container, 'empty', this.isEmpty);
|
||||
const container = assertIsDefined(this.container);
|
||||
toggleClass(container, 'empty', this.isEmpty);
|
||||
}
|
||||
|
||||
private notifyGroupIndexChange(): void {
|
||||
@@ -1024,15 +1038,19 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
||||
|
||||
//#endregion
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
return {
|
||||
type: Parts.EDITOR_PART
|
||||
};
|
||||
}
|
||||
|
||||
//#region TODO@matt this should move into some kind of service
|
||||
|
||||
createEditorDropTarget(container: HTMLElement, delegate: EditorDropTargetDelegate): IDisposable {
|
||||
return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
registerSingleton(IEditorGroupsService, EditorPart);
|
||||
|
||||
@@ -15,7 +15,7 @@ import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
@@ -23,7 +23,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
|
||||
constructor(
|
||||
private editor: EditorInput,
|
||||
private editor: IEditorInput,
|
||||
private _group: IEditorGroup,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IModelService private readonly modelService: IModelService
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'vs/css!./media/editorstatus';
|
||||
import * as nls from 'vs/nls';
|
||||
import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { format } from 'vs/base/common/strings';
|
||||
import { extname, basename } from 'vs/base/common/resources';
|
||||
import { extname, basename, isEqual } from 'vs/base/common/resources';
|
||||
import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
@@ -39,7 +39,7 @@ import { ConfigurationChangedEvent, IEditorOptions, EditorOption } from 'vs/edit
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
@@ -57,7 +57,7 @@ import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/q
|
||||
class SideBySideEditorEncodingSupport implements IEncodingSupport {
|
||||
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
|
||||
|
||||
getEncoding(): string {
|
||||
getEncoding(): string | undefined {
|
||||
return this.master.getEncoding(); // always report from modified (right hand) side
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
private readonly screenRedearModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly indentationElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly selectionElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly encodingElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly encodingElement = Object.keys(SUPPORTED_ENCODINGS).length > 1 ? this._register(new MutableDisposable<IStatusbarEntryAccessor>()) : undefined;
|
||||
private readonly eolElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly modeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
private readonly metadataElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
|
||||
@@ -355,7 +355,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
|
||||
}
|
||||
|
||||
const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [
|
||||
const picks: QuickPickInput<IQuickPickItem & { run(): void; }>[] = [
|
||||
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
|
||||
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
|
||||
activeTextEditorWidget.getAction(DetectIndentation.ID),
|
||||
@@ -366,7 +366,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
return {
|
||||
id: a.id,
|
||||
label: a.label,
|
||||
detail: Language.isDefaultVariant() ? undefined : a.alias,
|
||||
detail: (Language.isDefaultVariant() || a.label === a.alias) ? undefined : a.alias,
|
||||
run: () => {
|
||||
activeTextEditorWidget.focus();
|
||||
a.run();
|
||||
@@ -378,7 +378,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") });
|
||||
|
||||
const action = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
|
||||
return action && action.run();
|
||||
return action?.run();
|
||||
}
|
||||
|
||||
private updateTabFocusModeElement(visible: boolean): void {
|
||||
@@ -440,6 +440,10 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private updateEncodingElement(text: string | undefined): void {
|
||||
if (!this.encodingElement) {
|
||||
return; // return early if encoding should not show (e.g. in Web we only support utf8)
|
||||
}
|
||||
|
||||
if (!text) {
|
||||
this.encodingElement.clear();
|
||||
return;
|
||||
@@ -573,7 +577,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
this.onModeChange(activeCodeEditor);
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
this.onEncodingChange(activeControl);
|
||||
this.onEncodingChange(activeControl, activeCodeEditor);
|
||||
this.onIndentationChange(activeCodeEditor);
|
||||
this.onMetadataChange(activeControl);
|
||||
|
||||
@@ -733,20 +737,23 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
const textModel = editorWidget.getModel();
|
||||
if (textModel) {
|
||||
info.selections.forEach(selection => {
|
||||
info.charactersSelected! += textModel.getValueLengthInRange(selection);
|
||||
if (typeof info.charactersSelected !== 'number') {
|
||||
info.charactersSelected = 0;
|
||||
}
|
||||
|
||||
info.charactersSelected += textModel.getCharacterCountInRange(selection);
|
||||
});
|
||||
}
|
||||
|
||||
// Compute the visible column for one selection. This will properly handle tabs and their configured widths
|
||||
if (info.selections.length === 1) {
|
||||
const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition()!);
|
||||
const editorPosition = editorWidget.getPosition();
|
||||
|
||||
let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
|
||||
selectionClone = new Selection(
|
||||
selectionClone.selectionStartLineNumber,
|
||||
selectionClone.selectionStartColumn,
|
||||
selectionClone.positionLineNumber,
|
||||
visibleColumn
|
||||
let selectionClone = new Selection(
|
||||
info.selections[0].selectionStartLineNumber,
|
||||
info.selections[0].selectionStartColumn,
|
||||
info.selections[0].positionLineNumber,
|
||||
editorPosition ? editorWidget.getStatusbarColumn(editorPosition) : info.selections[0].positionColumn
|
||||
);
|
||||
|
||||
info.selections[0] = selectionClone;
|
||||
@@ -769,19 +776,21 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
private onEncodingChange(e?: IBaseEditor): void {
|
||||
if (e && !this.isActiveEditor(e)) {
|
||||
private onEncodingChange(editor: IBaseEditor | undefined, editorWidget: ICodeEditor | undefined): void {
|
||||
if (editor && !this.isActiveEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const info: StateDelta = { encoding: undefined };
|
||||
|
||||
// We only support text based editors
|
||||
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
|
||||
const encodingSupport: IEncodingSupport | null = e.input ? toEditorWithEncodingSupport(e.input) : null;
|
||||
// We only support text based editors that have a model associated
|
||||
// This ensures we do not show the encoding picker while an editor
|
||||
// is still loading.
|
||||
if (editor && editorWidget?.hasModel()) {
|
||||
const encodingSupport: IEncodingSupport | null = editor.input ? toEditorWithEncodingSupport(editor.input) : null;
|
||||
if (encodingSupport) {
|
||||
const rawEncoding = encodingSupport.getEncoding();
|
||||
const encodingInfo = SUPPORTED_ENCODINGS[rawEncoding];
|
||||
const encodingInfo = typeof rawEncoding === 'string' ? SUPPORTED_ENCODINGS[rawEncoding] : undefined;
|
||||
if (encodingInfo) {
|
||||
info.encoding = encodingInfo.labelShort; // if we have a label, take it from there
|
||||
} else {
|
||||
@@ -797,8 +806,10 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (activeControl) {
|
||||
const activeResource = toResource(activeControl.input, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
if (activeResource && activeResource.toString() === resource.toString()) {
|
||||
return this.onEncodingChange(activeControl); // only update if the encoding changed for the active resource
|
||||
if (activeResource && isEqual(activeResource, resource)) {
|
||||
const activeCodeEditor = withNullAsUndefined(getCodeEditor(activeControl.getControl()));
|
||||
|
||||
return this.onEncodingChange(activeControl, activeCodeEditor); // only update if the encoding changed for the active resource
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -877,7 +888,7 @@ export class ChangeModeAction extends Action {
|
||||
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }) : null;
|
||||
|
||||
let hasLanguageSupport = !!resource;
|
||||
if (resource && resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
if (resource?.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
|
||||
}
|
||||
|
||||
@@ -1041,11 +1052,11 @@ export class ChangeModeAction extends Action {
|
||||
let fakeResource: URI | undefined;
|
||||
|
||||
const extensions = this.modeService.getExtensions(lang);
|
||||
if (extensions && extensions.length) {
|
||||
if (extensions?.length) {
|
||||
fakeResource = URI.file(extensions[0]);
|
||||
} else {
|
||||
const filenames = this.modeService.getFilenames(lang);
|
||||
if (filenames && filenames.length) {
|
||||
if (filenames?.length) {
|
||||
fakeResource = URI.file(filenames[0]);
|
||||
}
|
||||
}
|
||||
@@ -1089,12 +1100,12 @@ export class ChangeEOLAction extends Action {
|
||||
{ label: nlsEOLCRLF, eol: EndOfLineSequence.CRLF },
|
||||
];
|
||||
|
||||
const selectedIndex = (textModel && textModel.getEOL() === '\n') ? 0 : 1;
|
||||
const selectedIndex = (textModel?.getEOL() === '\n') ? 0 : 1;
|
||||
|
||||
const eol = await this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] });
|
||||
if (eol) {
|
||||
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
|
||||
if (activeCodeEditor?.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
|
||||
textModel = activeCodeEditor.getModel();
|
||||
textModel.pushEOL(eol.eol);
|
||||
}
|
||||
@@ -1134,14 +1145,19 @@ export class ChangeEncodingAction extends Action {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
|
||||
}
|
||||
|
||||
let saveWithEncodingPick: IQuickPickItem;
|
||||
let reopenWithEncodingPick: IQuickPickItem;
|
||||
if (Language.isDefaultVariant()) {
|
||||
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
|
||||
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
|
||||
} else {
|
||||
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding"), detail: 'Save with Encoding', };
|
||||
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding"), detail: 'Reopen with Encoding' };
|
||||
const saveWithEncodingPick: IQuickPickItem = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
|
||||
const reopenWithEncodingPick: IQuickPickItem = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
|
||||
|
||||
if (!Language.isDefaultVariant()) {
|
||||
const saveWithEncodingAlias = 'Save with Encoding';
|
||||
if (saveWithEncodingAlias !== saveWithEncodingPick.label) {
|
||||
saveWithEncodingPick.detail = saveWithEncodingAlias;
|
||||
}
|
||||
|
||||
const reopenWithEncodingAlias = 'Reopen with Encoding';
|
||||
if (reopenWithEncodingAlias !== reopenWithEncodingPick.label) {
|
||||
reopenWithEncodingPick.detail = reopenWithEncodingAlias;
|
||||
}
|
||||
}
|
||||
|
||||
let action: IQuickPickItem;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IOverlayWidget, ICodeEditor, IOverlayWidgetPosition, OverlayWidgetPosit
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { $, append } from 'vs/base/browser/dom';
|
||||
import { $, append, clearNode } from 'vs/base/browser/dom';
|
||||
import { attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
import { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -31,12 +31,14 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
constructor(
|
||||
private editor: ICodeEditor,
|
||||
private label: string,
|
||||
keyBindingAction: string,
|
||||
keyBindingAction: string | null,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IThemeService private readonly themeService: IThemeService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._domNode = $('.floating-click-widget');
|
||||
|
||||
if (keyBindingAction) {
|
||||
const keybinding = keybindingService.lookupKeybinding(keyBindingAction);
|
||||
if (keybinding) {
|
||||
@@ -60,7 +62,7 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
}
|
||||
|
||||
render() {
|
||||
this._domNode = $('.floating-click-widget');
|
||||
clearNode(this._domNode);
|
||||
|
||||
this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder }, colors => {
|
||||
const backgroundColor = colors.buttonBackground ? colors.buttonBackground : colors.editorBackground;
|
||||
@@ -73,9 +75,9 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
this._domNode.style.color = foregroundColor.toString();
|
||||
}
|
||||
|
||||
const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : null;
|
||||
this._domNode.style.borderWidth = borderColor ? '1px' : null;
|
||||
this._domNode.style.borderStyle = borderColor ? 'solid' : null;
|
||||
const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : '';
|
||||
this._domNode.style.borderWidth = borderColor ? '1px' : '';
|
||||
this._domNode.style.borderStyle = borderColor ? 'solid' : '';
|
||||
this._domNode.style.borderColor = borderColor;
|
||||
}));
|
||||
|
||||
@@ -99,7 +101,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
|
||||
return editor.getContribution<OpenWorkspaceButtonContribution>(OpenWorkspaceButtonContribution.ID);
|
||||
}
|
||||
|
||||
private static readonly ID = 'editor.contrib.openWorkspaceButton';
|
||||
public static readonly ID = 'editor.contrib.openWorkspaceButton';
|
||||
|
||||
private openWorkspaceButton: FloatingClickWidget | undefined;
|
||||
|
||||
@@ -120,10 +122,6 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
|
||||
this._register(this.editor.onDidChangeModel(e => this.update()));
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return OpenWorkspaceButtonContribution.ID;
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
if (!this.shouldShowButton(this.editor)) {
|
||||
this.disposeOpenWorkspaceWidgetRenderer();
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
overflow: visible; /* ...but still show the close button on hover, focus and when dirty */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close {
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off:not(.dirty) > .tab-close {
|
||||
display: none; /* hide the close action bar when we are configured to hide it */
|
||||
}
|
||||
|
||||
@@ -232,11 +232,16 @@
|
||||
padding-right: 5px; /* we need less room when sizing is shrink */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty-border-top > .tab-close {
|
||||
display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without close button */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off.dirty:not(.dirty-border-top) {
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: center;
|
||||
background-position-x: calc(100% - 6px); /* to the right of the tab label */
|
||||
padding-right: 28px; /* make room for dirty indication when we are running without close button */
|
||||
padding-right: 0; /* remove extra padding when we are running without close button */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.close-button-off > .tab-close {
|
||||
pointer-events: none; /* don't allow dirty state/close button to be clicked when running without close button */
|
||||
}
|
||||
|
||||
/* Editor Actions */
|
||||
|
||||
@@ -14,7 +14,7 @@ import { EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { withNullAsUndefined, assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||
|
||||
interface IRenderedEditorLabel {
|
||||
editor?: IEditorInput;
|
||||
@@ -22,23 +22,23 @@ interface IRenderedEditorLabel {
|
||||
}
|
||||
|
||||
export class NoTabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private editorLabel: IResourceLabel;
|
||||
private titleContainer: HTMLElement | undefined;
|
||||
private editorLabel: IResourceLabel | undefined;
|
||||
private activeLabel: IRenderedEditorLabel = Object.create(null);
|
||||
|
||||
protected create(parent: HTMLElement): void {
|
||||
this.titleContainer = parent;
|
||||
this.titleContainer.draggable = true;
|
||||
const titleContainer = this.titleContainer = parent;
|
||||
titleContainer.draggable = true;
|
||||
|
||||
//Container listeners
|
||||
this.registerContainerListeners();
|
||||
this.registerContainerListeners(titleContainer);
|
||||
|
||||
// Gesture Support
|
||||
this._register(Gesture.addTarget(this.titleContainer));
|
||||
this._register(Gesture.addTarget(titleContainer));
|
||||
|
||||
const labelContainer = document.createElement('div');
|
||||
addClass(labelContainer, 'label-container');
|
||||
this.titleContainer.appendChild(labelContainer);
|
||||
titleContainer.appendChild(labelContainer);
|
||||
|
||||
// Editor Label
|
||||
this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, undefined)).element;
|
||||
@@ -46,41 +46,41 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
// Breadcrumbs
|
||||
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent });
|
||||
toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
this._register({ dispose: () => removeClass(this.titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node
|
||||
toggleClass(titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
this._register({ dispose: () => removeClass(titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node
|
||||
|
||||
// Right Actions Container
|
||||
const actionsContainer = document.createElement('div');
|
||||
addClass(actionsContainer, 'title-actions');
|
||||
this.titleContainer.appendChild(actionsContainer);
|
||||
titleContainer.appendChild(actionsContainer);
|
||||
|
||||
// Editor actions toolbar
|
||||
this.createEditorActionsToolBar(actionsContainer);
|
||||
}
|
||||
|
||||
private registerContainerListeners(): void {
|
||||
private registerContainerListeners(titleContainer: HTMLElement): void {
|
||||
|
||||
// Group dragging
|
||||
this.enableGroupDragging(this.titleContainer);
|
||||
this.enableGroupDragging(titleContainer);
|
||||
|
||||
// Pin on double click
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
this._register(addDisposableListener(titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
|
||||
// Detect mouse click
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
this._register(addDisposableListener(titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Detect touch
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Context Menu
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
|
||||
this._register(addDisposableListener(titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
|
||||
if (this.group.activeEditor) {
|
||||
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
|
||||
this.onContextMenu(this.group.activeEditor, e, titleContainer);
|
||||
}
|
||||
}));
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => {
|
||||
this._register(addDisposableListener(titleContainer, TouchEventType.Contextmenu, (e: Event) => {
|
||||
if (this.group.activeEditor) {
|
||||
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
|
||||
this.onContextMenu(this.group.activeEditor, e, titleContainer);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -157,10 +157,11 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
|
||||
updateEditorDirty(editor: IEditorInput): void {
|
||||
this.ifEditorIsActive(editor, () => {
|
||||
const titleContainer = assertIsDefined(this.titleContainer);
|
||||
if (editor.isDirty()) {
|
||||
addClass(this.titleContainer, 'dirty');
|
||||
addClass(titleContainer, 'dirty');
|
||||
} else {
|
||||
removeClass(this.titleContainer, 'dirty');
|
||||
removeClass(titleContainer, 'dirty');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -176,7 +177,9 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
protected handleBreadcrumbsEnablementChange(): void {
|
||||
toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
const titleContainer = assertIsDefined(this.titleContainer);
|
||||
|
||||
toggleClass(titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
@@ -230,9 +233,10 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Clear if there is no editor
|
||||
const [titleContainer, editorLabel] = assertAllDefined(this.titleContainer, this.editorLabel);
|
||||
if (!editor) {
|
||||
removeClass(this.titleContainer, 'dirty');
|
||||
this.editorLabel.clear();
|
||||
removeClass(titleContainer, 'dirty');
|
||||
editorLabel.clear();
|
||||
this.clearEditorActionsToolbar();
|
||||
}
|
||||
|
||||
@@ -261,11 +265,11 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
title = ''; // dont repeat what is already shown
|
||||
}
|
||||
|
||||
this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
|
||||
editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
|
||||
if (isGroupActive) {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
|
||||
editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
|
||||
} else {
|
||||
this.editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
|
||||
editorLabel.element.style.color = this.getColor(TAB_UNFOCUSED_ACTIVE_FOREGROUND);
|
||||
}
|
||||
|
||||
// Update Editor Actions Toolbar
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IMAGE_PREVIEW_BORDER } from 'vs/workbench/common/theme';
|
||||
export interface IResourceDescriptor {
|
||||
readonly resource: URI;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly size?: number;
|
||||
readonly etag?: string;
|
||||
readonly mime: string;
|
||||
}
|
||||
@@ -82,8 +82,8 @@ export class ResourceViewer {
|
||||
container.className = 'monaco-resource-viewer';
|
||||
|
||||
// Large Files
|
||||
if (descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
|
||||
return FileTooLargeFileView.create(container, descriptor, scrollbar, delegate);
|
||||
if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) {
|
||||
return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate);
|
||||
}
|
||||
|
||||
// Seemingly Binary Files
|
||||
@@ -96,11 +96,11 @@ export class ResourceViewer {
|
||||
class FileTooLargeFileView {
|
||||
static create(
|
||||
container: HTMLElement,
|
||||
descriptor: IResourceDescriptor,
|
||||
descriptorSize: number,
|
||||
scrollbar: DomScrollableElement,
|
||||
delegate: ResourceViewerDelegate
|
||||
) {
|
||||
const size = BinarySize.formatSize(descriptor.size);
|
||||
const size = BinarySize.formatSize(descriptorSize);
|
||||
delegate.metadataClb(size);
|
||||
|
||||
DOM.clearNode(container);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe
|
||||
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Event, Relay, Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
@@ -47,10 +48,10 @@ export class SideBySideEditor extends BaseEditor {
|
||||
protected masterEditor?: BaseEditor;
|
||||
protected detailsEditor?: BaseEditor;
|
||||
|
||||
private masterEditorContainer: HTMLElement;
|
||||
private detailsEditorContainer: HTMLElement;
|
||||
private masterEditorContainer: HTMLElement | undefined;
|
||||
private detailsEditorContainer: HTMLElement | undefined;
|
||||
|
||||
private splitview: SplitView;
|
||||
private splitview: SplitView | undefined;
|
||||
private dimension: DOM.Dimension = new DOM.Dimension(0, 0);
|
||||
|
||||
private onDidCreateEditors = this._register(new Emitter<{ width: number; height: number; } | undefined>());
|
||||
@@ -69,8 +70,8 @@ export class SideBySideEditor extends BaseEditor {
|
||||
protected createEditor(parent: HTMLElement): void {
|
||||
DOM.addClass(parent, 'side-by-side-editor');
|
||||
|
||||
this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
|
||||
this._register(this.splitview.onDidSashReset(() => this.splitview.distributeViewSizes()));
|
||||
const splitview = this.splitview = this._register(new SplitView(parent, { orientation: Orientation.HORIZONTAL }));
|
||||
this._register(this.splitview.onDidSashReset(() => splitview.distributeViewSizes()));
|
||||
|
||||
this.detailsEditorContainer = DOM.$('.details-editor-container');
|
||||
this.splitview.addView({
|
||||
@@ -140,7 +141,9 @@ export class SideBySideEditor extends BaseEditor {
|
||||
|
||||
layout(dimension: DOM.Dimension): void {
|
||||
this.dimension = dimension;
|
||||
this.splitview.layout(dimension.width);
|
||||
|
||||
const splitview = assertIsDefined(this.splitview);
|
||||
splitview.layout(dimension.width);
|
||||
}
|
||||
|
||||
getControl(): IEditorControl | undefined {
|
||||
@@ -179,8 +182,8 @@ export class SideBySideEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
|
||||
const detailsEditor = this.doCreateEditor(newInput.details, this.detailsEditorContainer);
|
||||
const masterEditor = this.doCreateEditor(newInput.master, this.masterEditorContainer);
|
||||
const detailsEditor = this.doCreateEditor(newInput.details, assertIsDefined(this.detailsEditorContainer));
|
||||
const masterEditor = this.doCreateEditor(newInput.master, assertIsDefined(this.masterEditorContainer));
|
||||
|
||||
return this.onEditorsCreated(detailsEditor, masterEditor, newInput.details, newInput.master, options, token);
|
||||
}
|
||||
@@ -234,8 +237,13 @@ export class SideBySideEditor extends BaseEditor {
|
||||
this.masterEditor = undefined;
|
||||
}
|
||||
|
||||
this.detailsEditorContainer.innerHTML = '';
|
||||
this.masterEditorContainer.innerHTML = '';
|
||||
if (this.detailsEditorContainer) {
|
||||
DOM.clearNode(this.detailsEditorContainer);
|
||||
}
|
||||
|
||||
if (this.masterEditorContainer) {
|
||||
DOM.clearNode(this.masterEditorContainer);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -40,7 +40,7 @@ import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorAc
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
@@ -51,7 +51,7 @@ import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/
|
||||
// {{SQL CARBON EDIT}} -- End
|
||||
|
||||
interface IEditorInputLabel {
|
||||
name: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}
|
||||
@@ -60,19 +60,20 @@ type AugmentedLabel = IEditorInputLabel & { editor: IEditorInput };
|
||||
|
||||
export class TabsTitleControl extends TitleControl {
|
||||
|
||||
private titleContainer: HTMLElement;
|
||||
private tabsContainer: HTMLElement;
|
||||
private editorToolbarContainer: HTMLElement;
|
||||
private tabsScrollbar: ScrollableElement;
|
||||
private titleContainer: HTMLElement | undefined;
|
||||
private tabsContainer: HTMLElement | undefined;
|
||||
private editorToolbarContainer: HTMLElement | undefined;
|
||||
private tabsScrollbar: ScrollableElement | undefined;
|
||||
|
||||
private closeOneEditorAction: CloseOneEditorAction;
|
||||
|
||||
private tabResourceLabels: ResourceLabels;
|
||||
private tabLabels: IEditorInputLabel[] = [];
|
||||
private tabDisposables: IDisposable[] = [];
|
||||
|
||||
private dimension: Dimension;
|
||||
private dimension: Dimension | undefined;
|
||||
private readonly layoutScheduled = this._register(new MutableDisposable());
|
||||
private blockRevealActiveTab: boolean;
|
||||
private blockRevealActiveTab: boolean | undefined;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@@ -97,6 +98,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
@ILabelService labelService: ILabelService
|
||||
) {
|
||||
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService, labelService);
|
||||
|
||||
this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER));
|
||||
this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
|
||||
}
|
||||
|
||||
protected create(parent: HTMLElement): void {
|
||||
@@ -113,13 +117,13 @@ export class TabsTitleControl extends TitleControl {
|
||||
this.tabsContainer.draggable = true;
|
||||
addClass(this.tabsContainer, 'tabs-container');
|
||||
|
||||
// Tabs Container listeners
|
||||
this.registerTabsContainerListeners();
|
||||
|
||||
// Tabs Scrollbar
|
||||
this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer));
|
||||
tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode());
|
||||
|
||||
// Tabs Container listeners
|
||||
this.registerTabsContainerListeners(this.tabsContainer, this.tabsScrollbar);
|
||||
|
||||
// Editor Toolbar Container
|
||||
this.editorToolbarContainer = document.createElement('div');
|
||||
addClass(this.editorToolbarContainer, 'editor-actions');
|
||||
@@ -128,17 +132,11 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Editor Actions Toolbar
|
||||
this.createEditorActionsToolBar(this.editorToolbarContainer);
|
||||
|
||||
// Close Action
|
||||
this.closeOneEditorAction = this._register(this.instantiationService.createInstance(CloseOneEditorAction, CloseOneEditorAction.ID, CloseOneEditorAction.LABEL));
|
||||
|
||||
// Breadcrumbs (are on a separate row below tabs and actions)
|
||||
const breadcrumbsContainer = document.createElement('div');
|
||||
addClass(breadcrumbsContainer, 'tabs-breadcrumbs');
|
||||
this.titleContainer.appendChild(breadcrumbsContainer);
|
||||
this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: breadcrumbsBackground });
|
||||
|
||||
// Tab Labels
|
||||
this.tabResourceLabels = this._register(this.instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER));
|
||||
}
|
||||
|
||||
private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement {
|
||||
@@ -170,23 +168,23 @@ export class TabsTitleControl extends TitleControl {
|
||||
this.group.relayout();
|
||||
}
|
||||
|
||||
private registerTabsContainerListeners(): void {
|
||||
private registerTabsContainerListeners(tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): void {
|
||||
|
||||
// Group dragging
|
||||
this.enableGroupDragging(this.tabsContainer);
|
||||
this.enableGroupDragging(tabsContainer);
|
||||
|
||||
// Forward scrolling inside the container to our custom scrollbar
|
||||
this._register(addDisposableListener(this.tabsContainer, EventType.SCROLL, () => {
|
||||
if (hasClass(this.tabsContainer, 'scroll')) {
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: this.tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar
|
||||
this._register(addDisposableListener(tabsContainer, EventType.SCROLL, () => {
|
||||
if (hasClass(tabsContainer, 'scroll')) {
|
||||
tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
// New file when double clicking on tabs container (but not tabs)
|
||||
this._register(addDisposableListener(this.tabsContainer, EventType.DBLCLICK, e => {
|
||||
if (e.target === this.tabsContainer) {
|
||||
this._register(addDisposableListener(tabsContainer, EventType.DBLCLICK, e => {
|
||||
if (e.target === tabsContainer) {
|
||||
EventHelper.stop(e);
|
||||
// {{SQL CARBON EDIT}}
|
||||
this.commandService.executeCommand(GlobalNewUntitledFileAction.ID).then(undefined, err => this.notificationService.warn(err));
|
||||
@@ -194,29 +192,31 @@ export class TabsTitleControl extends TitleControl {
|
||||
}));
|
||||
|
||||
// Prevent auto-scrolling (https://github.com/Microsoft/vscode/issues/16690)
|
||||
this._register(addDisposableListener(this.tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
if (e.button === 1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
// Drop support
|
||||
this._register(new DragAndDropObserver(this.tabsContainer, {
|
||||
this._register(new DragAndDropObserver(tabsContainer, {
|
||||
onDragEnter: e => {
|
||||
|
||||
// Always enable support to scroll while dragging
|
||||
addClass(this.tabsContainer, 'scroll');
|
||||
addClass(tabsContainer, 'scroll');
|
||||
|
||||
// Return if the target is not on the tabs container
|
||||
if (e.target !== this.tabsContainer) {
|
||||
this.updateDropFeedback(this.tabsContainer, false); // fixes https://github.com/Microsoft/vscode/issues/52093
|
||||
if (e.target !== tabsContainer) {
|
||||
this.updateDropFeedback(tabsContainer, false); // fixes https://github.com/Microsoft/vscode/issues/52093
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if transfer is unsupported
|
||||
if (!this.isSupportedDropTransfer(e)) {
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,38 +225,46 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
isLocalDragAndDrop = true;
|
||||
|
||||
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) {
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const localDraggedEditor = data[0].identifier;
|
||||
if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) {
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the dropEffect to "copy" if there is no local data to be dragged because
|
||||
// in that case we can only copy the data into and not move it from its source
|
||||
if (!isLocalDragAndDrop) {
|
||||
e.dataTransfer!.dropEffect = 'copy';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDropFeedback(this.tabsContainer, true);
|
||||
this.updateDropFeedback(tabsContainer, true);
|
||||
},
|
||||
|
||||
onDragLeave: e => {
|
||||
this.updateDropFeedback(this.tabsContainer, false);
|
||||
removeClass(this.tabsContainer, 'scroll');
|
||||
this.updateDropFeedback(tabsContainer, false);
|
||||
removeClass(tabsContainer, 'scroll');
|
||||
},
|
||||
|
||||
onDragEnd: e => {
|
||||
this.updateDropFeedback(this.tabsContainer, false);
|
||||
removeClass(this.tabsContainer, 'scroll');
|
||||
this.updateDropFeedback(tabsContainer, false);
|
||||
removeClass(tabsContainer, 'scroll');
|
||||
},
|
||||
|
||||
onDrop: e => {
|
||||
this.updateDropFeedback(this.tabsContainer, false);
|
||||
removeClass(this.tabsContainer, 'scroll');
|
||||
this.updateDropFeedback(tabsContainer, false);
|
||||
removeClass(tabsContainer, 'scroll');
|
||||
|
||||
if (e.target === this.tabsContainer) {
|
||||
this.onDrop(e, this.group.count);
|
||||
if (e.target === tabsContainer) {
|
||||
this.onDrop(e, this.group.count, tabsContainer);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -273,8 +281,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
openEditor(editor: IEditorInput): void {
|
||||
|
||||
// Create tabs as needed
|
||||
for (let i = this.tabsContainer.children.length; i < this.group.count; i++) {
|
||||
this.tabsContainer.appendChild(this.createTab(i));
|
||||
const [tabsContainer, tabsScrollbar] = assertAllDefined(this.tabsContainer, this.tabsScrollbar);
|
||||
for (let i = tabsContainer.children.length; i < this.group.count; i++) {
|
||||
tabsContainer.appendChild(this.createTab(i, tabsContainer, tabsScrollbar));
|
||||
}
|
||||
|
||||
// An add of a tab requires to recompute all labels
|
||||
@@ -305,10 +314,11 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (this.group.activeEditor) {
|
||||
|
||||
// Remove tabs that got closed
|
||||
while (this.tabsContainer.children.length > this.group.count) {
|
||||
const tabsContainer = assertIsDefined(this.tabsContainer);
|
||||
while (tabsContainer.children.length > this.group.count) {
|
||||
|
||||
// Remove one tab from container (must be the last to keep indexes in order!)
|
||||
(this.tabsContainer.lastChild as HTMLElement).remove();
|
||||
(tabsContainer.lastChild as HTMLElement).remove();
|
||||
|
||||
// Remove associated tab label and widget
|
||||
this.tabDisposables.pop()!.dispose();
|
||||
@@ -323,7 +333,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// No tabs to show
|
||||
else {
|
||||
clearNode(this.tabsContainer);
|
||||
if (this.tabsContainer) {
|
||||
clearNode(this.tabsContainer);
|
||||
}
|
||||
|
||||
this.tabDisposables = dispose(this.tabDisposables);
|
||||
this.tabResourceLabels.clear();
|
||||
@@ -419,13 +431,14 @@ export class TabsTitleControl extends TitleControl {
|
||||
private withTab(editor: IEditorInput, fn: (tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void {
|
||||
const editorIndex = this.group.getIndexOfEditor(editor);
|
||||
|
||||
const tabContainer = this.tabsContainer.children[editorIndex] as HTMLElement;
|
||||
const tabsContainer = assertIsDefined(this.tabsContainer);
|
||||
const tabContainer = tabsContainer.children[editorIndex] as HTMLElement;
|
||||
if (tabContainer) {
|
||||
fn(tabContainer, this.tabResourceLabels.get(editorIndex), this.tabLabels[editorIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
private createTab(index: number): HTMLElement {
|
||||
private createTab(index: number, tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): HTMLElement {
|
||||
|
||||
// Tab Container
|
||||
const tabContainer = document.createElement('div');
|
||||
@@ -462,14 +475,14 @@ export class TabsTitleControl extends TitleControl {
|
||||
tabActionBar.onDidBeforeRun(() => this.blockRevealActiveTabOnce());
|
||||
|
||||
// Eventing
|
||||
const eventsDisposable = this.registerTabListeners(tabContainer, index);
|
||||
const eventsDisposable = this.registerTabListeners(tabContainer, index, tabsContainer, tabsScrollbar);
|
||||
|
||||
this.tabDisposables.push(combinedDisposable(eventsDisposable, tabActionBar, tabActionRunner, editorLabel));
|
||||
|
||||
return tabContainer;
|
||||
}
|
||||
|
||||
private registerTabListeners(tab: HTMLElement, index: number): IDisposable {
|
||||
private registerTabListeners(tab: HTMLElement, index: number, tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => {
|
||||
@@ -511,7 +524,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Touch Scroll Support
|
||||
disposables.add(addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => {
|
||||
this.tabsScrollbar.setScrollPosition({ scrollLeft: this.tabsScrollbar.getScrollPosition().scrollLeft - e.translationX });
|
||||
tabsScrollbar.setScrollPosition({ scrollLeft: tabsScrollbar.getScrollPosition().scrollLeft - e.translationX });
|
||||
}));
|
||||
|
||||
// Close on mouse middle click
|
||||
@@ -572,7 +585,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (target) {
|
||||
handled = true;
|
||||
this.group.openEditor(target, { preserveFocus: true });
|
||||
(<HTMLElement>this.tabsContainer.childNodes[targetIndex]).focus();
|
||||
(<HTMLElement>tabsContainer.childNodes[targetIndex]).focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,8 +594,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// moving in the tabs container can have an impact on scrolling position, so we need to update the custom scrollbar
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: this.tabsContainer.scrollLeft
|
||||
tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: tabsContainer.scrollLeft
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -612,7 +625,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype);
|
||||
|
||||
e.dataTransfer!.effectAllowed = 'copyMove';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
|
||||
// Apply some datatransfer types to allow for dragging the element outside of the application
|
||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
@@ -634,7 +649,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Return if transfer is unsupported
|
||||
if (!this.isSupportedDropTransfer(e)) {
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -643,17 +661,25 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
isLocalDragAndDrop = true;
|
||||
|
||||
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const localDraggedEditor = data[0].identifier;
|
||||
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the dropEffect to "copy" if there is no local data to be dragged because
|
||||
// in that case we can only copy the data into and not move it from its source
|
||||
if (!isLocalDragAndDrop) {
|
||||
e.dataTransfer!.dropEffect = 'copy';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDropFeedback(tab, true, index);
|
||||
@@ -675,7 +701,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
removeClass(tab, 'dragged-over');
|
||||
this.updateDropFeedback(tab, false, index);
|
||||
|
||||
this.onDrop(e, index);
|
||||
this.onDrop(e, index, tabsContainer);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -684,9 +710,12 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
private isSupportedDropTransfer(e: DragEvent): boolean {
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0];
|
||||
if (group.identifier === this.group.id) {
|
||||
return false; // groups cannot be dropped on title area it originates from
|
||||
const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const group = data[0];
|
||||
if (group.identifier === this.group.id) {
|
||||
return false; // groups cannot be dropped on title area it originates from
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -709,8 +738,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
const isActiveTab = isTab && !!editor && this.group.isActive(editor);
|
||||
|
||||
// Background
|
||||
const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : null;
|
||||
element.style.backgroundColor = isDND ? this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) : noDNDBackgroundColor;
|
||||
const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : '';
|
||||
element.style.backgroundColor = (isDND ? this.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND) : noDNDBackgroundColor) || '';
|
||||
|
||||
// Outline
|
||||
const activeContrastBorderColor = this.getColor(activeContrastBorder);
|
||||
@@ -745,7 +774,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Build labels and descriptions for each editor
|
||||
const labels = this.group.editors.map(editor => ({
|
||||
editor,
|
||||
name: editor.getName()!,
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription(verbosity),
|
||||
title: withNullAsUndefined(editor.getTitle(Verbosity.LONG))
|
||||
}));
|
||||
@@ -856,7 +885,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
private forEachTab(fn: (editor: IEditorInput, index: number, tabContainer: HTMLElement, tabLabelWidget: IResourceLabel, tabLabel: IEditorInputLabel) => void): void {
|
||||
this.group.editors.forEach((editor, index) => {
|
||||
const tabContainer = this.tabsContainer.children[index] as HTMLElement;
|
||||
const tabsContainer = assertIsDefined(this.tabsContainer);
|
||||
const tabContainer = tabsContainer.children[index] as HTMLElement;
|
||||
if (tabContainer) {
|
||||
fn(editor, index, tabContainer, this.tabResourceLabels.get(index), this.tabLabels[index]);
|
||||
}
|
||||
@@ -870,7 +900,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Borders / Outline
|
||||
const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder));
|
||||
tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : null;
|
||||
tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : '';
|
||||
tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || '';
|
||||
|
||||
// Settings
|
||||
@@ -929,7 +959,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Container
|
||||
addClass(tabContainer, 'active');
|
||||
tabContainer.setAttribute('aria-selected', 'true');
|
||||
tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND);
|
||||
tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND) || '';
|
||||
|
||||
const activeTabBorderColorBottom = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER);
|
||||
if (activeTabBorderColorBottom) {
|
||||
@@ -959,8 +989,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Container
|
||||
removeClass(tabContainer, 'active');
|
||||
tabContainer.setAttribute('aria-selected', 'false');
|
||||
tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND);
|
||||
tabContainer.style.boxShadow = null;
|
||||
tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND) || '';
|
||||
tabContainer.style.boxShadow = '';
|
||||
|
||||
// Label
|
||||
tabLabelWidget.element.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND);
|
||||
@@ -1013,7 +1043,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
return hasModifiedBorderColor;
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension | undefined): void {
|
||||
this.dimension = dimension;
|
||||
|
||||
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
|
||||
@@ -1026,7 +1056,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
// this a little bit we try at least to schedule this work on the next animation frame.
|
||||
if (!this.layoutScheduled.value) {
|
||||
this.layoutScheduled.value = scheduleAtNextAnimationFrame(() => {
|
||||
this.doLayout(this.dimension);
|
||||
const dimension = assertIsDefined(this.dimension);
|
||||
this.doLayout(dimension);
|
||||
|
||||
this.layoutScheduled.clear();
|
||||
});
|
||||
}
|
||||
@@ -1038,16 +1070,18 @@ export class TabsTitleControl extends TitleControl {
|
||||
return;
|
||||
}
|
||||
|
||||
const [tabsContainer, tabsScrollbar] = assertAllDefined(this.tabsContainer, this.tabsScrollbar);
|
||||
|
||||
if (this.breadcrumbsControl && !this.breadcrumbsControl.isHidden()) {
|
||||
this.breadcrumbsControl.layout({ width: dimension.width, height: BreadcrumbsControl.HEIGHT });
|
||||
this.tabsScrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`;
|
||||
tabsScrollbar.getDomNode().style.height = `${dimension.height - BreadcrumbsControl.HEIGHT}px`;
|
||||
}
|
||||
|
||||
const visibleContainerWidth = this.tabsContainer.offsetWidth;
|
||||
const totalContainerWidth = this.tabsContainer.scrollWidth;
|
||||
const visibleContainerWidth = tabsContainer.offsetWidth;
|
||||
const totalContainerWidth = tabsContainer.scrollWidth;
|
||||
|
||||
let activeTabPosX: number;
|
||||
let activeTabWidth: number;
|
||||
let activeTabPosX: number | undefined;
|
||||
let activeTabWidth: number | undefined;
|
||||
|
||||
if (!this.blockRevealActiveTab) {
|
||||
activeTabPosX = activeTab.offsetLeft;
|
||||
@@ -1055,33 +1089,33 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Update scrollbar
|
||||
this.tabsScrollbar.setScrollDimensions({
|
||||
tabsScrollbar.setScrollDimensions({
|
||||
width: visibleContainerWidth,
|
||||
scrollWidth: totalContainerWidth
|
||||
});
|
||||
|
||||
// Return now if we are blocked to reveal the active tab and clear flag
|
||||
if (this.blockRevealActiveTab) {
|
||||
if (this.blockRevealActiveTab || typeof activeTabPosX !== 'number' || typeof activeTabWidth !== 'number') {
|
||||
this.blockRevealActiveTab = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reveal the active one
|
||||
const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft;
|
||||
const activeTabFits = activeTabWidth! <= visibleContainerWidth;
|
||||
const containerScrollPosX = tabsScrollbar.getScrollPosition().scrollLeft;
|
||||
const activeTabFits = activeTabWidth <= visibleContainerWidth;
|
||||
|
||||
// Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right
|
||||
// Note: only try to do this if we actually have enough width to give to show the tab fully!
|
||||
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) {
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: containerScrollPosX + ((activeTabPosX! + activeTabWidth!) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
|
||||
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) {
|
||||
tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
|
||||
});
|
||||
}
|
||||
|
||||
// Tab is overlflowng to the left or does not fit: Scroll it into view to the left
|
||||
else if (containerScrollPosX > activeTabPosX! || !activeTabFits) {
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: activeTabPosX!
|
||||
else if (containerScrollPosX > activeTabPosX || !activeTabFits) {
|
||||
tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: activeTabPosX
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1089,7 +1123,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
private getTab(editor: IEditorInput): HTMLElement | undefined {
|
||||
const editorIndex = this.group.getIndexOfEditor(editor);
|
||||
if (editorIndex >= 0) {
|
||||
return this.tabsContainer.children[editorIndex] as HTMLElement;
|
||||
const tabsContainer = assertIsDefined(this.tabsContainer);
|
||||
|
||||
return tabsContainer.children[editorIndex] as HTMLElement;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -1116,49 +1152,55 @@ export class TabsTitleControl extends TitleControl {
|
||||
return !!findParentWithClass(element, 'action-item', 'tab');
|
||||
}
|
||||
|
||||
private onDrop(e: DragEvent, targetIndex: number): void {
|
||||
private onDrop(e: DragEvent, targetIndex: number, tabsContainer: HTMLElement): void {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
this.updateDropFeedback(this.tabsContainer, false);
|
||||
removeClass(this.tabsContainer, 'scroll');
|
||||
this.updateDropFeedback(tabsContainer, false);
|
||||
removeClass(tabsContainer, 'scroll');
|
||||
|
||||
// Local Editor DND
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
const data = this.editorTransfer.getData(DraggedEditorIdentifier.prototype);
|
||||
if (Array.isArray(data)) {
|
||||
const draggedEditor = data[0].identifier;
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
|
||||
if (sourceGroup) {
|
||||
if (sourceGroup) {
|
||||
|
||||
// Move editor to target position and index
|
||||
if (this.isMoveOperation(e, draggedEditor.groupId)) {
|
||||
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
|
||||
// Move editor to target position and index
|
||||
if (this.isMoveOperation(e, draggedEditor.groupId)) {
|
||||
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
|
||||
}
|
||||
|
||||
// Copy editor to target position and index
|
||||
else {
|
||||
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
|
||||
}
|
||||
}
|
||||
|
||||
// Copy editor to target position and index
|
||||
else {
|
||||
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
|
||||
}
|
||||
this.group.focus();
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
}
|
||||
|
||||
this.group.focus();
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
}
|
||||
|
||||
// Local Editor Group DND
|
||||
else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
|
||||
const data = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype);
|
||||
if (data) {
|
||||
const sourceGroup = this.accessor.getGroup(data[0].identifier);
|
||||
|
||||
if (sourceGroup) {
|
||||
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
|
||||
if (!this.isMoveOperation(e, sourceGroup.id)) {
|
||||
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
|
||||
if (sourceGroup) {
|
||||
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
|
||||
if (!this.isMoveOperation(e, sourceGroup.id)) {
|
||||
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
|
||||
}
|
||||
|
||||
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
|
||||
}
|
||||
|
||||
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
|
||||
this.group.focus();
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}
|
||||
|
||||
this.group.focus();
|
||||
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
|
||||
}
|
||||
|
||||
// External DND
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { isFunction, isObject, isArray } from 'vs/base/common/types';
|
||||
import { isFunction, isObject, isArray, assertIsDefined } from 'vs/base/common/types';
|
||||
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
@@ -110,7 +110,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
}
|
||||
|
||||
// Set Editor Model
|
||||
const diffEditor = this.getControl();
|
||||
const diffEditor = assertIsDefined(this.getControl());
|
||||
const resolvedDiffEditorModel = <TextDiffEditorModel>resolvedModel;
|
||||
diffEditor.setModel(resolvedDiffEditorModel.textDiffEditorModel);
|
||||
|
||||
@@ -123,7 +123,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
// Otherwise restore View State
|
||||
let hasPreviousViewState = false;
|
||||
if (!optionsGotApplied) {
|
||||
hasPreviousViewState = this.restoreTextDiffEditorViewState(input);
|
||||
hasPreviousViewState = this.restoreTextDiffEditorViewState(input, diffEditor);
|
||||
}
|
||||
|
||||
// Diff navigator
|
||||
@@ -148,17 +148,18 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
setOptions(options: EditorOptions | undefined): void {
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && isFunction(textOptions.apply)) {
|
||||
textOptions.apply(this.getControl(), ScrollType.Smooth);
|
||||
const diffEditor = assertIsDefined(this.getControl());
|
||||
textOptions.apply(diffEditor, ScrollType.Smooth);
|
||||
}
|
||||
}
|
||||
|
||||
private restoreTextDiffEditorViewState(input: EditorInput): boolean {
|
||||
if (input instanceof DiffEditorInput) {
|
||||
const resource = this.toDiffEditorViewStateResource(input);
|
||||
private restoreTextDiffEditorViewState(editor: EditorInput, control: IDiffEditor): boolean {
|
||||
if (editor instanceof DiffEditorInput) {
|
||||
const resource = this.toDiffEditorViewStateResource(editor);
|
||||
if (resource) {
|
||||
const viewState = this.loadTextEditorViewState(resource);
|
||||
if (viewState) {
|
||||
this.getControl().restoreViewState(viewState);
|
||||
control.restoreViewState(viewState);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -268,7 +269,10 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
this.saveTextDiffEditorViewState(this.input);
|
||||
|
||||
// Clear Model
|
||||
this.getControl().setModel(null);
|
||||
const diffEditor = this.getControl();
|
||||
if (diffEditor) {
|
||||
diffEditor.setModel(null);
|
||||
}
|
||||
|
||||
// Pass to super
|
||||
super.clearInput();
|
||||
@@ -278,8 +282,8 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return this.diffNavigator;
|
||||
}
|
||||
|
||||
getControl(): IDiffEditor {
|
||||
return super.getControl() as IDiffEditor;
|
||||
getControl(): IDiffEditor | undefined {
|
||||
return super.getControl() as IDiffEditor | undefined;
|
||||
}
|
||||
|
||||
protected loadTextEditorViewState(resource: URI): IDiffEditorViewState {
|
||||
@@ -317,7 +321,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
}
|
||||
|
||||
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null {
|
||||
const control = this.getControl();
|
||||
const control = assertIsDefined(this.getControl());
|
||||
const model = control.getModel();
|
||||
if (!model || !model.modified || !model.original) {
|
||||
return null; // view state always needs a model
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { distinct, deepClone, assign } from 'vs/base/common/objects';
|
||||
import { isObject, assertIsDefined } from 'vs/base/common/types';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorInput, EditorOptions, IEditorMemento, ITextEditor } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
@@ -37,8 +37,8 @@ export interface IEditorConfiguration {
|
||||
* be subclassed and not instantiated.
|
||||
*/
|
||||
export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
private editorControl: IEditor;
|
||||
private _editorContainer: HTMLElement;
|
||||
private editorControl: IEditor | undefined;
|
||||
private _editorContainer: HTMLElement | undefined;
|
||||
private hasPendingConfigurationChange: boolean | undefined;
|
||||
private lastAppliedEditorOptions?: IEditorOptions;
|
||||
private editorMemento: IEditorMemento<IEditorViewState>;
|
||||
@@ -96,8 +96,8 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
protected computeConfiguration(configuration: IEditorConfiguration): IEditorOptions {
|
||||
|
||||
// Specific editor options always overwrite user configuration
|
||||
const editorConfiguration: IEditorOptions = types.isObject(configuration.editor) ? objects.deepClone(configuration.editor) : Object.create(null);
|
||||
objects.assign(editorConfiguration, this.getConfigurationOverrides());
|
||||
const editorConfiguration: IEditorOptions = isObject(configuration.editor) ? deepClone(configuration.editor) : Object.create(null);
|
||||
assign(editorConfiguration, this.getConfigurationOverrides());
|
||||
|
||||
// ARIA label
|
||||
editorConfiguration.ariaLabel = this.computeAriaLabel();
|
||||
@@ -111,7 +111,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
// Apply group information to help identify in which group we are
|
||||
if (ariaLabel) {
|
||||
if (this.group) {
|
||||
ariaLabel = nls.localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label);
|
||||
ariaLabel = localize('editorLabelWithGroup', "{0}, {1}.", ariaLabel, this.group.label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
|
||||
protected getConfigurationOverrides(): IEditorOptions {
|
||||
const overrides = {};
|
||||
objects.assign(overrides, {
|
||||
assign(overrides, {
|
||||
overviewRulerLanes: 3,
|
||||
lineNumbersMinChars: 3,
|
||||
fixedOverflowWidgets: true
|
||||
@@ -133,7 +133,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
|
||||
// Editor for Text
|
||||
this._editorContainer = parent;
|
||||
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource()!))));
|
||||
this.editorControl = this._register(this.createEditorControl(parent, this.computeConfiguration(this.configurationService.getValue<IEditorConfiguration>(this.getResource()))));
|
||||
|
||||
// Model & Language changes
|
||||
const codeEditor = getCodeEditor(this.editorControl);
|
||||
@@ -197,33 +197,40 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
// Update editor options after having set the input. We do this because there can be
|
||||
// editor input specific options (e.g. an ARIA label depending on the input showing)
|
||||
this.updateEditorConfiguration();
|
||||
this._editorContainer.setAttribute('aria-label', this.computeAriaLabel());
|
||||
|
||||
const editorContainer = assertIsDefined(this._editorContainer);
|
||||
editorContainer.setAttribute('aria-label', this.computeAriaLabel());
|
||||
}
|
||||
|
||||
protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
|
||||
|
||||
// Pass on to Editor
|
||||
const editorControl = assertIsDefined(this.editorControl);
|
||||
if (visible) {
|
||||
this.consumePendingConfigurationChangeEvent();
|
||||
this.editorControl.onVisible();
|
||||
editorControl.onVisible();
|
||||
} else {
|
||||
this.editorControl.onHide();
|
||||
editorControl.onHide();
|
||||
}
|
||||
|
||||
super.setEditorVisible(visible, group);
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.editorControl.focus();
|
||||
}
|
||||
|
||||
layout(dimension: DOM.Dimension): void {
|
||||
|
||||
// Pass on to Editor
|
||||
this.editorControl.layout(dimension);
|
||||
const editorControl = assertIsDefined(this.editorControl);
|
||||
editorControl.focus();
|
||||
}
|
||||
|
||||
getControl(): IEditor {
|
||||
layout(dimension: Dimension): void {
|
||||
|
||||
// Pass on to Editor
|
||||
const editorControl = assertIsDefined(this.editorControl);
|
||||
editorControl.layout(dimension);
|
||||
}
|
||||
|
||||
getControl(): IEditor | undefined {
|
||||
return this.editorControl;
|
||||
}
|
||||
|
||||
@@ -296,7 +303,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
|
||||
// have been applied to the editor directly.
|
||||
let editorSettingsToApply = editorConfiguration;
|
||||
if (this.lastAppliedEditorOptions) {
|
||||
editorSettingsToApply = objects.distinct(this.lastAppliedEditorOptions, editorSettingsToApply);
|
||||
editorSettingsToApply = distinct(this.lastAppliedEditorOptions, editorSettingsToApply);
|
||||
}
|
||||
|
||||
if (Object.keys(editorSettingsToApply).length > 0) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { assertIsDefined, isFunction } from 'vs/base/common/types';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
@@ -19,7 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { ScrollType, IEditor } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -74,36 +74,37 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
}
|
||||
|
||||
// Set Editor Model
|
||||
const textEditor = this.getControl();
|
||||
const textEditor = assertIsDefined(this.getControl());
|
||||
const textEditorModel = resolvedModel.textEditorModel;
|
||||
textEditor.setModel(textEditorModel);
|
||||
|
||||
// Apply Options from TextOptions
|
||||
let optionsGotApplied = false;
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
if (textOptions && isFunction(textOptions.apply)) {
|
||||
optionsGotApplied = textOptions.apply(textEditor, ScrollType.Immediate);
|
||||
}
|
||||
|
||||
// Otherwise restore View State
|
||||
if (!optionsGotApplied) {
|
||||
this.restoreTextResourceEditorViewState(input);
|
||||
this.restoreTextResourceEditorViewState(input, textEditor);
|
||||
}
|
||||
}
|
||||
|
||||
private restoreTextResourceEditorViewState(input: EditorInput) {
|
||||
if (input instanceof UntitledEditorInput || input instanceof ResourceEditorInput) {
|
||||
const viewState = this.loadTextEditorViewState(input.getResource());
|
||||
private restoreTextResourceEditorViewState(editor: EditorInput, control: IEditor) {
|
||||
if (editor instanceof UntitledEditorInput || editor instanceof ResourceEditorInput) {
|
||||
const viewState = this.loadTextEditorViewState(editor.getResource());
|
||||
if (viewState) {
|
||||
this.getControl().restoreViewState(viewState);
|
||||
control.restoreViewState(viewState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(options: EditorOptions | undefined): void {
|
||||
const textOptions = <TextEditorOptions>options;
|
||||
if (textOptions && types.isFunction(textOptions.apply)) {
|
||||
textOptions.apply(this.getControl(), ScrollType.Smooth);
|
||||
if (textOptions && isFunction(textOptions.apply)) {
|
||||
const textEditor = assertIsDefined(this.getControl());
|
||||
textOptions.apply(textEditor, ScrollType.Smooth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +121,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
const isReadonly = !(this.input instanceof UntitledEditorInput);
|
||||
|
||||
let ariaLabel: string;
|
||||
const inputName = input && input.getName();
|
||||
const inputName = input?.getName();
|
||||
if (isReadonly) {
|
||||
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text editor.");
|
||||
} else {
|
||||
@@ -149,7 +150,10 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
this.saveTextResourceEditorViewState(this.input);
|
||||
|
||||
// Clear Model
|
||||
this.getControl().setModel(null);
|
||||
const textEditor = this.getControl();
|
||||
if (textEditor) {
|
||||
textEditor.setModel(null);
|
||||
}
|
||||
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import { Themable } from 'vs/workbench/common/theme';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { withNullAsUndefined, withUndefinedAsNull, assertIsDefined } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
export interface IToolbarActions {
|
||||
@@ -57,7 +57,7 @@ export abstract class TitleControl extends Themable {
|
||||
private currentPrimaryEditorActionIds: string[] = [];
|
||||
private currentSecondaryEditorActionIds: string[] = [];
|
||||
|
||||
private editorActionsToolbar: ToolBar;
|
||||
private editorActionsToolbar: ToolBar | undefined;
|
||||
|
||||
private resourceContext: ResourceContextKey;
|
||||
private editorPinnedContext: IContextKey<boolean>;
|
||||
@@ -189,7 +189,8 @@ export abstract class TitleControl extends Themable {
|
||||
primaryEditorActions.some(action => action instanceof ExecuteCommandAction) || // execute command actions can have the same ID but different arguments
|
||||
secondaryEditorActions.some(action => action instanceof ExecuteCommandAction) // see also https://github.com/Microsoft/vscode/issues/16298
|
||||
) {
|
||||
this.editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
|
||||
const editorActionsToolbar = assertIsDefined(this.editorActionsToolbar);
|
||||
editorActionsToolbar.setActions(primaryEditorActions, secondaryEditorActions)();
|
||||
|
||||
this.currentPrimaryEditorActionIds = primaryEditorActionIds;
|
||||
this.currentSecondaryEditorActionIds = secondaryEditorActionIds;
|
||||
@@ -228,7 +229,7 @@ export abstract class TitleControl extends Themable {
|
||||
const activeControl = this.group.activeControl;
|
||||
if (activeControl instanceof BaseEditor) {
|
||||
const codeEditor = getCodeEditor(activeControl.getControl());
|
||||
const scopedContextKeyService = codeEditor && codeEditor.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
|
||||
const scopedContextKeyService = codeEditor?.invokeWithinContext(accessor => accessor.get(IContextKeyService)) || this.contextKeyService;
|
||||
const titleBarMenu = this.menuService.createMenu(MenuId.EditorTitle, scopedContextKeyService);
|
||||
this.editorToolBarMenuDisposables.add(titleBarMenu);
|
||||
this.editorToolBarMenuDisposables.add(titleBarMenu.onDidChange(() => {
|
||||
@@ -242,7 +243,9 @@ export abstract class TitleControl extends Themable {
|
||||
}
|
||||
|
||||
protected clearEditorActionsToolbar(): void {
|
||||
this.editorActionsToolbar.setActions([], [])();
|
||||
if (this.editorActionsToolbar) {
|
||||
this.editorActionsToolbar.setActions([], [])();
|
||||
}
|
||||
|
||||
this.currentPrimaryEditorActionIds = [];
|
||||
this.currentSecondaryEditorActionIds = [];
|
||||
@@ -258,7 +261,9 @@ export abstract class TitleControl extends Themable {
|
||||
|
||||
// Set editor group as transfer
|
||||
this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype);
|
||||
e.dataTransfer!.effectAllowed = 'copyMove';
|
||||
if (e.dataTransfer) {
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
}
|
||||
|
||||
// If tabs are disabled, treat dragging as if an editor tab was dragged
|
||||
if (!this.accessor.partOptions.showTabs) {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
height: 22px;
|
||||
margin-right: 4px;
|
||||
margin-left: 4px;
|
||||
font-size: 18px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@@ -70,8 +71,7 @@
|
||||
}
|
||||
|
||||
.monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container,
|
||||
.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container,
|
||||
.monaco-workbench .notifications-list-container .notification-list-item.expanded .notification-list-item-toolbar-container {
|
||||
.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export class ClearAllNotificationsAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, 'codicon-close-all');
|
||||
super(id, label, 'codicon-clear-all');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
@@ -63,7 +63,7 @@ export class HideNotificationsCenterAction extends Action {
|
||||
label: string,
|
||||
@ICommandService private readonly commandService: ICommandService
|
||||
) {
|
||||
super(id, label, 'codicon-chevron-down');
|
||||
super(id, label, 'codicon-close');
|
||||
}
|
||||
|
||||
run(notification: INotificationViewItem): Promise<any> {
|
||||
|
||||
@@ -22,21 +22,23 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { assertAllDefined, assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export class NotificationsCenter extends Themable {
|
||||
|
||||
private static MAX_DIMENSIONS = new Dimension(450, 400);
|
||||
private static readonly MAX_DIMENSIONS = new Dimension(450, 400);
|
||||
|
||||
private readonly _onDidChangeVisibility: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeVisibility: Event<void> = this._onDidChangeVisibility.event;
|
||||
|
||||
private notificationsCenterContainer: HTMLElement;
|
||||
private notificationsCenterHeader: HTMLElement;
|
||||
private notificationsCenterTitle: HTMLSpanElement;
|
||||
private notificationsList: NotificationsList;
|
||||
private _isVisible: boolean;
|
||||
private workbenchDimensions: Dimension;
|
||||
private notificationsCenterContainer: HTMLElement | undefined;
|
||||
private notificationsCenterHeader: HTMLElement | undefined;
|
||||
private notificationsCenterTitle: HTMLSpanElement | undefined;
|
||||
private notificationsList: NotificationsList | undefined;
|
||||
private _isVisible: boolean | undefined;
|
||||
private workbenchDimensions: Dimension | undefined;
|
||||
private notificationsCenterVisibleContextKey: IContextKey<boolean>;
|
||||
private clearAllAction: ClearAllNotificationsAction | undefined;
|
||||
|
||||
constructor(
|
||||
private container: HTMLElement,
|
||||
@@ -61,12 +63,13 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
get isVisible(): boolean {
|
||||
return this._isVisible;
|
||||
return !!this._isVisible;
|
||||
}
|
||||
|
||||
show(): void {
|
||||
if (this._isVisible) {
|
||||
this.notificationsList.show(true /* focus */);
|
||||
const notificationsList = assertIsDefined(this.notificationsList);
|
||||
notificationsList.show(true /* focus */);
|
||||
|
||||
return; // already visible
|
||||
}
|
||||
@@ -80,18 +83,19 @@ export class NotificationsCenter extends Themable {
|
||||
this.updateTitle();
|
||||
|
||||
// Make visible
|
||||
const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer);
|
||||
this._isVisible = true;
|
||||
addClass(this.notificationsCenterContainer, 'visible');
|
||||
this.notificationsList.show();
|
||||
addClass(notificationsCenterContainer, 'visible');
|
||||
notificationsList.show();
|
||||
|
||||
// Layout
|
||||
this.layout(this.workbenchDimensions);
|
||||
|
||||
// Show all notifications that are present now
|
||||
this.notificationsList.updateNotificationsList(0, 0, this.model.notifications);
|
||||
notificationsList.updateNotificationsList(0, 0, this.model.notifications);
|
||||
|
||||
// Focus first
|
||||
this.notificationsList.focusFirst();
|
||||
notificationsList.focusFirst();
|
||||
|
||||
// Theming
|
||||
this.updateStyles();
|
||||
@@ -104,10 +108,14 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
private updateTitle(): void {
|
||||
const [notificationsCenterTitle, clearAllAction] = assertAllDefined(this.notificationsCenterTitle, this.clearAllAction);
|
||||
|
||||
if (this.model.notifications.length === 0) {
|
||||
this.notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications");
|
||||
notificationsCenterTitle.textContent = localize('notificationsEmpty', "No new notifications");
|
||||
clearAllAction.enabled = false;
|
||||
} else {
|
||||
this.notificationsCenterTitle.textContent = localize('notifications', "Notifications");
|
||||
notificationsCenterTitle.textContent = localize('notifications', "Notifications");
|
||||
clearAllAction.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,12 +147,12 @@ export class NotificationsCenter extends Themable {
|
||||
actionRunner
|
||||
}));
|
||||
|
||||
this.clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL));
|
||||
notificationsToolBar.push(this.clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(this.clearAllAction) });
|
||||
|
||||
const hideAllAction = this._register(this.instantiationService.createInstance(HideNotificationsCenterAction, HideNotificationsCenterAction.ID, HideNotificationsCenterAction.LABEL));
|
||||
notificationsToolBar.push(hideAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(hideAllAction) });
|
||||
|
||||
const clearAllAction = this._register(this.instantiationService.createInstance(ClearAllNotificationsAction, ClearAllNotificationsAction.ID, ClearAllNotificationsAction.LABEL));
|
||||
notificationsToolBar.push(clearAllAction, { icon: true, label: false, keybinding: this.getKeybindingLabel(clearAllAction) });
|
||||
|
||||
// Notifications List
|
||||
this.notificationsList = this.instantiationService.createInstance(NotificationsList, this.notificationsCenterContainer, {
|
||||
ariaLabel: localize('notificationsList', "Notifications List")
|
||||
@@ -167,16 +175,17 @@ export class NotificationsCenter extends Themable {
|
||||
let focusGroup = false;
|
||||
|
||||
// Update notifications list based on event
|
||||
const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer);
|
||||
switch (e.kind) {
|
||||
case NotificationChangeType.ADD:
|
||||
this.notificationsList.updateNotificationsList(e.index, 0, [e.item]);
|
||||
notificationsList.updateNotificationsList(e.index, 0, [e.item]);
|
||||
break;
|
||||
case NotificationChangeType.CHANGE:
|
||||
this.notificationsList.updateNotificationsList(e.index, 1, [e.item]);
|
||||
notificationsList.updateNotificationsList(e.index, 1, [e.item]);
|
||||
break;
|
||||
case NotificationChangeType.REMOVE:
|
||||
focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer);
|
||||
this.notificationsList.updateNotificationsList(e.index, 1);
|
||||
focusGroup = isAncestor(document.activeElement, notificationsCenterContainer);
|
||||
notificationsList.updateNotificationsList(e.index, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -195,7 +204,7 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
if (!this._isVisible || !this.notificationsCenterContainer) {
|
||||
if (!this._isVisible || !this.notificationsCenterContainer || !this.notificationsList) {
|
||||
return; // already hidden
|
||||
}
|
||||
|
||||
@@ -219,22 +228,22 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
if (this.notificationsCenterContainer) {
|
||||
if (this.notificationsCenterContainer && this.notificationsCenterHeader) {
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
|
||||
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
|
||||
|
||||
const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER);
|
||||
this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : null;
|
||||
this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : '';
|
||||
|
||||
const headerForeground = this.getColor(NOTIFICATIONS_CENTER_HEADER_FOREGROUND);
|
||||
this.notificationsCenterHeader.style.color = headerForeground ? headerForeground.toString() : null;
|
||||
|
||||
const headerBackground = this.getColor(NOTIFICATIONS_CENTER_HEADER_BACKGROUND);
|
||||
this.notificationsCenterHeader.style.background = headerBackground ? headerBackground.toString() : null;
|
||||
this.notificationsCenterHeader.style.background = headerBackground ? headerBackground.toString() : '';
|
||||
}
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension | undefined): void {
|
||||
this.workbenchDimensions = dimension;
|
||||
|
||||
if (this._isVisible && this.notificationsCenterContainer) {
|
||||
@@ -264,7 +273,8 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
// Apply to list
|
||||
this.notificationsList.layout(Math.min(maxWidth, availableWidth), Math.min(maxHeight, availableHeight));
|
||||
const notificationsList = assertIsDefined(this.notificationsList);
|
||||
notificationsList.layout(Math.min(maxWidth, availableWidth), Math.min(maxHeight, availableHeight));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ import { NotificationsListDelegate, NotificationRenderer } from 'vs/workbench/br
|
||||
import { NotificationActionRunner, CopyNotificationMessageAction } from 'vs/workbench/browser/parts/notifications/notificationsActions';
|
||||
import { NotificationFocusedContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||
|
||||
export class NotificationsList extends Themable {
|
||||
private listContainer: HTMLElement;
|
||||
private list: WorkbenchList<INotificationViewItem>;
|
||||
private listContainer: HTMLElement | undefined;
|
||||
private list: WorkbenchList<INotificationViewItem> | undefined;
|
||||
private viewModel: INotificationViewItem[];
|
||||
private isVisible: boolean | undefined;
|
||||
|
||||
@@ -38,7 +39,8 @@ export class NotificationsList extends Themable {
|
||||
show(focus?: boolean): void {
|
||||
if (this.isVisible) {
|
||||
if (focus) {
|
||||
this.list.domFocus();
|
||||
const list = assertIsDefined(this.list);
|
||||
list.domFocus();
|
||||
}
|
||||
|
||||
return; // already visible
|
||||
@@ -54,7 +56,8 @@ export class NotificationsList extends Themable {
|
||||
|
||||
// Focus
|
||||
if (focus) {
|
||||
this.list.domFocus();
|
||||
const list = assertIsDefined(this.list);
|
||||
list.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +73,7 @@ export class NotificationsList extends Themable {
|
||||
const renderer = this.instantiationService.createInstance(NotificationRenderer, actionRunner);
|
||||
|
||||
// List
|
||||
this.list = this._register(this.instantiationService.createInstance(
|
||||
const list = this.list = this._register(this.instantiationService.createInstance(
|
||||
WorkbenchList,
|
||||
'NotificationsList',
|
||||
this.listContainer,
|
||||
@@ -85,13 +88,13 @@ export class NotificationsList extends Themable {
|
||||
|
||||
// Context menu to copy message
|
||||
const copyAction = this._register(this.instantiationService.createInstance(CopyNotificationMessageAction, CopyNotificationMessageAction.ID, CopyNotificationMessageAction.LABEL));
|
||||
this._register((this.list.onContextMenu(e => {
|
||||
this._register((list.onContextMenu(e => {
|
||||
if (!e.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor!,
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => [copyAction],
|
||||
getActionsContext: () => e.element,
|
||||
actionRunner
|
||||
@@ -99,27 +102,27 @@ export class NotificationsList extends Themable {
|
||||
})));
|
||||
|
||||
// Toggle on double click
|
||||
this._register((this.list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle())));
|
||||
this._register((list.onMouseDblClick(event => (event.element as INotificationViewItem).toggle())));
|
||||
|
||||
// Clear focus when DOM focus moves out
|
||||
// Use document.hasFocus() to not clear the focus when the entire window lost focus
|
||||
// This ensures that when the focus comes back, the notification is still focused
|
||||
const listFocusTracker = this._register(trackFocus(this.list.getHTMLElement()));
|
||||
const listFocusTracker = this._register(trackFocus(list.getHTMLElement()));
|
||||
this._register(listFocusTracker.onDidBlur(() => {
|
||||
if (document.hasFocus()) {
|
||||
this.list.setFocus([]);
|
||||
list.setFocus([]);
|
||||
}
|
||||
}));
|
||||
|
||||
// Context key
|
||||
NotificationFocusedContext.bindTo(this.list.contextKeyService);
|
||||
NotificationFocusedContext.bindTo(list.contextKeyService);
|
||||
|
||||
// Only allow for focus in notifications, as the
|
||||
// selection is too strong over the contents of
|
||||
// the notification
|
||||
this._register(this.list.onSelectionChange(e => {
|
||||
this._register(list.onSelectionChange(e => {
|
||||
if (e.indexes.length > 0) {
|
||||
this.list.setSelection([]);
|
||||
list.setSelection([]);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -129,23 +132,24 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
|
||||
updateNotificationsList(start: number, deleteCount: number, items: INotificationViewItem[] = []) {
|
||||
const listHasDOMFocus = isAncestor(document.activeElement, this.listContainer);
|
||||
const [list, listContainer] = assertAllDefined(this.list, this.listContainer);
|
||||
const listHasDOMFocus = isAncestor(document.activeElement, listContainer);
|
||||
|
||||
// Remember focus and relative top of that item
|
||||
const focusedIndex = this.list.getFocus()[0];
|
||||
const focusedIndex = list.getFocus()[0];
|
||||
const focusedItem = this.viewModel[focusedIndex];
|
||||
|
||||
let focusRelativeTop: number | null = null;
|
||||
if (typeof focusedIndex === 'number') {
|
||||
focusRelativeTop = this.list.getRelativeTop(focusedIndex);
|
||||
focusRelativeTop = list.getRelativeTop(focusedIndex);
|
||||
}
|
||||
|
||||
// Update view model
|
||||
this.viewModel.splice(start, deleteCount, ...items);
|
||||
|
||||
// Update list
|
||||
this.list.splice(start, deleteCount, items);
|
||||
this.list.layout();
|
||||
list.splice(start, deleteCount, items);
|
||||
list.layout();
|
||||
|
||||
// Hide if no more notifications to show
|
||||
if (this.viewModel.length === 0) {
|
||||
@@ -167,15 +171,15 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
|
||||
if (typeof focusRelativeTop === 'number') {
|
||||
this.list.reveal(indexToFocus, focusRelativeTop);
|
||||
list.reveal(indexToFocus, focusRelativeTop);
|
||||
}
|
||||
|
||||
this.list.setFocus([indexToFocus]);
|
||||
list.setFocus([indexToFocus]);
|
||||
}
|
||||
|
||||
// Restore DOM focus if we had focus before
|
||||
if (listHasDOMFocus) {
|
||||
this.list.domFocus();
|
||||
list.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +208,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
|
||||
hasFocus(): boolean {
|
||||
if (!this.isVisible || !this.list) {
|
||||
if (!this.isVisible || !this.listContainer) {
|
||||
return false; // hidden
|
||||
}
|
||||
|
||||
@@ -217,7 +221,7 @@ export class NotificationsList extends Themable {
|
||||
this.listContainer.style.color = foreground ? foreground.toString() : null;
|
||||
|
||||
const background = this.getColor(NOTIFICATIONS_BACKGROUND);
|
||||
this.listContainer.style.background = background ? background.toString() : null;
|
||||
this.listContainer.style.background = background ? background.toString() : '';
|
||||
|
||||
const outlineColor = this.getColor(contrastBorder);
|
||||
this.listContainer.style.outlineColor = outlineColor ? outlineColor.toString() : '';
|
||||
@@ -225,7 +229,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
|
||||
layout(width: number, maxHeight?: number): void {
|
||||
if (this.list) {
|
||||
if (this.listContainer && this.list) {
|
||||
this.listContainer.style.width = `${width}px`;
|
||||
|
||||
if (typeof maxHeight === 'number') {
|
||||
|
||||
@@ -18,11 +18,12 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
|
||||
import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Severity, NotificationsFilter } from 'vs/platform/notification/common/notification';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
interface INotificationToast {
|
||||
item: INotificationViewItem;
|
||||
@@ -40,8 +41,8 @@ enum ToastVisibility {
|
||||
|
||||
export class NotificationsToasts extends Themable {
|
||||
|
||||
private static MAX_WIDTH = 450;
|
||||
private static MAX_NOTIFICATIONS = 3;
|
||||
private static readonly MAX_WIDTH = 450;
|
||||
private static readonly MAX_NOTIFICATIONS = 3;
|
||||
|
||||
private static PURGE_TIMEOUT: { [severity: number]: number } = (() => {
|
||||
const intervals = Object.create(null);
|
||||
@@ -52,8 +53,8 @@ export class NotificationsToasts extends Themable {
|
||||
return intervals;
|
||||
})();
|
||||
|
||||
private notificationsToastsContainer: HTMLElement;
|
||||
private workbenchDimensions: Dimension;
|
||||
private notificationsToastsContainer: HTMLElement | undefined;
|
||||
private workbenchDimensions: Dimension | undefined;
|
||||
private isNotificationsCenterVisible: boolean | undefined;
|
||||
private mapNotificationToToast: Map<INotificationViewItem, INotificationToast>;
|
||||
private notificationsToastsVisibleContextKey: IContextKey<boolean>;
|
||||
@@ -91,6 +92,13 @@ export class NotificationsToasts extends Themable {
|
||||
// Update toasts on notification changes
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
});
|
||||
|
||||
// Filter
|
||||
this._register(this.model.onDidFilterChange(filter => {
|
||||
if (filter === NotificationsFilter.SILENT || filter === NotificationsFilter.ERROR) {
|
||||
this.hide();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async onCanShowNotifications(): Promise<void> {
|
||||
@@ -125,15 +133,16 @@ export class NotificationsToasts extends Themable {
|
||||
}
|
||||
|
||||
// Lazily create toasts containers
|
||||
if (!this.notificationsToastsContainer) {
|
||||
this.notificationsToastsContainer = document.createElement('div');
|
||||
addClass(this.notificationsToastsContainer, 'notifications-toasts');
|
||||
let notificationsToastsContainer = this.notificationsToastsContainer;
|
||||
if (!notificationsToastsContainer) {
|
||||
notificationsToastsContainer = this.notificationsToastsContainer = document.createElement('div');
|
||||
addClass(notificationsToastsContainer, 'notifications-toasts');
|
||||
|
||||
this.container.appendChild(this.notificationsToastsContainer);
|
||||
this.container.appendChild(notificationsToastsContainer);
|
||||
}
|
||||
|
||||
// Make Visible
|
||||
addClass(this.notificationsToastsContainer, 'visible');
|
||||
addClass(notificationsToastsContainer, 'visible');
|
||||
|
||||
const itemDisposables = new DisposableStore();
|
||||
|
||||
@@ -141,11 +150,11 @@ export class NotificationsToasts extends Themable {
|
||||
const notificationToastContainer = document.createElement('div');
|
||||
addClass(notificationToastContainer, 'notification-toast-container');
|
||||
|
||||
const firstToast = this.notificationsToastsContainer.firstChild;
|
||||
const firstToast = notificationsToastsContainer.firstChild;
|
||||
if (firstToast) {
|
||||
this.notificationsToastsContainer.insertBefore(notificationToastContainer, firstToast); // always first
|
||||
notificationsToastsContainer.insertBefore(notificationToastContainer, firstToast); // always first
|
||||
} else {
|
||||
this.notificationsToastsContainer.appendChild(notificationToastContainer);
|
||||
notificationsToastsContainer.appendChild(notificationToastContainer);
|
||||
}
|
||||
|
||||
// Toast
|
||||
@@ -155,6 +164,7 @@ export class NotificationsToasts extends Themable {
|
||||
|
||||
// Create toast with item and show
|
||||
const notificationList = this.instantiationService.createInstance(NotificationsList, notificationToast, {
|
||||
ariaRole: 'dialog', // https://github.com/microsoft/vscode/issues/82728
|
||||
ariaLabel: localize('notificationsToast', "Notification Toast"),
|
||||
verticalScrollMode: ScrollbarVisibility.Hidden
|
||||
});
|
||||
@@ -164,8 +174,8 @@ export class NotificationsToasts extends Themable {
|
||||
this.mapNotificationToToast.set(item, toast);
|
||||
|
||||
itemDisposables.add(toDisposable(() => {
|
||||
if (this.isVisible(toast)) {
|
||||
this.notificationsToastsContainer.removeChild(toast.container);
|
||||
if (this.isVisible(toast) && notificationsToastsContainer) {
|
||||
notificationsToastsContainer.removeChild(toast.container);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -318,7 +328,7 @@ export class NotificationsToasts extends Themable {
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
const focusGroup = isAncestor(document.activeElement, this.notificationsToastsContainer);
|
||||
const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
|
||||
|
||||
this.removeToasts();
|
||||
|
||||
@@ -412,10 +422,10 @@ export class NotificationsToasts extends Themable {
|
||||
protected updateStyles(): void {
|
||||
this.mapNotificationToToast.forEach(t => {
|
||||
const widgetShadowColor = this.getColor(widgetShadow);
|
||||
t.toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
|
||||
t.toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
|
||||
|
||||
const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER);
|
||||
t.toast.style.border = borderColor ? `1px solid ${borderColor}` : null;
|
||||
t.toast.style.border = borderColor ? `1px solid ${borderColor}` : '';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -443,7 +453,7 @@ export class NotificationsToasts extends Themable {
|
||||
return notificationToasts.reverse(); // from newest to oldest
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
layout(dimension: Dimension | undefined): void {
|
||||
this.workbenchDimensions = dimension;
|
||||
|
||||
const maxDimensions = this.computeMaxDimensions();
|
||||
@@ -525,10 +535,11 @@ export class NotificationsToasts extends Themable {
|
||||
return;
|
||||
}
|
||||
|
||||
const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer);
|
||||
if (visible) {
|
||||
this.notificationsToastsContainer.appendChild(toast.container);
|
||||
notificationsToastsContainer.appendChild(toast.container);
|
||||
} else {
|
||||
this.notificationsToastsContainer.removeChild(toast.container);
|
||||
notificationsToastsContainer.removeChild(toast.container);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/pa
|
||||
export class ClosePanelAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.closePanel';
|
||||
static LABEL = nls.localize('closePanel', "Close Panel");
|
||||
static readonly LABEL = nls.localize('closePanel', "Close Panel");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -40,7 +40,7 @@ export class ClosePanelAction extends Action {
|
||||
export class TogglePanelAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.togglePanel';
|
||||
static LABEL = nls.localize('togglePanel', "Toggle Panel");
|
||||
static readonly LABEL = nls.localize('togglePanel', "Toggle Panel");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -209,7 +209,7 @@ export class SwitchPanelViewAction extends Action {
|
||||
export class PreviousPanelViewAction extends SwitchPanelViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.previousPanelView';
|
||||
static LABEL = nls.localize('previousPanelView', 'Previous Panel View');
|
||||
static readonly LABEL = nls.localize('previousPanelView', 'Previous Panel View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@@ -227,7 +227,7 @@ export class PreviousPanelViewAction extends SwitchPanelViewAction {
|
||||
export class NextPanelViewAction extends SwitchPanelViewAction {
|
||||
|
||||
static readonly ID = 'workbench.action.nextPanelView';
|
||||
static LABEL = nls.localize('nextPanelView', 'Next Panel View');
|
||||
static readonly LABEL = nls.localize('nextPanelView', 'Next Panel View');
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import 'vs/css!./media/panelpart';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IPanel, ActivePanelContext, PanelFocusContext } from 'vs/workbench/common/panel';
|
||||
@@ -30,7 +30,7 @@ import { Dimension, trackFocus } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isUndefinedOrNull, withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
@@ -60,28 +60,25 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
readonly snap = true;
|
||||
|
||||
get preferredHeight(): number | undefined {
|
||||
const sidebarDimension = this.layoutService.getDimension(Parts.SIDEBAR_PART);
|
||||
return sidebarDimension.height * 0.4;
|
||||
// Don't worry about titlebar or statusbar visibility
|
||||
// The difference is minimal and keeps this function clean
|
||||
return this.layoutService.dimension.height * 0.4;
|
||||
}
|
||||
|
||||
get preferredWidth(): number | undefined {
|
||||
const statusbarPart = this.layoutService.getDimension(Parts.STATUSBAR_PART);
|
||||
return statusbarPart.width * 0.4;
|
||||
return this.layoutService.dimension.width * 0.4;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); }
|
||||
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean; }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); }
|
||||
readonly onDidPanelClose: Event<IPanel> = this.onDidCompositeClose.event;
|
||||
|
||||
private _onDidVisibilityChange = this._register(new Emitter<boolean>());
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
|
||||
private activePanelContextKey: IContextKey<string>;
|
||||
private panelFocusContextKey: IContextKey<boolean>;
|
||||
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: Map<string, { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction }> = new Map();
|
||||
private compositeActions: Map<string, { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction; }> = new Map();
|
||||
|
||||
private blockOpeningPanel = false;
|
||||
private _contentDimension: Dimension | undefined;
|
||||
@@ -123,7 +120,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
openComposite: (compositeId: string) => Promise.resolve(this.openPanel(compositeId, true)),
|
||||
getActivityAction: (compositeId: string) => this.getCompositeActions(compositeId).activityAction,
|
||||
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))),
|
||||
getContextMenuActions: () => [
|
||||
this.instantiationService.createInstance(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL),
|
||||
this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))
|
||||
@@ -208,19 +205,19 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = this.getContainer();
|
||||
container.style.backgroundColor = this.getColor(PANEL_BACKGROUND);
|
||||
container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
container.style.backgroundColor = this.getColor(PANEL_BACKGROUND) || '';
|
||||
container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || '';
|
||||
|
||||
const title = this.getTitleArea();
|
||||
if (title) {
|
||||
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
|
||||
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || '';
|
||||
}
|
||||
}
|
||||
|
||||
openPanel(id: string, focus?: boolean): Panel | null {
|
||||
openPanel(id: string, focus?: boolean): Panel | undefined {
|
||||
if (this.blockOpeningPanel) {
|
||||
return null; // Workaround against a potential race condition
|
||||
return undefined; // Workaround against a potential race condition
|
||||
}
|
||||
|
||||
// First check if panel is hidden and show if so
|
||||
@@ -233,7 +230,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
}
|
||||
|
||||
return withUndefinedAsNull(this.openComposite(id, focus));
|
||||
return this.openComposite(id, focus);
|
||||
}
|
||||
|
||||
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
|
||||
@@ -241,15 +238,15 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
|
||||
getPanel(panelId: string): IPanelIdentifier | undefined {
|
||||
return withNullAsUndefined(Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanel(panelId));
|
||||
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanel(panelId);
|
||||
}
|
||||
|
||||
getPanels(): PanelDescriptor[] {
|
||||
getPanels(): readonly PanelDescriptor[] {
|
||||
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels()
|
||||
.sort((v1, v2) => typeof v1.order === 'number' && typeof v2.order === 'number' ? v1.order - v2.order : NaN);
|
||||
}
|
||||
|
||||
getPinnedPanels(): PanelDescriptor[] {
|
||||
getPinnedPanels(): readonly PanelDescriptor[] {
|
||||
const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(c => c.id);
|
||||
return this.getPanels()
|
||||
.filter(p => pinnedCompositeIds.indexOf(p.id) !== -1)
|
||||
@@ -263,7 +260,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
];
|
||||
}
|
||||
|
||||
getActivePanel(): IPanel | null {
|
||||
getActivePanel(): IPanel | undefined {
|
||||
return this.getActiveComposite();
|
||||
}
|
||||
|
||||
@@ -303,9 +300,9 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
|
||||
if (this.layoutService.getPanelPosition() === Position.RIGHT) {
|
||||
this._contentDimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting
|
||||
this._contentDimension = new Dimension(width - 1, height); // Take into account the 1px border when layouting
|
||||
} else {
|
||||
this._contentDimension = new Dimension(width, height!);
|
||||
this._contentDimension = new Dimension(width, height);
|
||||
}
|
||||
|
||||
// Layout contents
|
||||
@@ -316,7 +313,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
|
||||
private layoutCompositeBar(): void {
|
||||
if (this._contentDimension) {
|
||||
if (this._contentDimension && this.dimension) {
|
||||
let availableWidth = this._contentDimension.width - 40; // take padding into account
|
||||
if (this.toolBar) {
|
||||
availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing
|
||||
@@ -326,11 +323,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
}
|
||||
}
|
||||
|
||||
private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } {
|
||||
private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction; } {
|
||||
let compositeActions = this.compositeActions.get(compositeId);
|
||||
if (!compositeActions) {
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
|
||||
activityAction: this.instantiationService.createInstance(PanelActivityAction, assertIsDefined(this.getPanel(compositeId))),
|
||||
pinnedAction: new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar)
|
||||
};
|
||||
|
||||
@@ -357,7 +354,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
private getToolbarWidth(): number {
|
||||
const activePanel = this.getActivePanel();
|
||||
if (!activePanel) {
|
||||
if (!activePanel || !this.toolBar) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -446,10 +443,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
return {
|
||||
type: Parts.PANEL_PART
|
||||
|
||||
@@ -305,8 +305,8 @@ class QuickInput extends Disposable implements IQuickInput {
|
||||
this.ui.inputBox.showDecoration(severity);
|
||||
if (severity === Severity.Error) {
|
||||
const styles = this.ui.inputBox.stylesForType(severity);
|
||||
this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : null;
|
||||
this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : null;
|
||||
this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : '';
|
||||
this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : '';
|
||||
this.ui.message.style.paddingBottom = '4px';
|
||||
} else {
|
||||
this.ui.message.style.backgroundColor = '';
|
||||
@@ -323,7 +323,7 @@ class QuickInput extends Disposable implements IQuickInput {
|
||||
|
||||
class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPick<T> {
|
||||
|
||||
private static INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results.");
|
||||
private static readonly INPUT_BOX_ARIA_LABEL = localize('quickInputBox.ariaLabel', "Type to narrow down results.");
|
||||
|
||||
private _value = '';
|
||||
private _placeholder: string;
|
||||
@@ -762,7 +762,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
|
||||
class InputBox extends QuickInput implements IInputBox {
|
||||
|
||||
private static noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel");
|
||||
private static readonly noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel");
|
||||
|
||||
private _value = '';
|
||||
private _valueSelection: Readonly<[number, number]>;
|
||||
@@ -1511,16 +1511,16 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
if (this.ui) {
|
||||
// TODO
|
||||
const titleColor = { dark: 'rgba(255, 255, 255, 0.105)', light: 'rgba(0,0,0,.06)', hc: 'black' }[theme.type];
|
||||
this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : null;
|
||||
this.titleBar.style.backgroundColor = titleColor ? titleColor.toString() : '';
|
||||
this.ui.inputBox.style(theme);
|
||||
const quickInputBackground = theme.getColor(QUICK_INPUT_BACKGROUND);
|
||||
this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : null;
|
||||
this.ui.container.style.backgroundColor = quickInputBackground ? quickInputBackground.toString() : '';
|
||||
const quickInputForeground = theme.getColor(QUICK_INPUT_FOREGROUND);
|
||||
this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : null;
|
||||
const contrastBorderColor = theme.getColor(contrastBorder);
|
||||
this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
|
||||
this.ui.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : '';
|
||||
const widgetShadowColor = theme.getColor(widgetShadow);
|
||||
this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : null;
|
||||
this.ui.container.style.boxShadow = widgetShadowColor ? `0 5px 8px ${widgetShadowColor}` : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IMatch } from 'vs/base/common/filters';
|
||||
import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon';
|
||||
import { matchesFuzzyCodiconAware, parseCodicons } from 'vs/base/common/codicon';
|
||||
import { compareAnything } from 'vs/base/common/comparers';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
@@ -114,7 +114,7 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
|
||||
const row2 = dom.append(rows, $('.quick-input-list-row'));
|
||||
|
||||
// Label
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportOcticons: true });
|
||||
data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true });
|
||||
|
||||
// Detail
|
||||
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
|
||||
@@ -152,14 +152,14 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
|
||||
|
||||
// ARIA label
|
||||
data.entry.setAttribute('aria-label', [element.saneLabel, element.saneDescription, element.saneDetail]
|
||||
.map(s => s && parseOcticons(s).text)
|
||||
.map(s => s && parseCodicons(s).text)
|
||||
.filter(s => !!s)
|
||||
.join(', '));
|
||||
|
||||
// Separator
|
||||
if (element.separator && element.separator.label) {
|
||||
data.separator.textContent = element.separator.label;
|
||||
data.separator.style.display = null;
|
||||
data.separator.style.display = '';
|
||||
} else {
|
||||
data.separator.style.display = 'none';
|
||||
}
|
||||
@@ -490,12 +490,12 @@ export class QuickInputList {
|
||||
});
|
||||
}
|
||||
|
||||
// Filter by value (since we support octicons, use octicon aware fuzzy matching)
|
||||
// Filter by value (since we support codicons, use codicon aware fuzzy matching)
|
||||
else {
|
||||
this.elements.forEach(element => {
|
||||
const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel))) : undefined;
|
||||
const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || ''))) : undefined;
|
||||
const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || ''))) : undefined;
|
||||
const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneLabel))) : undefined;
|
||||
const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDescription || ''))) : undefined;
|
||||
const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDetail || ''))) : undefined;
|
||||
|
||||
if (labelHighlights || descriptionHighlights || detailHighlights) {
|
||||
element.labelHighlights = labelHighlights;
|
||||
|
||||
@@ -69,21 +69,21 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
private readonly _onHide: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onHide: Event<void> = this._onHide.event;
|
||||
|
||||
private preserveInput: boolean;
|
||||
private isQuickOpen: boolean;
|
||||
private lastInputValue: string;
|
||||
private lastSubmittedInputValue: string;
|
||||
private quickOpenWidget: QuickOpenWidget;
|
||||
private preserveInput: boolean | undefined;
|
||||
private isQuickOpen: boolean | undefined;
|
||||
private lastInputValue: string | undefined;
|
||||
private lastSubmittedInputValue: string | undefined;
|
||||
private quickOpenWidget: QuickOpenWidget | undefined;
|
||||
private mapResolvedHandlersToPrefix: Map<string, Promise<QuickOpenHandler>> = new Map();
|
||||
private mapContextKeyToContext: Map<string, IContextKey<boolean>> = new Map();
|
||||
private handlerOnOpenCalled: Set<string> = new Set();
|
||||
private promisesToCompleteOnHide: ValueCallback[] = [];
|
||||
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor | null;
|
||||
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor | null | undefined;
|
||||
private actionProvider = new ContributableActionProvider();
|
||||
private closeOnFocusLost: boolean;
|
||||
private searchInEditorHistory: boolean;
|
||||
private closeOnFocusLost: boolean | undefined;
|
||||
private searchInEditorHistory: boolean | undefined;
|
||||
private editorHistoryHandler: EditorHistoryHandler;
|
||||
private pendingGetResultsInvocation: CancellationTokenSource | null;
|
||||
private pendingGetResultsInvocation: CancellationTokenSource | null = null;
|
||||
|
||||
constructor(
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@@ -107,7 +107,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration()));
|
||||
this._register(this.layoutService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this._register(this.layoutService.onPartVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget()));
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
}
|
||||
@@ -173,12 +173,12 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Create upon first open
|
||||
if (!this.quickOpenWidget) {
|
||||
this.quickOpenWidget = this._register(new QuickOpenWidget(
|
||||
const quickOpenWidget: QuickOpenWidget = this.quickOpenWidget = this._register(new QuickOpenWidget(
|
||||
this.layoutService.getWorkbenchElement(),
|
||||
{
|
||||
onOk: () => this.onOk(),
|
||||
onCancel: () => { /* ignore */ },
|
||||
onType: (value: string) => this.onType(value || ''),
|
||||
onType: (value: string) => this.onType(quickOpenWidget, value || ''),
|
||||
onShow: () => this.handleOnShow(),
|
||||
onHide: (reason) => this.handleOnHide(reason),
|
||||
onFocusLost: () => !this.closeOnFocusLost
|
||||
@@ -186,8 +186,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
inputPlaceHolder: this.hasHandler(HELP_PREFIX) ? nls.localize('quickOpenInput', "Type '?' to get help on the actions you can take from here") : '',
|
||||
keyboardSupport: false,
|
||||
treeCreator: (container, config, opts) => this.instantiationService.createInstance(WorkbenchTree, container, config, opts)
|
||||
}
|
||||
));
|
||||
}));
|
||||
this._register(attachQuickOpenStyler(this.quickOpenWidget, this.themeService, { background: QUICK_INPUT_BACKGROUND, foreground: QUICK_INPUT_FOREGROUND }));
|
||||
|
||||
const quickOpenContainer = this.quickOpenWidget.create();
|
||||
@@ -307,7 +306,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
}
|
||||
|
||||
if (key && key.get()) {
|
||||
if (key?.get()) {
|
||||
return; // already active context
|
||||
}
|
||||
|
||||
@@ -339,7 +338,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
}
|
||||
|
||||
private onType(value: string): void {
|
||||
private onType(quickOpenWidget: QuickOpenWidget, value: string): void {
|
||||
|
||||
// cancel any pending get results invocation and create new
|
||||
this.cancelPendingGetResultsInvocation();
|
||||
@@ -351,16 +350,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen);
|
||||
const handlerDescriptor = registry.getQuickOpenHandler(value);
|
||||
const defaultHandlerDescriptor = registry.getDefaultQuickOpenHandler();
|
||||
const instantProgress = handlerDescriptor && handlerDescriptor.instantProgress;
|
||||
const instantProgress = handlerDescriptor?.instantProgress;
|
||||
const contextKey = handlerDescriptor ? handlerDescriptor.contextKey : defaultHandlerDescriptor.contextKey;
|
||||
|
||||
// Reset Progress
|
||||
if (!instantProgress) {
|
||||
this.quickOpenWidget.getProgressBar().stop().hide();
|
||||
quickOpenWidget.getProgressBar().stop().hide();
|
||||
}
|
||||
|
||||
// Reset Extra Class
|
||||
this.quickOpenWidget.setExtraClass(null);
|
||||
quickOpenWidget.setExtraClass(null);
|
||||
|
||||
// Update context
|
||||
this.setQuickOpenContextKey(contextKey);
|
||||
@@ -374,7 +373,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
// Trigger onOpen
|
||||
this.resolveHandler(handlerDescriptor || defaultHandlerDescriptor);
|
||||
|
||||
this.quickOpenWidget.setInput(this.getEditorHistoryWithGroupLabel(), { autoFocusFirstEntry: true });
|
||||
quickOpenWidget.setInput(this.getEditorHistoryWithGroupLabel(), { autoFocusFirstEntry: true });
|
||||
|
||||
// If quickOpen entered empty we have to clear the prefill-cache
|
||||
this.lastInputValue = '';
|
||||
@@ -388,7 +387,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
if (handlerDescriptor) {
|
||||
this.isQuickOpen = false;
|
||||
resultPromise = this.handleSpecificHandler(handlerDescriptor, value, pendingResultsInvocationToken);
|
||||
resultPromise = this.handleSpecificHandler(quickOpenWidget, handlerDescriptor, value, pendingResultsInvocationToken);
|
||||
}
|
||||
|
||||
// Otherwise handle default handlers if no specific handler present
|
||||
@@ -396,7 +395,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
this.isQuickOpen = true;
|
||||
// Cache the value for prefilling the quickOpen next time is opened
|
||||
this.lastInputValue = trimmedValue;
|
||||
resultPromise = this.handleDefaultHandler(defaultHandlerDescriptor, value, pendingResultsInvocationToken);
|
||||
resultPromise = this.handleDefaultHandler(quickOpenWidget, defaultHandlerDescriptor, value, pendingResultsInvocationToken);
|
||||
}
|
||||
|
||||
// Remember as the active one
|
||||
@@ -405,7 +404,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
// Progress if task takes a long time
|
||||
setTimeout(() => {
|
||||
if (!resultPromiseDone && !pendingResultsInvocationToken.isCancellationRequested) {
|
||||
this.quickOpenWidget.getProgressBar().infinite().show();
|
||||
quickOpenWidget.getProgressBar().infinite().show();
|
||||
}
|
||||
}, instantProgress ? 0 : 800);
|
||||
|
||||
@@ -414,7 +413,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
resultPromiseDone = true;
|
||||
|
||||
if (!pendingResultsInvocationToken.isCancellationRequested) {
|
||||
this.quickOpenWidget.getProgressBar().hide();
|
||||
quickOpenWidget.getProgressBar().hide();
|
||||
}
|
||||
|
||||
pendingResultsInvocationTokenSource.dispose();
|
||||
@@ -428,7 +427,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
});
|
||||
}
|
||||
|
||||
private async handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise<void> {
|
||||
private async handleDefaultHandler(quickOpenWidget: QuickOpenWidget, handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise<void> {
|
||||
|
||||
// Fill in history results if matching and we are configured to search in history
|
||||
let matchingHistoryEntries: QuickOpenEntry[];
|
||||
@@ -451,8 +450,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// If we have matching entries from history we want to show them directly and not wait for the other results to come in
|
||||
// This also applies when we used to have entries from a previous run and now there are no more history results matching
|
||||
const previousInput = this.quickOpenWidget.getInput();
|
||||
const wasShowingHistory = previousInput && previousInput.entries && previousInput.entries.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup);
|
||||
const previousInput = quickOpenWidget.getInput();
|
||||
const wasShowingHistory = previousInput?.entries?.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup);
|
||||
if (wasShowingHistory || matchingHistoryEntries.length > 0) {
|
||||
(async () => {
|
||||
if (resolvedHandler.hasShortResponseTime()) {
|
||||
@@ -460,7 +459,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
if (!token.isCancellationRequested && !inputSet) {
|
||||
this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
inputSet = true;
|
||||
}
|
||||
})();
|
||||
@@ -472,17 +471,17 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// now is the time to show the input if we did not have set it before
|
||||
if (!inputSet) {
|
||||
this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
inputSet = true;
|
||||
}
|
||||
|
||||
// merge history and default handler results
|
||||
const handlerResults = (result && result.entries) || [];
|
||||
this.mergeResults(quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel()));
|
||||
const handlerResults = result?.entries || [];
|
||||
this.mergeResults(quickOpenWidget, quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel()));
|
||||
}
|
||||
}
|
||||
|
||||
private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string | undefined): void {
|
||||
private mergeResults(quickOpenWidget: QuickOpenWidget, quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string | undefined): void {
|
||||
|
||||
// Remove results already showing by checking for a "resource" property
|
||||
const mapEntryToResource = this.mapEntriesToResource(quickOpenModel);
|
||||
@@ -501,17 +500,17 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
const useTopBorder = quickOpenModel.getEntries().length > 0;
|
||||
additionalHandlerResults[0] = new QuickOpenEntryGroup(additionalHandlerResults[0], groupLabel, useTopBorder);
|
||||
quickOpenModel.addEntries(additionalHandlerResults);
|
||||
this.quickOpenWidget.refresh(quickOpenModel, { autoFocusFirstEntry });
|
||||
quickOpenWidget.refresh(quickOpenModel, { autoFocusFirstEntry });
|
||||
}
|
||||
|
||||
// Otherwise if no results are present (even from histoy) indicate this to the user
|
||||
else if (quickOpenModel.getEntries().length === 0) {
|
||||
quickOpenModel.addEntries([new PlaceholderQuickOpenEntry(nls.localize('noResultsFound1', "No results found"))]);
|
||||
this.quickOpenWidget.refresh(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
quickOpenWidget.refresh(quickOpenModel, { autoFocusFirstEntry: true });
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise<void> {
|
||||
private async handleSpecificHandler(quickOpenWidget: QuickOpenWidget, handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise<void> {
|
||||
const resolvedHandler = await this.resolveHandler(handlerDescriptor);
|
||||
|
||||
// Remove handler prefix from search value
|
||||
@@ -523,7 +522,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
const placeHolderLabel = (typeof canRun === 'string') ? canRun : nls.localize('canNotRunPlaceholder', "This quick open handler can not be used in the current context");
|
||||
|
||||
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider);
|
||||
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
this.showModel(quickOpenWidget, model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -531,12 +530,12 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
// Support extra class from handler
|
||||
const extraClass = resolvedHandler.getClass();
|
||||
if (extraClass) {
|
||||
this.quickOpenWidget.setExtraClass(extraClass);
|
||||
quickOpenWidget.setExtraClass(extraClass);
|
||||
}
|
||||
|
||||
// When handlers change, clear the result list first before loading the new results
|
||||
if (this.previousActiveHandlerDescriptor !== handlerDescriptor) {
|
||||
this.clearModel();
|
||||
this.clearModel(quickOpenWidget);
|
||||
}
|
||||
|
||||
// Receive Results from Handler and apply
|
||||
@@ -544,28 +543,28 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
if (!token.isCancellationRequested) {
|
||||
if (!result || !result.entries.length) {
|
||||
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(resolvedHandler.getEmptyLabel(value))]);
|
||||
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
this.showModel(quickOpenWidget, model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
} else {
|
||||
this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
this.showModel(quickOpenWidget, result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private showModel(model: IModel<any>, autoFocus?: IAutoFocus, ariaLabel?: string): void {
|
||||
private showModel(quickOpenWidget: QuickOpenWidget, model: IModel<any>, autoFocus?: IAutoFocus, ariaLabel?: string): void {
|
||||
|
||||
// If the given model is already set in the widget, refresh and return early
|
||||
if (this.quickOpenWidget.getInput() === model) {
|
||||
this.quickOpenWidget.refresh(model, autoFocus);
|
||||
if (quickOpenWidget.getInput() === model) {
|
||||
quickOpenWidget.refresh(model, autoFocus);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise just set it
|
||||
this.quickOpenWidget.setInput(model, autoFocus, ariaLabel);
|
||||
quickOpenWidget.setInput(model, autoFocus, ariaLabel);
|
||||
}
|
||||
|
||||
private clearModel(): void {
|
||||
this.showModel(new QuickOpenModel(), undefined);
|
||||
private clearModel(quickOpenWidget: QuickOpenWidget): void {
|
||||
this.showModel(quickOpenWidget, new QuickOpenModel(), undefined);
|
||||
}
|
||||
|
||||
private mapEntriesToResource(model: QuickOpenModel): { [resource: string]: QuickOpenEntry; } {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { LayoutPriority } from 'vs/base/browser/ui/grid/grid';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
export class SidebarPart extends CompositePart<Viewlet> implements IViewletService {
|
||||
|
||||
@@ -58,7 +59,6 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
}
|
||||
|
||||
const width = viewlet.getOptimalWidth();
|
||||
|
||||
if (typeof width !== 'number') {
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
@@ -70,9 +70,6 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
|
||||
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
|
||||
|
||||
private _onDidVisibilityChange = this._register(new Emitter<boolean>());
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
|
||||
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
|
||||
readonly onDidViewletDeregister: Event<ViewletDescriptor> = this._onDidViewletDeregister.event;
|
||||
|
||||
@@ -173,19 +170,19 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
super.updateStyles();
|
||||
|
||||
// Part container
|
||||
const container = this.getContainer();
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
|
||||
container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND);
|
||||
container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || '';
|
||||
container.style.color = this.getColor(SIDE_BAR_FOREGROUND);
|
||||
|
||||
const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
|
||||
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
|
||||
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
|
||||
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
|
||||
container.style.borderRightColor = isPositionLeft ? borderColor : null;
|
||||
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null;
|
||||
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null;
|
||||
container.style.borderLeftColor = !isPositionLeft ? borderColor : null;
|
||||
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '';
|
||||
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : '';
|
||||
container.style.borderRightColor = isPositionLeft ? borderColor || '' : '';
|
||||
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '';
|
||||
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : '';
|
||||
container.style.borderLeftColor = !isPositionLeft ? borderColor || '' : '';
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
@@ -198,7 +195,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
|
||||
// Viewlet service
|
||||
|
||||
getActiveViewlet(): IViewlet | null {
|
||||
getActiveViewlet(): IViewlet | undefined {
|
||||
return <IViewlet>this.getActiveComposite();
|
||||
}
|
||||
|
||||
@@ -210,7 +207,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
this.hideActiveComposite();
|
||||
}
|
||||
|
||||
async openViewlet(id: string | undefined, focus?: boolean): Promise<IViewlet | null> {
|
||||
async openViewlet(id: string | undefined, focus?: boolean): Promise<IViewlet | undefined> {
|
||||
if (typeof id === 'string' && this.getViewlet(id)) {
|
||||
return this.doOpenViewlet(id, focus);
|
||||
}
|
||||
@@ -221,12 +218,21 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
return this.doOpenViewlet(id, focus);
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getViewlets(): ViewletDescriptor[] {
|
||||
return this.viewletRegistry.getViewlets()
|
||||
.sort((v1, v2) => v1.order! - v2.order!);
|
||||
return this.viewletRegistry.getViewlets().sort((v1, v2) => {
|
||||
if (typeof v1.order !== 'number') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (typeof v2.order !== 'number') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return v1.order - v2.order;
|
||||
});
|
||||
}
|
||||
|
||||
getDefaultViewletId(): string {
|
||||
@@ -237,9 +243,9 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
return this.getViewlets().filter(viewlet => viewlet.id === id)[0];
|
||||
}
|
||||
|
||||
private doOpenViewlet(id: string, focus?: boolean): Viewlet | null {
|
||||
private doOpenViewlet(id: string, focus?: boolean): Viewlet | undefined {
|
||||
if (this.blockOpeningViewlet) {
|
||||
return null; // Workaround against a potential race condition
|
||||
return undefined; // Workaround against a potential race condition
|
||||
}
|
||||
|
||||
// First check if sidebar is hidden and show if so
|
||||
@@ -275,10 +281,6 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
this._onDidVisibilityChange.fire(visible);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
return {
|
||||
type: Parts.SIDEBAR_PART
|
||||
@@ -308,7 +310,7 @@ class FocusSideBarAction extends Action {
|
||||
}
|
||||
|
||||
// Focus into active viewlet
|
||||
let viewlet = this.viewletService.getActiveViewlet();
|
||||
const viewlet = this.viewletService.getActiveViewlet();
|
||||
if (viewlet) {
|
||||
viewlet.focus();
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar > .items-container > .statusbar-item span.octicon {
|
||||
.monaco-workbench .part.statusbar > .items-container > .statusbar-item span.codicon {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'vs/css!./media/statusbarpart';
|
||||
import * as nls from 'vs/nls';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { dispose, IDisposable, Disposable, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
@@ -27,11 +27,12 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { coalesce, find } from 'vs/base/common/arrays';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
interface IPendingStatusbarEntry {
|
||||
id: string;
|
||||
@@ -175,13 +176,7 @@ class StatusbarViewModel extends Disposable {
|
||||
}
|
||||
|
||||
findEntry(container: HTMLElement): IStatusbarViewModelEntry | undefined {
|
||||
for (const entry of this._entries) {
|
||||
if (entry.container === container) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return find(this._entries, entry => entry.container === container);
|
||||
}
|
||||
|
||||
getEntries(alignment: StatusbarAlignment): IStatusbarViewModelEntry[] {
|
||||
@@ -238,7 +233,10 @@ class StatusbarViewModel extends Disposable {
|
||||
return entryB.priority - entryA.priority; // higher priority towards the left
|
||||
}
|
||||
|
||||
return mapEntryToIndex.get(entryA)! - mapEntryToIndex.get(entryB)!; // otherwise maintain stable order
|
||||
const indexA = mapEntryToIndex.get(entryA);
|
||||
const indexB = mapEntryToIndex.get(entryB);
|
||||
|
||||
return indexA! - indexB!; // otherwise maintain stable order (both values known to be in map)
|
||||
}
|
||||
|
||||
if (entryA.alignment === StatusbarAlignment.LEFT) {
|
||||
@@ -310,8 +308,8 @@ class ToggleStatusbarEntryVisibilityAction extends Action {
|
||||
|
||||
class HideStatusbarEntryAction extends Action {
|
||||
|
||||
constructor(id: string, private model: StatusbarViewModel) {
|
||||
super(id, nls.localize('hide', "Hide"), undefined, true);
|
||||
constructor(id: string, name: string, private model: StatusbarViewModel) {
|
||||
super(id, nls.localize('hide', "Hide '{0}'", name), undefined, true);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -334,14 +332,14 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
|
||||
//#endregion
|
||||
|
||||
private styleElement!: HTMLStyleElement;
|
||||
private styleElement: HTMLStyleElement | undefined;
|
||||
|
||||
private pendingEntries: IPendingStatusbarEntry[] = [];
|
||||
|
||||
private readonly viewModel: StatusbarViewModel;
|
||||
|
||||
private leftItemsContainer!: HTMLElement;
|
||||
private rightItemsContainer!: HTMLElement;
|
||||
private leftItemsContainer: HTMLElement | undefined;
|
||||
private rightItemsContainer: HTMLElement | undefined;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -475,7 +473,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
...this.viewModel.getEntries(StatusbarAlignment.LEFT),
|
||||
...this.viewModel.getEntries(StatusbarAlignment.RIGHT).reverse() // reversing due to flex: row-reverse
|
||||
].forEach(entry => {
|
||||
const target = entry.alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer;
|
||||
const target = assertIsDefined(entry.alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer);
|
||||
|
||||
target.appendChild(entry.container);
|
||||
});
|
||||
@@ -488,7 +486,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
entries.reverse(); // reversing due to flex: row-reverse
|
||||
}
|
||||
|
||||
const target = alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer;
|
||||
const target = assertIsDefined(alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer);
|
||||
|
||||
// find an entry that has lower priority than the new one
|
||||
// and then insert the item before that one
|
||||
@@ -562,7 +560,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
|
||||
if (statusEntryUnderMouse) {
|
||||
actions.push(new Separator());
|
||||
actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, this.viewModel));
|
||||
actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, statusEntryUnderMouse.name, this.viewModel));
|
||||
}
|
||||
|
||||
return actions;
|
||||
@@ -571,10 +569,10 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = this.getContainer();
|
||||
const container = assertIsDefined(this.getContainer());
|
||||
|
||||
// Background colors
|
||||
const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND);
|
||||
const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND) || '';
|
||||
container.style.backgroundColor = backgroundColor;
|
||||
container.style.color = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND);
|
||||
|
||||
@@ -630,7 +628,7 @@ class StatusbarEntryItem extends Disposable {
|
||||
private entry!: IStatusbarEntry;
|
||||
|
||||
private labelContainer!: HTMLElement;
|
||||
private label!: OcticonLabel;
|
||||
private label!: CodiconLabel;
|
||||
|
||||
private readonly foregroundListener = this._register(new MutableDisposable());
|
||||
private readonly backgroundListener = this._register(new MutableDisposable());
|
||||
@@ -659,7 +657,7 @@ class StatusbarEntryItem extends Disposable {
|
||||
this.labelContainer.tabIndex = -1; // allows screen readers to read title, but still prevents tab focus.
|
||||
|
||||
// Label
|
||||
this.label = new OcticonLabel(this.labelContainer);
|
||||
this.label = new CodiconLabel(this.labelContainer);
|
||||
|
||||
// Add to parent
|
||||
this.container.appendChild(this.labelContainer);
|
||||
@@ -691,8 +689,9 @@ class StatusbarEntryItem extends Disposable {
|
||||
if (!this.entry || entry.command !== this.entry.command) {
|
||||
this.commandListener.clear();
|
||||
|
||||
if (entry.command) {
|
||||
this.commandListener.value = addDisposableListener(this.labelContainer, EventType.CLICK, () => this.executeCommand(entry.command!, entry.arguments));
|
||||
const command = entry.command;
|
||||
if (command) {
|
||||
this.commandListener.value = addDisposableListener(this.labelContainer, EventType.CLICK, () => this.executeCommand(command, entry.arguments));
|
||||
|
||||
removeClass(this.labelContainer, 'disabled');
|
||||
} else {
|
||||
@@ -779,7 +778,7 @@ class StatusbarEntryItem extends Disposable {
|
||||
}
|
||||
|
||||
if (isBackground) {
|
||||
container.style.backgroundColor = colorResult;
|
||||
container.style.backgroundColor = colorResult || '';
|
||||
} else {
|
||||
container.style.color = colorResult;
|
||||
}
|
||||
|
||||
@@ -106,54 +106,29 @@
|
||||
|
||||
.monaco-workbench.fullscreen .part.titlebar > .window-controls-container {
|
||||
display: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg {
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon {
|
||||
display: inline-block;
|
||||
-webkit-app-region: no-drag;
|
||||
line-height: 30px;
|
||||
height: 100%;
|
||||
width: 33.34%;
|
||||
width: 46px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container .window-icon svg {
|
||||
shape-rendering: crispEdges;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-close {
|
||||
-webkit-mask: url('chrome-close.svg') no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar.titlebar > .window-controls-container .window-unmaximize {
|
||||
-webkit-mask: url('chrome-restore.svg') no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container .window-maximize {
|
||||
-webkit-mask: url('chrome-maximize.svg') no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container .window-minimize {
|
||||
-webkit-mask: url('chrome-minimize.svg') no-repeat 50% 50%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg > .window-icon {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-mask-size: 23.1%;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg:hover {
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon-bg:hover {
|
||||
.monaco-workbench .part.titlebar.light > .window-controls-container > .window-icon:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon-bg.window-close-bg:hover {
|
||||
.monaco-workbench .part.titlebar > .window-controls-container > .window-icon.window-close:hover {
|
||||
background-color: rgba(232, 17, 35, 0.9);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-controls-container .window-icon.window-close:hover {
|
||||
background-color: white;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
@@ -77,11 +77,11 @@ export abstract class MenubarControl extends Disposable {
|
||||
'Help': nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")
|
||||
};
|
||||
|
||||
protected recentlyOpened: IRecentlyOpened;
|
||||
protected recentlyOpened: IRecentlyOpened = { files: [], workspaces: [] };
|
||||
|
||||
protected menuUpdater: RunOnceScheduler;
|
||||
|
||||
protected static MAX_MENU_RECENT_ENTRIES = 10;
|
||||
protected static readonly MAX_MENU_RECENT_ENTRIES = 10;
|
||||
|
||||
constructor(
|
||||
protected readonly menuService: IMenuService,
|
||||
@@ -121,6 +121,9 @@ export abstract class MenubarControl extends Disposable {
|
||||
protected abstract doUpdateMenubar(firstTime: boolean): void;
|
||||
|
||||
protected registerListeners(): void {
|
||||
// Listen for window focus changes
|
||||
this._register(this.hostService.onDidChangeFocus(e => this.onDidChangeWindowFocus(e)));
|
||||
|
||||
// Update when config changes
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
|
||||
|
||||
@@ -179,6 +182,13 @@ export abstract class MenubarControl extends Disposable {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected onDidChangeWindowFocus(hasFocus: boolean): void {
|
||||
// When we regain focus, update the recent menu items
|
||||
if (hasFocus) {
|
||||
this.onRecentlyOpenedChange();
|
||||
}
|
||||
}
|
||||
|
||||
private onConfigurationUpdated(event: IConfigurationChangeEvent): void {
|
||||
if (this.keys.some(key => event.affectsConfiguration(key))) {
|
||||
this.updateMenubar();
|
||||
@@ -260,10 +270,10 @@ export abstract class MenubarControl extends Disposable {
|
||||
}
|
||||
|
||||
export class CustomMenubarControl extends MenubarControl {
|
||||
private menubar: MenuBar;
|
||||
private container: HTMLElement;
|
||||
private alwaysOnMnemonics: boolean;
|
||||
private focusInsideMenubar: boolean;
|
||||
private menubar: MenuBar | undefined;
|
||||
private container: HTMLElement | undefined;
|
||||
private alwaysOnMnemonics: boolean = false;
|
||||
private focusInsideMenubar: boolean = false;
|
||||
|
||||
private readonly _onVisibilityChange: Emitter<boolean>;
|
||||
private readonly _onFocusStateChange: Emitter<boolean>;
|
||||
@@ -435,9 +445,9 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
return null;
|
||||
|
||||
case StateType.Idle:
|
||||
const windowId = this.electronEnvironmentService.windowId;
|
||||
const context = `window:${this.electronEnvironmentService ? this.electronEnvironmentService.windowId : 'any'}`;
|
||||
return new Action('update.check', nls.localize({ key: 'checkForUpdates', comment: ['&& denotes a mnemonic'] }, "Check for &&Updates..."), undefined, true, () =>
|
||||
this.updateService.checkForUpdates({ windowId }));
|
||||
this.updateService.checkForUpdates(context));
|
||||
|
||||
case StateType.CheckingForUpdates:
|
||||
return new Action('update.checking', nls.localize('checkingForUpdates', "Checking for Updates..."), undefined, false);
|
||||
@@ -463,7 +473,7 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
}
|
||||
|
||||
private get currentMenubarVisibility(): MenuBarVisibility {
|
||||
return this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
|
||||
return getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
}
|
||||
|
||||
private get currentDisableMenuBarAltFocus(): boolean {
|
||||
@@ -519,6 +529,11 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
}
|
||||
|
||||
private setupCustomMenubar(firstTime: boolean): void {
|
||||
// If there is no container, we cannot setup the menubar
|
||||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
this.menubar = this._register(new MenuBar(
|
||||
this.container, {
|
||||
@@ -527,12 +542,13 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
visibility: this.currentMenubarVisibility,
|
||||
getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id),
|
||||
compactMode: this.currentCompactMenuMode
|
||||
}
|
||||
));
|
||||
}));
|
||||
|
||||
this.accessibilityService.alwaysUnderlineAccessKeys().then(val => {
|
||||
this.alwaysOnMnemonics = val;
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode });
|
||||
if (this.menubar) {
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode });
|
||||
}
|
||||
});
|
||||
|
||||
this._register(this.menubar.onFocusStateChange(focused => {
|
||||
@@ -558,7 +574,9 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
|
||||
this._register(attachMenuStyler(this.menubar, this.themeService));
|
||||
} else {
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode });
|
||||
if (this.menubar) {
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode });
|
||||
}
|
||||
}
|
||||
|
||||
// Update the menu actions
|
||||
@@ -571,19 +589,20 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
for (let action of actions) {
|
||||
this.insertActionsBefore(action, target);
|
||||
if (action instanceof SubmenuItemAction) {
|
||||
if (!this.menus[action.item.submenu]) {
|
||||
this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
|
||||
const submenu = this.menus[action.item.submenu];
|
||||
this._register(submenu!.onDidChange(() => {
|
||||
let submenu = this.menus[action.item.submenu];
|
||||
if (!submenu) {
|
||||
submenu = this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
|
||||
this._register(submenu.onDidChange(() => {
|
||||
if (!this.focusInsideMenubar) {
|
||||
const actions: IAction[] = [];
|
||||
updateActions(menu, actions, topLevelTitle);
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[topLevelTitle]) });
|
||||
if (this.menubar) {
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[topLevelTitle]) });
|
||||
}
|
||||
}
|
||||
}, this));
|
||||
}
|
||||
|
||||
const submenu = this.menus[action.item.submenu]!;
|
||||
const submenuActions: SubmenuAction[] = [];
|
||||
updateActions(submenu, submenuActions, topLevelTitle);
|
||||
target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions));
|
||||
@@ -606,7 +625,9 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
if (!this.focusInsideMenubar) {
|
||||
const actions: IAction[] = [];
|
||||
updateActions(menu, actions, title);
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
if (this.menubar) {
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -616,21 +637,27 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
updateActions(menu, actions, title);
|
||||
}
|
||||
|
||||
if (!firstTime) {
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
} else {
|
||||
this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
if (this.menubar) {
|
||||
if (!firstTime) {
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
} else {
|
||||
this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeWindowFocus(hasFocus: boolean): void {
|
||||
protected onDidChangeWindowFocus(hasFocus: boolean): void {
|
||||
super.onDidChangeWindowFocus(hasFocus);
|
||||
|
||||
if (this.container) {
|
||||
if (hasFocus) {
|
||||
DOM.removeClass(this.container, 'inactive');
|
||||
} else {
|
||||
DOM.addClass(this.container, 'inactive');
|
||||
this.menubar.blur();
|
||||
if (this.menubar) {
|
||||
this.menubar.blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,9 +665,6 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
protected registerListeners(): void {
|
||||
super.registerListeners();
|
||||
|
||||
// Listen for window focus changes
|
||||
this._register(this.hostService.onDidChangeFocus(e => this.onDidChangeWindowFocus(e)));
|
||||
|
||||
// Listen for maximize/unmaximize
|
||||
if (!isWeb) {
|
||||
this._register(Event.any(
|
||||
@@ -650,7 +674,9 @@ export class CustomMenubarControl extends MenubarControl {
|
||||
}
|
||||
|
||||
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => {
|
||||
this.menubar.blur();
|
||||
if (this.menubar) {
|
||||
this.menubar.blur();
|
||||
}
|
||||
}));
|
||||
|
||||
// Mnemonics require fullscreen in web
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
|
||||
import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import { MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
@@ -18,7 +18,7 @@ import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import * as nls from 'vs/nls';
|
||||
import { EditorInput, toResource, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme';
|
||||
import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform';
|
||||
@@ -68,19 +68,20 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private title: HTMLElement;
|
||||
private dragRegion: HTMLElement;
|
||||
private windowControls: HTMLElement;
|
||||
private maxRestoreControl: HTMLElement;
|
||||
private appIcon: HTMLElement;
|
||||
private title!: HTMLElement;
|
||||
private dragRegion: HTMLElement | undefined;
|
||||
private windowControls: HTMLElement | undefined;
|
||||
private maxRestoreControl: HTMLElement | undefined;
|
||||
private appIcon: HTMLElement | undefined;
|
||||
private customMenubar: CustomMenubarControl | undefined;
|
||||
private menubar?: HTMLElement;
|
||||
private resizer: HTMLElement;
|
||||
private lastLayoutDimensions: Dimension;
|
||||
private resizer: HTMLElement | undefined;
|
||||
private lastLayoutDimensions: Dimension | undefined;
|
||||
private titleBarStyle: 'native' | 'custom';
|
||||
|
||||
private pendingTitle: string;
|
||||
private pendingTitle: string | undefined;
|
||||
|
||||
private isInactive: boolean;
|
||||
private isInactive: boolean = false;
|
||||
|
||||
private readonly properties: ITitleProperties = { isPure: true, isAdmin: false };
|
||||
private readonly activeEditorListeners = this._register(new DisposableStore());
|
||||
@@ -110,6 +111,8 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService));
|
||||
|
||||
this.titleBarStyle = getTitleBarStyle(this.configurationService, this.environmentService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
@@ -138,11 +141,13 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
this.titleUpdater.schedule();
|
||||
}
|
||||
|
||||
if (event.affectsConfiguration('window.menuBarVisibility')) {
|
||||
if (this.currentMenubarVisibility === 'compact') {
|
||||
this.uninstallMenubar();
|
||||
} else {
|
||||
this.installMenubar();
|
||||
if (this.titleBarStyle !== 'native') {
|
||||
if (event.affectsConfiguration('window.menuBarVisibility')) {
|
||||
if (this.currentMenubarVisibility === 'compact') {
|
||||
this.uninstallMenubar();
|
||||
} else {
|
||||
this.installMenubar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,8 +163,10 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
// Hide title when toggling menu bar
|
||||
if (!isWeb && this.currentMenubarVisibility === 'toggle' && visible) {
|
||||
// Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor
|
||||
hide(this.dragRegion);
|
||||
setTimeout(() => show(this.dragRegion), 50);
|
||||
if (this.dragRegion) {
|
||||
hide(this.dragRegion);
|
||||
setTimeout(() => show(this.dragRegion!), 50);
|
||||
}
|
||||
}
|
||||
|
||||
this.adjustTitleMarginToCenter();
|
||||
@@ -169,7 +176,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
}
|
||||
|
||||
private onMenubarFocusChanged(focused: boolean) {
|
||||
if (!isWeb && (isWindows || isLinux) && this.currentMenubarVisibility === 'compact') {
|
||||
if (!isWeb && (isWindows || isLinux) && this.currentMenubarVisibility === 'compact' && this.dragRegion) {
|
||||
if (focused) {
|
||||
hide(this.dragRegion);
|
||||
} else {
|
||||
@@ -281,14 +288,22 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
// Compute active editor folder
|
||||
const editorResource = editor ? toResource(editor) : undefined;
|
||||
let editorFolderResource = editorResource ? resources.dirname(editorResource) : undefined;
|
||||
if (editorFolderResource && editorFolderResource.path === '.') {
|
||||
if (editorFolderResource?.path === '.') {
|
||||
editorFolderResource = undefined;
|
||||
}
|
||||
|
||||
// Compute folder resource
|
||||
// Single Root Workspace: always the root single workspace in this case
|
||||
// Otherwise: root folder of the currently active file if any
|
||||
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })!);
|
||||
let folder: IWorkspaceFolder | null = null;
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
|
||||
folder = workspace.folders[0];
|
||||
} else {
|
||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
if (resource) {
|
||||
folder = this.contextService.getWorkspaceFolder(resource);
|
||||
}
|
||||
}
|
||||
|
||||
// Variables
|
||||
const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
|
||||
@@ -301,7 +316,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
const rootPath = root ? this.labelService.getUriLabel(root) : '';
|
||||
const folderName = folder ? folder.name : '';
|
||||
const folderPath = folder ? this.labelService.getUriLabel(folder.uri) : '';
|
||||
const dirty = editor && editor.isDirty() ? TitlebarPart.TITLE_DIRTY : '';
|
||||
const dirty = editor?.isDirty() ? TitlebarPart.TITLE_DIRTY : '';
|
||||
const appName = this.environmentService.appNameLong;
|
||||
const remoteName = this.environmentService.configuration.remoteAuthority;
|
||||
const separator = TitlebarPart.TITLE_SEPARATOR;
|
||||
@@ -338,6 +353,11 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
}
|
||||
|
||||
private installMenubar(): void {
|
||||
// If the menubar is already installed, skip
|
||||
if (this.menubar) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.customMenubar = this._register(this.instantiationService.createInstance(CustomMenubarControl));
|
||||
|
||||
this.menubar = this.element.insertBefore($('div.menubar'), this.title);
|
||||
@@ -370,7 +390,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
// Menubar: install a custom menu bar depending on configuration
|
||||
// and when not in activity bar
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) !== 'native'
|
||||
if (this.titleBarStyle !== 'native'
|
||||
&& (!isMacintosh || isWeb)
|
||||
&& this.currentMenubarVisibility !== 'compact') {
|
||||
this.installMenubar();
|
||||
@@ -400,17 +420,13 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
this.windowControls = append(this.element, $('div.window-controls-container'));
|
||||
|
||||
// Minimize
|
||||
const minimizeIconContainer = append(this.windowControls, $('div.window-icon-bg'));
|
||||
const minimizeIcon = append(minimizeIconContainer, $('div.window-icon'));
|
||||
addClass(minimizeIcon, 'window-minimize');
|
||||
const minimizeIcon = append(this.windowControls, $('div.window-icon.window-minimize.codicon.codicon-chrome-minimize'));
|
||||
this._register(addDisposableListener(minimizeIcon, EventType.CLICK, e => {
|
||||
this.electronService.minimizeWindow();
|
||||
}));
|
||||
|
||||
// Restore
|
||||
const restoreIconContainer = append(this.windowControls, $('div.window-icon-bg'));
|
||||
this.maxRestoreControl = append(restoreIconContainer, $('div.window-icon'));
|
||||
addClass(this.maxRestoreControl, 'window-max-restore');
|
||||
this.maxRestoreControl = append(this.windowControls, $('div.window-icon.window-max-restore.codicon'));
|
||||
this._register(addDisposableListener(this.maxRestoreControl, EventType.CLICK, async e => {
|
||||
const maximized = await this.electronService.isMaximized();
|
||||
if (maximized) {
|
||||
@@ -421,10 +437,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
}));
|
||||
|
||||
// Close
|
||||
const closeIconContainer = append(this.windowControls, $('div.window-icon-bg'));
|
||||
addClass(closeIconContainer, 'window-close-bg');
|
||||
const closeIcon = append(closeIconContainer, $('div.window-icon'));
|
||||
addClass(closeIcon, 'window-close');
|
||||
const closeIcon = append(this.windowControls, $('div.window-icon.window-close.codicon.codicon-chrome-close'));
|
||||
this._register(addDisposableListener(closeIcon, EventType.CLICK, e => {
|
||||
this.electronService.closeWindow();
|
||||
}));
|
||||
@@ -464,11 +477,11 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
private onDidChangeMaximized(maximized: boolean) {
|
||||
if (this.maxRestoreControl) {
|
||||
if (maximized) {
|
||||
removeClass(this.maxRestoreControl, 'window-maximize');
|
||||
addClass(this.maxRestoreControl, 'window-unmaximize');
|
||||
removeClass(this.maxRestoreControl, 'codicon-chrome-maximize');
|
||||
addClass(this.maxRestoreControl, 'codicon-chrome-restore');
|
||||
} else {
|
||||
removeClass(this.maxRestoreControl, 'window-unmaximize');
|
||||
addClass(this.maxRestoreControl, 'window-maximize');
|
||||
removeClass(this.maxRestoreControl, 'codicon-chrome-restore');
|
||||
addClass(this.maxRestoreControl, 'codicon-chrome-maximize');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +507,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
removeClass(this.element, 'inactive');
|
||||
}
|
||||
|
||||
const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND);
|
||||
const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND) || '';
|
||||
this.element.style.backgroundColor = titleBackground;
|
||||
if (titleBackground && Color.fromHex(titleBackground).isLighter()) {
|
||||
addClass(this.element, 'light');
|
||||
@@ -506,15 +519,15 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
this.element.style.color = titleForeground;
|
||||
|
||||
const titleBorder = this.getColor(TITLE_BAR_BORDER);
|
||||
this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : null;
|
||||
this.element.style.borderBottom = titleBorder ? `1px solid ${titleBorder}` : '';
|
||||
}
|
||||
}
|
||||
|
||||
private onUpdateAppIconDragBehavior() {
|
||||
const setting = this.configurationService.getValue('window.doubleClickIconToClose');
|
||||
if (setting) {
|
||||
if (setting && this.appIcon) {
|
||||
(this.appIcon.style as any)['-webkit-app-region'] = 'no-drag';
|
||||
} else {
|
||||
} else if (this.appIcon) {
|
||||
(this.appIcon.style as any)['-webkit-app-region'] = 'drag';
|
||||
}
|
||||
}
|
||||
@@ -546,8 +559,8 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
// Center between menu and window controls
|
||||
if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 ||
|
||||
rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) {
|
||||
this.title.style.position = null;
|
||||
this.title.style.left = null;
|
||||
this.title.style.position = '';
|
||||
this.title.style.left = '';
|
||||
this.title.style.transform = '';
|
||||
return;
|
||||
}
|
||||
@@ -559,7 +572,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
}
|
||||
|
||||
private get currentMenubarVisibility(): MenuBarVisibility {
|
||||
return this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
|
||||
return getMenuBarVisibility(this.configurationService, this.environmentService);
|
||||
}
|
||||
|
||||
updateLayout(dimension: Dimension): void {
|
||||
@@ -570,14 +583,24 @@ export class TitlebarPart extends Part implements ITitleService {
|
||||
if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') {
|
||||
this.title.style.zoom = `${1 / getZoomFactor()}`;
|
||||
if (!isWeb && (isWindows || isLinux)) {
|
||||
this.appIcon.style.zoom = `${1 / getZoomFactor()}`;
|
||||
this.windowControls.style.zoom = `${1 / getZoomFactor()}`;
|
||||
if (this.appIcon) {
|
||||
this.appIcon.style.zoom = `${1 / getZoomFactor()}`;
|
||||
}
|
||||
|
||||
if (this.windowControls) {
|
||||
this.windowControls.style.zoom = `${1 / getZoomFactor()}`;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.title.style.zoom = null;
|
||||
if (!isWeb && (isWindows || isLinux)) {
|
||||
this.appIcon.style.zoom = null;
|
||||
this.windowControls.style.zoom = null;
|
||||
if (this.appIcon) {
|
||||
this.appIcon.style.zoom = null;
|
||||
}
|
||||
|
||||
if (this.windowControls) {
|
||||
this.windowControls.style.zoom = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,7 +631,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
if (titlebarActiveFg) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .part.titlebar > .window-controls-container .window-icon {
|
||||
background-color: ${titlebarActiveFg};
|
||||
color: ${titlebarActiveFg};
|
||||
}
|
||||
`);
|
||||
}
|
||||
@@ -617,7 +640,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
if (titlebarInactiveFg) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .part.titlebar.inactive > .window-controls-container .window-icon {
|
||||
background-color: ${titlebarInactiveFg};
|
||||
color: ${titlebarInactiveFg};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list
|
||||
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { localize } from 'vs/nls';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -673,15 +673,15 @@ class TreeDataSource implements IAsyncDataSource<ITreeItem, ITreeItem> {
|
||||
// todo@joh,sandy make this proper and contributable from extensions
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
|
||||
const findMatchHighlightColor = theme.getColor(editorFindMatchHighlight);
|
||||
if (findMatchHighlightColor) {
|
||||
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`);
|
||||
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`);
|
||||
const matchBackgroundColor = theme.getColor(listFilterMatchHighlight);
|
||||
if (matchBackgroundColor) {
|
||||
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`);
|
||||
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${matchBackgroundColor}; }`);
|
||||
}
|
||||
const findMatchHighlightColorBorder = theme.getColor(editorFindMatchHighlightBorder);
|
||||
if (findMatchHighlightColorBorder) {
|
||||
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`);
|
||||
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`);
|
||||
const matchBorderColor = theme.getColor(listFilterMatchHighlightBorder);
|
||||
if (matchBorderColor) {
|
||||
collector.addRule(`.file-icon-themable-tree .monaco-list-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`);
|
||||
collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${matchBorderColor}; box-sizing: border-box; }`);
|
||||
}
|
||||
const link = theme.getColor(textLinkForeground);
|
||||
if (link) {
|
||||
@@ -753,6 +753,23 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : resource ? { label: basename(resource) } : undefined;
|
||||
const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined;
|
||||
const label = treeItemLabel ? treeItemLabel.label : undefined;
|
||||
const matches = (treeItemLabel && treeItemLabel.highlights && label) ? treeItemLabel.highlights.map(([start, end]) => {
|
||||
if ((Math.abs(start) > label.length) || (Math.abs(end) >= label.length)) {
|
||||
return ({ start: 0, end: 0 });
|
||||
}
|
||||
if (start < 0) {
|
||||
start = label.length + start;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = label.length + end;
|
||||
}
|
||||
if (start > end) {
|
||||
const swap = start;
|
||||
start = end;
|
||||
end = swap;
|
||||
}
|
||||
return ({ start, end });
|
||||
}) : undefined;
|
||||
const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark;
|
||||
const iconUrl = icon ? URI.revive(icon) : null;
|
||||
const title = node.tooltip ? node.tooltip : resource ? undefined : label;
|
||||
@@ -762,9 +779,9 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
|
||||
|
||||
if (resource || node.themeIcon) {
|
||||
const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
|
||||
templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: createMatches(element.filterData) });
|
||||
templateData.resourceLabel.setResource({ name: label, description, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) });
|
||||
} else {
|
||||
templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: createMatches(element.filterData) });
|
||||
templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) });
|
||||
}
|
||||
|
||||
templateData.icon.style.backgroundImage = iconUrl ? DOM.asCSSUrl(iconUrl) : '';
|
||||
|
||||
@@ -29,6 +29,7 @@ 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 IPanelColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
@@ -46,7 +47,7 @@ export interface IViewletPanelOptions extends IPanelOptions {
|
||||
|
||||
export abstract class ViewletPanel extends Panel implements IView {
|
||||
|
||||
private static AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
|
||||
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
|
||||
|
||||
private _onDidFocus = this._register(new Emitter<void>());
|
||||
readonly onDidFocus: Event<void> = this._onDidFocus.event;
|
||||
@@ -67,11 +68,11 @@ export abstract class ViewletPanel extends Panel implements IView {
|
||||
readonly title: string;
|
||||
|
||||
protected actionRunner?: IActionRunner;
|
||||
protected toolbar: ToolBar;
|
||||
protected toolbar?: ToolBar;
|
||||
private readonly showActionsAlways: boolean = false;
|
||||
private headerContainer: HTMLElement;
|
||||
private titleContainer: HTMLElement;
|
||||
protected twistiesContainer: HTMLElement;
|
||||
private headerContainer?: HTMLElement;
|
||||
private titleContainer?: HTMLElement;
|
||||
protected twistiesContainer?: HTMLElement;
|
||||
|
||||
constructor(
|
||||
options: IViewletPanelOptions,
|
||||
@@ -165,7 +166,9 @@ export abstract class ViewletPanel extends Panel implements IView {
|
||||
}
|
||||
|
||||
protected updateTitle(title: string): void {
|
||||
this.titleContainer.textContent = title;
|
||||
if (this.titleContainer) {
|
||||
this.titleContainer.textContent = title;
|
||||
}
|
||||
this._onDidChangeTitleArea.fire();
|
||||
}
|
||||
|
||||
@@ -177,11 +180,16 @@ export abstract class ViewletPanel extends Panel implements IView {
|
||||
}
|
||||
|
||||
private setActions(): void {
|
||||
this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
|
||||
this.toolbar.context = this.getActionsContext();
|
||||
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);
|
||||
}
|
||||
@@ -229,10 +237,10 @@ export class PanelViewlet extends Viewlet {
|
||||
|
||||
private lastFocusedPanel: ViewletPanel | undefined;
|
||||
private panelItems: IViewletPanelItem[] = [];
|
||||
private panelview: PanelView;
|
||||
private panelview?: PanelView;
|
||||
|
||||
get onDidSashChange(): Event<number> {
|
||||
return this.panelview.onDidSashChange;
|
||||
return assertIsDefined(this.panelview).onDidSashChange;
|
||||
}
|
||||
|
||||
protected get panels(): ViewletPanel[] {
|
||||
@@ -274,7 +282,7 @@ export class PanelViewlet extends Viewlet {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
|
||||
let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy };
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.getContextMenuActions()
|
||||
@@ -332,7 +340,9 @@ export class PanelViewlet extends Viewlet {
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
this.panelview.layout(dimension.height, dimension.width);
|
||||
if (this.panelview) {
|
||||
this.panelview.layout(dimension.height, dimension.width);
|
||||
}
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
@@ -342,7 +352,7 @@ export class PanelViewlet extends Viewlet {
|
||||
return Math.max(...sizes);
|
||||
}
|
||||
|
||||
addPanels(panels: { panel: ViewletPanel, size: number, index?: number }[]): void {
|
||||
addPanels(panels: { panel: ViewletPanel, size: number, index?: number; }[]): void {
|
||||
const wasSingleView = this.isSingleView();
|
||||
|
||||
for (const { panel, size, index } of panels) {
|
||||
@@ -378,7 +388,7 @@ export class PanelViewlet extends Viewlet {
|
||||
const panelItem: IViewletPanelItem = { panel, disposable };
|
||||
|
||||
this.panelItems.splice(index, 0, panelItem);
|
||||
this.panelview.addPanel(panel, size, index);
|
||||
assertIsDefined(this.panelview).addPanel(panel, size, index);
|
||||
}
|
||||
|
||||
removePanels(panels: ViewletPanel[]): void {
|
||||
@@ -403,7 +413,7 @@ export class PanelViewlet extends Viewlet {
|
||||
this.lastFocusedPanel = undefined;
|
||||
}
|
||||
|
||||
this.panelview.removePanel(panel);
|
||||
assertIsDefined(this.panelview).removePanel(panel);
|
||||
const [panelItem] = this.panelItems.splice(index, 1);
|
||||
panelItem.disposable.dispose();
|
||||
|
||||
@@ -424,15 +434,15 @@ export class PanelViewlet extends Viewlet {
|
||||
const [panelItem] = this.panelItems.splice(fromIndex, 1);
|
||||
this.panelItems.splice(toIndex, 0, panelItem);
|
||||
|
||||
this.panelview.movePanel(from, to);
|
||||
assertIsDefined(this.panelview).movePanel(from, to);
|
||||
}
|
||||
|
||||
resizePanel(panel: ViewletPanel, size: number): void {
|
||||
this.panelview.resizePanel(panel, size);
|
||||
assertIsDefined(this.panelview).resizePanel(panel, size);
|
||||
}
|
||||
|
||||
getPanelSize(panel: ViewletPanel): number {
|
||||
return this.panelview.getPanelSize(panel);
|
||||
return assertIsDefined(this.panelview).getPanelSize(panel);
|
||||
}
|
||||
|
||||
protected updateViewHeaders(): void {
|
||||
@@ -451,6 +461,8 @@ export class PanelViewlet extends Viewlet {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this.panelItems.forEach(i => i.disposable.dispose());
|
||||
this.panelview.dispose();
|
||||
if (this.panelview) {
|
||||
this.panelview.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/views';
|
||||
import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
@@ -22,14 +22,14 @@ import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/th
|
||||
import { toggleClass, addClass } from 'vs/base/browser/dom';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event<IViewDescriptor[]> {
|
||||
function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[]; }>): Event<IViewDescriptor[]> {
|
||||
return Event.chain(event)
|
||||
.map(({ views, viewContainer }) => viewContainer === container ? views : [])
|
||||
.filter(views => views.length > 0)
|
||||
.event;
|
||||
}
|
||||
|
||||
function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[] }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[] }> {
|
||||
function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[]; }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[]; }> {
|
||||
return Event.chain(event)
|
||||
.map(({ views, from, to }) => from === container ? { removed: views } : to === container ? { added: views } : {})
|
||||
.filter(({ added, removed }) => isNonEmptyArray(added) || isNonEmptyArray(removed))
|
||||
@@ -78,8 +78,8 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
private contextKeys = new CounterSet<string>();
|
||||
private items: IViewItem[] = [];
|
||||
|
||||
private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>());
|
||||
readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }> = this._onDidChange.event;
|
||||
private _onDidChange: Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._register(new Emitter<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }>());
|
||||
readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[]; }> = this._onDidChange.event;
|
||||
|
||||
get activeViewDescriptors(): IViewDescriptor[] {
|
||||
return this.items
|
||||
@@ -356,7 +356,7 @@ export class ContributableViewsModel extends Disposable {
|
||||
return viewDescriptor.workspace ? !!viewState.visibleWorkspace : !!viewState.visibleGlobal;
|
||||
}
|
||||
|
||||
private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState } {
|
||||
private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState; } {
|
||||
for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) {
|
||||
const viewDescriptor = this.viewDescriptors[i];
|
||||
const state = this.viewStates.get(viewDescriptor.id);
|
||||
@@ -436,8 +436,8 @@ export class ContributableViewsModel extends Disposable {
|
||||
(a, b) => a.id === b.id ? 0 : a.id < b.id ? -1 : 1
|
||||
).reverse();
|
||||
|
||||
const toRemove: { index: number, viewDescriptor: IViewDescriptor }[] = [];
|
||||
const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean }[] = [];
|
||||
const toRemove: { index: number, viewDescriptor: IViewDescriptor; }[] = [];
|
||||
const toAdd: { index: number, viewDescriptor: IViewDescriptor, size?: number, collapsed: boolean; }[] = [];
|
||||
|
||||
for (const splice of splices) {
|
||||
const startViewDescriptor = this.viewDescriptors[splice.start];
|
||||
@@ -521,7 +521,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
}
|
||||
|
||||
private saveWorkspaceViewsStates(): void {
|
||||
const storedViewsStates: { [id: string]: IStoredWorkspaceViewState } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
|
||||
const storedViewsStates: { [id: string]: IStoredWorkspaceViewState; } = JSON.parse(this.storageService.get(this.workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
|
||||
for (const viewDescriptor of this.viewDescriptors) {
|
||||
const viewState = this.viewStates.get(viewDescriptor.id);
|
||||
if (viewState) {
|
||||
@@ -557,7 +557,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
|
||||
private static loadViewsStates(workspaceViewsStateStorageId: string, globalViewsStateStorageId: string, storageService: IStorageService): Map<string, IViewState> {
|
||||
const viewStates = new Map<string, IViewState>();
|
||||
const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
|
||||
const workspaceViewsStates = <{ [id: string]: IStoredWorkspaceViewState; }>JSON.parse(storageService.get(workspaceViewsStateStorageId, StorageScope.WORKSPACE, '{}'));
|
||||
for (const id of Object.keys(workspaceViewsStates)) {
|
||||
const workspaceViewState = workspaceViewsStates[id];
|
||||
viewStates.set(id, {
|
||||
@@ -636,7 +636,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>;
|
||||
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable; }>;
|
||||
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
|
||||
private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
|
||||
|
||||
@@ -646,7 +646,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
) {
|
||||
super();
|
||||
|
||||
this.viewDescriptorCollections = new Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>();
|
||||
this.viewDescriptorCollections = new Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable; }>();
|
||||
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
|
||||
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
|
||||
|
||||
@@ -692,13 +692,13 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}
|
||||
|
||||
private onDidRegisterViewContainer(viewContainer: ViewContainer): void {
|
||||
const viewDescriptorCollection = new ViewDescriptorCollection(viewContainer, this.contextKeyService);
|
||||
const disposables: IDisposable[] = [viewDescriptorCollection];
|
||||
const disposables = new DisposableStore();
|
||||
const viewDescriptorCollection = disposables.add(new ViewDescriptorCollection(viewContainer, this.contextKeyService));
|
||||
|
||||
this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] });
|
||||
viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables);
|
||||
|
||||
this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: toDisposable(() => dispose(disposables)) });
|
||||
this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: disposables });
|
||||
}
|
||||
|
||||
private onDidDeregisterViewContainer(viewContainer: ViewContainer): void {
|
||||
@@ -709,7 +709,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}
|
||||
}
|
||||
|
||||
private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[] }): void {
|
||||
private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[]; }): void {
|
||||
added.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(true));
|
||||
removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false));
|
||||
}
|
||||
@@ -717,7 +717,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void {
|
||||
const viewlet = this.viewletService.getViewlet(container.id);
|
||||
for (const viewDescriptor of views) {
|
||||
const disposables: IDisposable[] = [];
|
||||
const disposables = new DisposableStore();
|
||||
const command: ICommandAction = {
|
||||
id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`,
|
||||
title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) },
|
||||
@@ -725,9 +725,9 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
};
|
||||
const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`);
|
||||
|
||||
disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true)));
|
||||
disposables.add(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true)));
|
||||
|
||||
disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command,
|
||||
when
|
||||
}));
|
||||
@@ -745,7 +745,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
});
|
||||
}
|
||||
|
||||
this.viewDisposable.set(viewDescriptor, toDisposable(() => dispose(disposables)));
|
||||
this.viewDisposable.set(viewDescriptor, disposables);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, 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);
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { localize } from 'vs/nls';
|
||||
import { mixin, assign } from 'vs/base/common/objects';
|
||||
import { first } from 'vs/base/common/arrays';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { isString, assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
@@ -111,9 +111,9 @@ export class QuickOpenHandler {
|
||||
*/
|
||||
getEmptyLabel(searchString: string): string {
|
||||
if (searchString.length > 0) {
|
||||
return nls.localize('noResultsMatching', "No results matching");
|
||||
return localize('noResultsMatching', "No results matching");
|
||||
}
|
||||
return nls.localize('noResultsFound2', "No results found");
|
||||
return localize('noResultsFound2', "No results found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +128,9 @@ export interface QuickOpenHandlerHelpEntry {
|
||||
*/
|
||||
export class QuickOpenHandlerDescriptor {
|
||||
prefix: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
contextKey?: string;
|
||||
helpEntries: QuickOpenHandlerHelpEntry[];
|
||||
helpEntries?: QuickOpenHandlerHelpEntry[];
|
||||
instantProgress: boolean;
|
||||
|
||||
private id: string;
|
||||
@@ -145,7 +145,7 @@ export class QuickOpenHandlerDescriptor {
|
||||
this.contextKey = contextKey;
|
||||
this.instantProgress = instantProgress;
|
||||
|
||||
if (types.isString(param)) {
|
||||
if (isString(param)) {
|
||||
this.description = param;
|
||||
} else {
|
||||
this.helpEntries = param;
|
||||
@@ -195,7 +195,7 @@ export interface IQuickOpenRegistry {
|
||||
|
||||
class QuickOpenRegistry implements IQuickOpenRegistry {
|
||||
private handlers: QuickOpenHandlerDescriptor[] = [];
|
||||
private defaultHandler: QuickOpenHandlerDescriptor;
|
||||
private defaultHandler: QuickOpenHandlerDescriptor | undefined;
|
||||
|
||||
registerQuickOpenHandler(descriptor: QuickOpenHandlerDescriptor): void {
|
||||
this.handlers.push(descriptor);
|
||||
@@ -214,11 +214,11 @@ class QuickOpenRegistry implements IQuickOpenRegistry {
|
||||
}
|
||||
|
||||
getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor | null {
|
||||
return text ? (arrays.first<QuickOpenHandlerDescriptor>(this.handlers, h => strings.startsWith(text, h.prefix)) || null) : null;
|
||||
return text ? (first<QuickOpenHandlerDescriptor>(this.handlers, h => startsWith(text, h.prefix)) || null) : null;
|
||||
}
|
||||
|
||||
getDefaultQuickOpenHandler(): QuickOpenHandlerDescriptor {
|
||||
return this.defaultHandler;
|
||||
return assertIsDefined(this.defaultHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,17 +275,17 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
|
||||
if (input instanceof EditorInput) {
|
||||
let opts = this.getOptions();
|
||||
if (opts) {
|
||||
opts = objects.mixin(opts, openOptions, true);
|
||||
opts = mixin(opts, openOptions, true);
|
||||
} else if (openOptions) {
|
||||
opts = EditorOptions.create(openOptions);
|
||||
}
|
||||
|
||||
this.editorService.openEditor(input, types.withNullAsUndefined(opts), sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
this.editorService.openEditor(input, withNullAsUndefined(opts), sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
} else {
|
||||
const resourceInput = <IResourceInput>input;
|
||||
|
||||
if (openOptions) {
|
||||
resourceInput.options = objects.assign(resourceInput.options || Object.create(null), openOptions);
|
||||
resourceInput.options = assign(resourceInput.options || Object.create(null), openOptions);
|
||||
}
|
||||
|
||||
this.editorService.openEditor(resourceInput, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
|
||||
@@ -21,6 +21,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
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';
|
||||
|
||||
export abstract class Viewlet extends Composite implements IViewlet {
|
||||
|
||||
@@ -34,8 +35,8 @@ export abstract class Viewlet extends Composite implements IViewlet {
|
||||
super(id, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
getOptimalWidth(): number | null {
|
||||
return null;
|
||||
getOptimalWidth(): number | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
@@ -76,7 +77,7 @@ export const Extensions = {
|
||||
};
|
||||
|
||||
export class ViewletRegistry extends CompositeRegistry<Viewlet> {
|
||||
private defaultViewletId!: string;
|
||||
private defaultViewletId: string | undefined;
|
||||
|
||||
/**
|
||||
* Registers a viewlet to the platform.
|
||||
@@ -120,7 +121,7 @@ export class ViewletRegistry extends CompositeRegistry<Viewlet> {
|
||||
* Gets the id of the viewlet that should open on startup by default.
|
||||
*/
|
||||
getDefaultViewletId(): string {
|
||||
return this.defaultViewletId;
|
||||
return assertIsDefined(this.defaultViewletId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +167,9 @@ export class ShowViewletAction extends Action {
|
||||
private sidebarHasFocus(): boolean {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
const activeElement = document.activeElement;
|
||||
const sidebarPart = this.layoutService.getContainer(Parts.SIDEBAR_PART);
|
||||
|
||||
return !!(activeViewlet && activeElement && DOM.isAncestor(activeElement, this.layoutService.getContainer(Parts.SIDEBAR_PART)));
|
||||
return !!(activeViewlet && activeElement && sidebarPart && DOM.isAncestor(activeElement, sidebarPart));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -288,14 +288,6 @@ class BrowserMain extends Disposable {
|
||||
let workspace: IWorkspace | undefined = undefined;
|
||||
if (this.configuration.workspaceProvider) {
|
||||
workspace = this.configuration.workspaceProvider.workspace;
|
||||
} else {
|
||||
// TODO@ben remove me once IWorkspaceProvider API is adopted
|
||||
const legacyConfiguration = this.configuration as { workspaceUri?: URI, folderUri?: URI };
|
||||
if (legacyConfiguration.workspaceUri) {
|
||||
workspace = { workspaceUri: legacyConfiguration.workspaceUri };
|
||||
} else if (legacyConfiguration.folderUri) {
|
||||
workspace = { folderUri: legacyConfiguration.folderUri };
|
||||
}
|
||||
}
|
||||
|
||||
// Multi-root workspace
|
||||
@@ -311,7 +303,7 @@ class BrowserMain extends Disposable {
|
||||
return { id: 'empty-window' };
|
||||
}
|
||||
|
||||
private getRemoteUserDataUri(): URI | null {
|
||||
private getRemoteUserDataUri(): URI | undefined {
|
||||
const element = document.getElementById('vscode-remote-user-data-uri');
|
||||
if (element) {
|
||||
const remoteUserDataPath = element.getAttribute('data-settings');
|
||||
@@ -320,7 +312,7 @@ class BrowserMain extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,16 @@ import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common
|
||||
],
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs.")
|
||||
},
|
||||
'workbench.editor.splitSizing': {
|
||||
'type': 'string',
|
||||
'enum': ['distribute', 'split'],
|
||||
'default': 'distribute',
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.splitSizingDistribute', "Splits all the editor groups to equal parts."),
|
||||
nls.localize('workbench.editor.splitSizingSplit', "Splits the active editor group to equal parts.")
|
||||
],
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'splitSizing' }, "Controls the sizing of editor groups when splitting them.")
|
||||
},
|
||||
'workbench.editor.focusRecentEditorAfterClose': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."),
|
||||
@@ -364,6 +374,11 @@ import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.")
|
||||
},
|
||||
'zenMode.silentNotifications': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.silentNotifications', "Controls whether notifications are shown while in zen mode. If true, only error notifications will pop out.")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/
|
||||
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
|
||||
import { getSingletonServiceDescriptors } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService, WillSaveStateReason, StorageScope, IWillSaveStateEvent } from 'vs/platform/storage/common/storage';
|
||||
import { IStorageService, WillSaveStateReason, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
@@ -157,7 +157,7 @@ export class Workbench extends Layout {
|
||||
this.renderWorkbench(instantiationService, accessor.get(INotificationService) as NotificationService, storageService, configurationService);
|
||||
|
||||
// Workbench Layout
|
||||
this.createWorkbenchLayout(instantiationService);
|
||||
this.createWorkbenchLayout();
|
||||
|
||||
// Layout
|
||||
this.layout();
|
||||
@@ -227,6 +227,20 @@ export class Workbench extends Layout {
|
||||
configurationService: IConfigurationService
|
||||
): void {
|
||||
|
||||
// Configuration changes
|
||||
this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing(configurationService)));
|
||||
|
||||
// Font Info
|
||||
if (isNative) {
|
||||
this._register(storageService.onWillSaveState(e => {
|
||||
if (e.reason === WillSaveStateReason.SHUTDOWN) {
|
||||
this.storeFontInfo(storageService);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
this._register(lifecycleService.onWillShutdown(() => this.storeFontInfo(storageService)));
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
this._register(lifecycleService.onBeforeShutdown(event => this._onBeforeShutdown.fire(event)));
|
||||
this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event)));
|
||||
@@ -234,12 +248,6 @@ export class Workbench extends Layout {
|
||||
this._onShutdown.fire();
|
||||
this.dispose();
|
||||
}));
|
||||
|
||||
// Configuration changes
|
||||
this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing(configurationService)));
|
||||
|
||||
// Storage
|
||||
this._register(storageService.onWillSaveState(e => this.storeFontInfo(e, storageService)));
|
||||
}
|
||||
|
||||
private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined;
|
||||
@@ -279,13 +287,19 @@ export class Workbench extends Layout {
|
||||
readFontInfo(BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel()));
|
||||
}
|
||||
|
||||
private storeFontInfo(e: IWillSaveStateEvent, storageService: IStorageService): void {
|
||||
if (e.reason === WillSaveStateReason.SHUTDOWN) {
|
||||
const serializedFontInfo = serializeFontInfo();
|
||||
if (serializedFontInfo) {
|
||||
const serializedFontInfoRaw = JSON.stringify(serializedFontInfo);
|
||||
private storeFontInfo(storageService: IStorageService): void {
|
||||
const serializedFontInfo = serializeFontInfo();
|
||||
if (serializedFontInfo) {
|
||||
const serializedFontInfoRaw = JSON.stringify(serializedFontInfo);
|
||||
|
||||
isNative ? storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL) : window.localStorage.setItem('editorFontInfo', serializedFontInfoRaw);
|
||||
// Font info is very specific to the machine the workbench runs
|
||||
// on. As such, in the web, we prefer to store this info in
|
||||
// local storage and not global storage because it would not make
|
||||
// much sense to synchronize to other machines.
|
||||
if (isNative) {
|
||||
storageService.store('editorFontInfo', serializedFontInfoRaw, StorageScope.GLOBAL);
|
||||
} else {
|
||||
window.localStorage.setItem('editorFontInfo', serializedFontInfoRaw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user