mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-15 10:58:31 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
/**
|
||||
* The action bar contributor allows to add actions to an actionbar in a given context.
|
||||
@@ -65,11 +65,7 @@ export const Scope = {
|
||||
* The ContributableActionProvider leverages the actionbar contribution model to find actions.
|
||||
*/
|
||||
export class ContributableActionProvider implements IActionProvider {
|
||||
private registry: IActionBarRegistry;
|
||||
|
||||
constructor() {
|
||||
this.registry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
|
||||
}
|
||||
private readonly registry: IActionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
|
||||
|
||||
private toContext(tree: ITree, element: any): any {
|
||||
return {
|
||||
@@ -235,16 +231,19 @@ export interface IActionBarRegistry {
|
||||
*/
|
||||
getActionBarContributors(scope: string): ActionBarContributor[];
|
||||
|
||||
setInstantiationService(service: IInstantiationService): void;
|
||||
/**
|
||||
* Starts the registry by providing the required services.
|
||||
*/
|
||||
start(accessor: ServicesAccessor): void;
|
||||
}
|
||||
|
||||
class ActionBarRegistry implements IActionBarRegistry {
|
||||
private actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0<ActionBarContributor>; }[] = [];
|
||||
private actionBarContributorInstances: { [scope: string]: ActionBarContributor[] } = Object.create(null);
|
||||
private readonly actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0<ActionBarContributor>; }[] = [];
|
||||
private readonly actionBarContributorInstances: { [scope: string]: ActionBarContributor[] } = Object.create(null);
|
||||
private instantiationService: IInstantiationService;
|
||||
|
||||
setInstantiationService(service: IInstantiationService): void {
|
||||
this.instantiationService = service;
|
||||
start(accessor: ServicesAccessor): void {
|
||||
this.instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
while (this.actionBarContributorConstructors.length > 0) {
|
||||
const entry = this.actionBarContributorConstructors.shift()!;
|
||||
|
||||
@@ -11,15 +11,15 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { MenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { IsMacContext } from 'vs/platform/workbench/common/contextkeys';
|
||||
import { IsMacContext } from 'vs/workbench/common/contextkeys';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { InEditorZenModeContext } from 'vs/workbench/common/editor';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -39,16 +39,16 @@ export class ToggleActivityBarVisibilityAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const visibility = this.partService.isVisible(Parts.ACTIVITYBAR_PART);
|
||||
const visibility = this.layoutService.isVisible(Parts.ACTIVITYBAR_PART);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
return this.configurationService.updateValue(ToggleActivityBarVisibilityAction.activityBarVisibleKey, newVisibilityValue, ConfigurationTarget.USER);
|
||||
@@ -76,16 +76,16 @@ class ToggleCenteredLayout extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered());
|
||||
this.layoutService.centerEditorLayout(!this.layoutService.isEditorLayoutCentered());
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export class ToggleEditorLayoutAction extends Action {
|
||||
const newOrientation = (this.editorGroupService.orientation === GroupOrientation.VERTICAL) ? GroupOrientation.HORIZONTAL : GroupOrientation.VERTICAL;
|
||||
this.editorGroupService.setGroupOrientation(newOrientation);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -153,7 +153,7 @@ CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', functi
|
||||
|
||||
editorGroupService.setGroupOrientation(orientation);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
const group = viewCategory;
|
||||
@@ -180,23 +180,23 @@ export class ToggleSidebarPositionAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.partService && !!this.configurationService;
|
||||
this.enabled = !!this.layoutService && !!this.configurationService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const position = this.partService.getSideBarPosition();
|
||||
const position = this.layoutService.getSideBarPosition();
|
||||
const newPositionValue = (position === Position.LEFT) ? 'right' : 'left';
|
||||
|
||||
return this.configurationService.updateValue(ToggleSidebarPositionAction.sidebarPositionConfigurationKey, newPositionValue, ConfigurationTarget.USER);
|
||||
}
|
||||
|
||||
static getLabel(partService: IPartService): string {
|
||||
return partService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left");
|
||||
static getLabel(layoutService: IWorkbenchLayoutService): string {
|
||||
return layoutService.getSideBarPosition() === Position.LEFT ? nls.localize('moveSidebarRight', "Move Side Bar Right") : nls.localize('moveSidebarLeft', "Move Side Bar Left");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,18 +220,18 @@ export class ToggleEditorVisibilityAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const hideEditor = this.partService.isVisible(Parts.EDITOR_PART);
|
||||
this.partService.setEditorHidden(hideEditor);
|
||||
const hideEditor = this.layoutService.isVisible(Parts.EDITOR_PART);
|
||||
this.layoutService.setEditorHidden(hideEditor);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -247,18 +247,18 @@ export class ToggleSidebarVisibilityAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART);
|
||||
this.partService.setSideBarHidden(hideSidebar);
|
||||
const hideSidebar = this.layoutService.isVisible(Parts.SIDEBAR_PART);
|
||||
this.layoutService.setSideBarHidden(hideSidebar);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,16 +285,16 @@ class ToggleStatusbarVisibilityAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const visibility = this.partService.isVisible(Parts.STATUSBAR_PART);
|
||||
const visibility = this.layoutService.isVisible(Parts.STATUSBAR_PART);
|
||||
const newVisibilityValue = !visibility;
|
||||
|
||||
return this.configurationService.updateValue(ToggleStatusbarVisibilityAction.statusbarVisibleKey, newVisibilityValue, ConfigurationTarget.USER);
|
||||
@@ -337,7 +337,11 @@ class ToggleTabsVisibilityAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W }), 'View: Toggle Tab Visibility', viewCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, {
|
||||
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);
|
||||
|
||||
// --- Toggle Zen Mode
|
||||
|
||||
@@ -349,16 +353,16 @@ class ToggleZenMode extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
this.enabled = !!this.partService;
|
||||
this.enabled = !!this.layoutService;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
this.partService.toggleZenMode();
|
||||
this.layoutService.toggleZenMode();
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,8 +381,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.exitZenMode',
|
||||
weight: KeybindingWeight.EditorContrib - 1000,
|
||||
handler(accessor: ServicesAccessor) {
|
||||
const partService = accessor.get(IPartService);
|
||||
partService.toggleZenMode();
|
||||
const layoutService = accessor.get(IWorkbenchLayoutService);
|
||||
layoutService.toggleZenMode();
|
||||
},
|
||||
when: InEditorZenModeContext,
|
||||
primary: KeyChord(KeyCode.Escape, KeyCode.Escape)
|
||||
@@ -443,15 +447,15 @@ export abstract class BaseResizeViewAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService protected partService: IPartService
|
||||
@IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
protected resizePart(sizeChange: number): void {
|
||||
const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART);
|
||||
const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART);
|
||||
const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART);
|
||||
const isEditorFocus = this.layoutService.hasFocus(Parts.EDITOR_PART);
|
||||
const isSidebarFocus = this.layoutService.hasFocus(Parts.SIDEBAR_PART);
|
||||
const isPanelFocus = this.layoutService.hasFocus(Parts.PANEL_PART);
|
||||
|
||||
let part: Parts | undefined;
|
||||
if (isSidebarFocus) {
|
||||
@@ -463,7 +467,7 @@ export abstract class BaseResizeViewAction extends Action {
|
||||
}
|
||||
|
||||
if (part) {
|
||||
this.partService.resizePart(part, sizeChange);
|
||||
this.layoutService.resizePart(part, sizeChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,9 +480,9 @@ export class IncreaseViewSizeAction extends BaseResizeViewAction {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService partService: IPartService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label, partService);
|
||||
super(id, label, layoutService);
|
||||
}
|
||||
|
||||
run(): Promise<boolean> {
|
||||
@@ -495,10 +499,10 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService partService: IPartService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
|
||||
) {
|
||||
super(id, label, partService);
|
||||
super(id, label, layoutService);
|
||||
}
|
||||
|
||||
run(): Promise<boolean> {
|
||||
|
||||
@@ -18,7 +18,7 @@ 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';
|
||||
|
||||
function ensureDOMFocus(widget: ListWidget): void {
|
||||
function ensureDOMFocus(widget: ListWidget | undefined): void {
|
||||
// it can happen that one of the commands is executed while
|
||||
// DOM focus is within another focusable control within the
|
||||
// list/tree item. therefor we should ensure that the
|
||||
@@ -88,10 +88,12 @@ function expandMultiSelection(focused: List<any> | PagedList<any> | ITree | Obje
|
||||
|
||||
const focus = list.getFocus() ? list.getFocus()[0] : undefined;
|
||||
const selection = list.getSelection();
|
||||
if (selection && selection.indexOf(focus) >= 0) {
|
||||
if (selection && typeof focus === 'number' && selection.indexOf(focus) >= 0) {
|
||||
list.setSelection(selection.filter(s => s !== previousFocus));
|
||||
} else {
|
||||
list.setSelection(selection.concat(focus));
|
||||
if (typeof focus === 'number') {
|
||||
list.setSelection(selection.concat(focus));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +583,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const list = focused;
|
||||
const fakeKeyboardEvent = getSelectionKeyboardEvent('keydown', false);
|
||||
list.setSelection(list.getFocus(), fakeKeyboardEvent);
|
||||
list.open(list.getFocus());
|
||||
list.open(list.getFocus(), fakeKeyboardEvent);
|
||||
}
|
||||
|
||||
// Tree
|
||||
@@ -636,7 +638,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const selectedNode = tree.getNode(start);
|
||||
const parentNode = selectedNode.parent;
|
||||
|
||||
if (!parentNode.parent) { // root
|
||||
if (!parentNode || !parentNode.parent) { // root
|
||||
scope = undefined;
|
||||
} else {
|
||||
scope = parentNode.element;
|
||||
@@ -752,7 +754,8 @@ CommandsRegistry.registerCommand({
|
||||
|
||||
// List
|
||||
if (focused instanceof List || focused instanceof PagedList) {
|
||||
// TODO@joao
|
||||
const list = focused;
|
||||
list.toggleKeyboardNavigation();
|
||||
}
|
||||
|
||||
// ObjectTree
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts, Position as PartPosition } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { IPanel } from 'vs/workbench/common/panel';
|
||||
@@ -22,19 +22,19 @@ abstract class BaseNavigationAction extends Action {
|
||||
label: string,
|
||||
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
|
||||
@IPanelService protected panelService: IPanelService,
|
||||
@IPartService protected partService: IPartService,
|
||||
@IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService protected viewletService: IViewletService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const isEditorFocus = this.partService.hasFocus(Parts.EDITOR_PART);
|
||||
const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART);
|
||||
const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART);
|
||||
const isEditorFocus = this.layoutService.hasFocus(Parts.EDITOR_PART);
|
||||
const isPanelFocus = this.layoutService.hasFocus(Parts.PANEL_PART);
|
||||
const isSidebarFocus = this.layoutService.hasFocus(Parts.SIDEBAR_PART);
|
||||
|
||||
const isSidebarPositionLeft = this.partService.getSideBarPosition() === PartPosition.LEFT;
|
||||
const isPanelPositionDown = this.partService.getPanelPosition() === PartPosition.BOTTOM;
|
||||
const isSidebarPositionLeft = this.layoutService.getSideBarPosition() === PartPosition.LEFT;
|
||||
const isPanelPositionDown = this.layoutService.getPanelPosition() === PartPosition.BOTTOM;
|
||||
|
||||
if (isEditorFocus) {
|
||||
return this.navigateOnEditorFocus(isSidebarPositionLeft, isPanelPositionDown);
|
||||
@@ -64,21 +64,25 @@ abstract class BaseNavigationAction extends Action {
|
||||
}
|
||||
|
||||
protected navigateToPanel(): IPanel | boolean {
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const activePanelId = this.panelService.getActivePanel().getId();
|
||||
const activePanelId = this.panelService.getActivePanel()!.getId();
|
||||
|
||||
return this.panelService.openPanel(activePanelId, true);
|
||||
return this.panelService.openPanel(activePanelId, true)!;
|
||||
}
|
||||
|
||||
protected navigateToSidebar(): Promise<IViewlet | boolean> {
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const activeViewletId = this.viewletService.getActiveViewlet().getId();
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
if (!activeViewlet) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const activeViewletId = activeViewlet.getId();
|
||||
|
||||
return this.viewletService.openViewlet(activeViewletId, true)
|
||||
.then(value => value === null ? false : value);
|
||||
@@ -114,10 +118,10 @@ class NavigateLeftAction extends BaseNavigationAction {
|
||||
label: string,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, editorGroupService, panelService, partService, viewletService);
|
||||
super(id, label, editorGroupService, panelService, layoutService, viewletService);
|
||||
}
|
||||
|
||||
protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise<boolean | IViewlet> {
|
||||
@@ -164,10 +168,10 @@ class NavigateRightAction extends BaseNavigationAction {
|
||||
label: string,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, editorGroupService, panelService, partService, viewletService);
|
||||
super(id, label, editorGroupService, panelService, layoutService, viewletService);
|
||||
}
|
||||
|
||||
protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise<boolean | IViewlet | IPanel> {
|
||||
@@ -214,10 +218,10 @@ class NavigateUpAction extends BaseNavigationAction {
|
||||
label: string,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, editorGroupService, panelService, partService, viewletService);
|
||||
super(id, label, editorGroupService, panelService, layoutService, viewletService);
|
||||
}
|
||||
|
||||
protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise<boolean> {
|
||||
@@ -243,10 +247,10 @@ class NavigateDownAction extends BaseNavigationAction {
|
||||
label: string,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService,
|
||||
@IPanelService panelService: IPanelService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService viewletService: IViewletService
|
||||
) {
|
||||
super(id, label, editorGroupService, panelService, partService, viewletService);
|
||||
super(id, label, editorGroupService, panelService, layoutService, viewletService);
|
||||
}
|
||||
|
||||
protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise<boolean | IPanel> {
|
||||
|
||||
@@ -9,13 +9,10 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { WORKSPACE_FILTER, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
@@ -133,15 +130,14 @@ export class SaveWorkspaceAsAction extends Action {
|
||||
id: string,
|
||||
label: string,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService
|
||||
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
return this.getNewWorkspaceConfigPath().then((configPathUri): Promise<void> | void => {
|
||||
return this.workspaceEditingService.pickNewWorkspacePath().then((configPathUri): Promise<void> | void => {
|
||||
if (configPathUri) {
|
||||
switch (this.contextService.getWorkbenchState()) {
|
||||
case WorkbenchState.EMPTY:
|
||||
@@ -155,15 +151,6 @@ export class SaveWorkspaceAsAction extends Action {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getNewWorkspaceConfigPath(): Promise<URI | undefined> {
|
||||
return this.dialogService.showSaveDialog({
|
||||
saveLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
|
||||
title: nls.localize('saveWorkspace', "Save Workspace"),
|
||||
filters: WORKSPACE_FILTER,
|
||||
defaultUri: this.dialogService.defaultWorkspacePath(Schemas.file)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenWorkspaceAction extends Action {
|
||||
@@ -253,10 +240,11 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
|
||||
|
||||
run(): Promise<any> {
|
||||
const folders = this.workspaceContextService.getWorkspace().folders;
|
||||
const remoteAuthority = this.windowService.getConfiguration().remoteAuthority;
|
||||
|
||||
return this.workspacesService.createUntitledWorkspace(folders).then(newWorkspace => {
|
||||
return this.workspacesService.createUntitledWorkspace(folders, remoteAuthority).then(newWorkspace => {
|
||||
return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => {
|
||||
return this.windowService.openWindow([URI.file(newWorkspace.configPath)], { forceNewWindow: true });
|
||||
return this.windowService.openWindow([{ uri: newWorkspace.configPath, typeHint: 'file' }], { forceNewWindow: true });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/qu
|
||||
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder';
|
||||
@@ -35,7 +34,7 @@ CommandsRegistry.registerCommand({
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: '_files.pickFolderAndOpen',
|
||||
handler: (accessor: ServicesAccessor, forceNewWindow: boolean) => accessor.get(IFileDialogService).pickFolderAndOpen({ forceNewWindow })
|
||||
handler: (accessor: ServicesAccessor, options: { forceNewWindow: boolean }) => accessor.get(IFileDialogService).pickFolderAndOpen(options)
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
@@ -64,7 +63,7 @@ CommandsRegistry.registerCommand({
|
||||
title: nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"),
|
||||
canSelectFolders: true,
|
||||
canSelectMany: true,
|
||||
defaultUri: dialogsService.defaultFolderPath(Schemas.file)
|
||||
defaultUri: dialogsService.defaultFolderPath()
|
||||
}).then((folders): Promise<any> | null => {
|
||||
if (!folders || !folders.length) {
|
||||
return null;
|
||||
@@ -93,7 +92,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc
|
||||
const folderPicks = folders.map(folder => {
|
||||
return {
|
||||
label: folder.name,
|
||||
description: labelService.getUriLabel(resources.dirname(folder.uri)!, { relative: true }),
|
||||
description: labelService.getUriLabel(resources.dirname(folder.uri), { relative: true }),
|
||||
folder,
|
||||
iconClasses: getIconClasses(modelService, modeService, folder.uri, FileKind.ROOT_FOLDER)
|
||||
} as IQuickPickItem;
|
||||
|
||||
@@ -38,7 +38,7 @@ export abstract class Composite extends Component implements IComposite {
|
||||
private _onDidFocus: Emitter<void>;
|
||||
get onDidFocus(): Event<void> {
|
||||
if (!this._onDidFocus) {
|
||||
this._registerFocusTrackEvents();
|
||||
this.registerFocusTrackEvents();
|
||||
}
|
||||
|
||||
return this._onDidFocus.event;
|
||||
@@ -47,13 +47,13 @@ export abstract class Composite extends Component implements IComposite {
|
||||
private _onDidBlur: Emitter<void>;
|
||||
get onDidBlur(): Event<void> {
|
||||
if (!this._onDidBlur) {
|
||||
this._registerFocusTrackEvents();
|
||||
this.registerFocusTrackEvents();
|
||||
}
|
||||
|
||||
return this._onDidBlur.event;
|
||||
}
|
||||
|
||||
private _registerFocusTrackEvents(): void {
|
||||
private registerFocusTrackEvents(): void {
|
||||
this._onDidFocus = this._register(new Emitter<void>());
|
||||
this._onDidBlur = this._register(new Emitter<void>());
|
||||
|
||||
@@ -173,6 +173,13 @@ export abstract class Composite extends Component implements IComposite {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a context to be passed to the toolbar.
|
||||
*/
|
||||
getActionsContext(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of IActionRunner to use with this composite for the
|
||||
* composite tool bar.
|
||||
@@ -217,11 +224,11 @@ export abstract class CompositeDescriptor<T extends Composite> {
|
||||
|
||||
constructor(
|
||||
private readonly ctor: IConstructorSignature0<T>,
|
||||
public readonly id: string,
|
||||
public readonly name: string,
|
||||
public readonly cssClass?: string,
|
||||
public readonly order?: number,
|
||||
public readonly keybindingId?: string,
|
||||
readonly id: string,
|
||||
readonly name: string,
|
||||
readonly cssClass?: string,
|
||||
readonly order?: number,
|
||||
readonly keybindingId?: string,
|
||||
) { }
|
||||
|
||||
instantiate(instantiationService: IInstantiationService): T {
|
||||
|
||||
211
src/vs/workbench/browser/contextkeys.ts
Normal file
211
src/vs/workbench/browser/contextkeys.ts
Normal file
@@ -0,0 +1,211 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext } from 'vs/workbench/common/editor';
|
||||
import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys';
|
||||
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';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { SidebarVisibleContext, SideBarVisibleContext } from 'vs/workbench/common/viewlet';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
|
||||
export class WorkbenchContextKeysHandler extends Disposable {
|
||||
private inputFocusedContext: IContextKey<boolean>;
|
||||
|
||||
private activeEditorContext: IContextKey<string | null>;
|
||||
private editorsVisibleContext: IContextKey<boolean>;
|
||||
private textCompareEditorVisibleContext: IContextKey<boolean>;
|
||||
private textCompareEditorActiveContext: IContextKey<boolean>;
|
||||
private activeEditorGroupEmpty: IContextKey<boolean>;
|
||||
private multipleEditorGroupsContext: IContextKey<boolean>;
|
||||
private splitEditorsVerticallyContext: IContextKey<boolean>;
|
||||
|
||||
private workbenchStateContext: IContextKey<string>;
|
||||
private workspaceFolderCountContext: IContextKey<number>;
|
||||
|
||||
|
||||
private inZenModeContext: IContextKey<boolean>;
|
||||
|
||||
private sideBarVisibleContext: IContextKey<boolean>;
|
||||
//TODO@Isidor remove in May
|
||||
private sidebarVisibleContext: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService private contextKeyService: IContextKeyService,
|
||||
@IWorkspaceContextService private contextService: IWorkspaceContextService,
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||
@IWindowService private windowService: IWindowService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@IEditorGroupsService private editorGroupService: IEditorGroupsService,
|
||||
@IWorkbenchLayoutService private layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService private viewletService: IViewletService
|
||||
) {
|
||||
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(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.viewletService.onDidViewletClose(() => this.updateSideBarContextKeys()));
|
||||
this._register(this.viewletService.onDidViewletOpen(() => this.updateSideBarContextKeys()));
|
||||
}
|
||||
|
||||
private initContextKeys(): void {
|
||||
|
||||
// Platform
|
||||
IsMacContext.bindTo(this.contextKeyService);
|
||||
IsLinuxContext.bindTo(this.contextKeyService);
|
||||
IsWindowsContext.bindTo(this.contextKeyService);
|
||||
|
||||
// macOS Native Tabs
|
||||
const windowConfig = this.configurationService.getValue<IWindowConfiguration>();
|
||||
HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs);
|
||||
|
||||
// Development
|
||||
IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment);
|
||||
|
||||
// File Pickers
|
||||
SupportsWorkspacesContext.bindTo(this.contextKeyService);
|
||||
SupportsOpenFileFolderContext.bindTo(this.contextKeyService).set(!!this.windowService.getConfiguration().remoteAuthority);
|
||||
|
||||
// Editors
|
||||
this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService);
|
||||
this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
|
||||
this.textCompareEditorVisibleContext = TextCompareEditorVisibleContext.bindTo(this.contextKeyService);
|
||||
this.textCompareEditorActiveContext = TextCompareEditorActiveContext.bindTo(this.contextKeyService);
|
||||
this.activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService);
|
||||
this.multipleEditorGroupsContext = MultipleEditorGroupsContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Inputs
|
||||
this.inputFocusedContext = InputFocusedContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Workbench State
|
||||
this.workbenchStateContext = WorkbenchStateContext.bindTo(this.contextKeyService);
|
||||
this.updateWorkbenchStateContextKey();
|
||||
|
||||
// Workspace Folder Count
|
||||
this.workspaceFolderCountContext = WorkspaceFolderCountContext.bindTo(this.contextKeyService);
|
||||
this.updateWorkspaceFolderCountContextKey();
|
||||
|
||||
// Editor Layout
|
||||
this.splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService);
|
||||
this.updateSplitEditorsVerticallyContext();
|
||||
|
||||
// Zen Mode
|
||||
this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService);
|
||||
|
||||
// Sidebar
|
||||
this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService);
|
||||
this.sidebarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService);
|
||||
}
|
||||
|
||||
private updateEditorContextKeys(): void {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
const visibleEditors = this.editorService.visibleControls;
|
||||
|
||||
this.textCompareEditorActiveContext.set(!!activeControl && activeControl.getId() === TEXT_DIFF_EDITOR_ID);
|
||||
this.textCompareEditorVisibleContext.set(visibleEditors.some(control => control.getId() === TEXT_DIFF_EDITOR_ID));
|
||||
|
||||
if (visibleEditors.length > 0) {
|
||||
this.editorsVisibleContext.set(true);
|
||||
} else {
|
||||
this.editorsVisibleContext.reset();
|
||||
}
|
||||
|
||||
if (!this.editorService.activeEditor) {
|
||||
this.activeEditorGroupEmpty.set(true);
|
||||
} else {
|
||||
this.activeEditorGroupEmpty.reset();
|
||||
}
|
||||
|
||||
if (this.editorGroupService.count > 1) {
|
||||
this.multipleEditorGroupsContext.set(true);
|
||||
} else {
|
||||
this.multipleEditorGroupsContext.reset();
|
||||
}
|
||||
|
||||
if (activeControl) {
|
||||
this.activeEditorContext.set(activeControl.getId());
|
||||
} else {
|
||||
this.activeEditorContext.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private updateInputContextKeys(): void {
|
||||
|
||||
function activeElementIsInput(): boolean {
|
||||
return !!document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA');
|
||||
}
|
||||
|
||||
const isInputFocused = activeElementIsInput();
|
||||
this.inputFocusedContext.set(isInputFocused);
|
||||
|
||||
if (isInputFocused) {
|
||||
const tracker = trackFocus(document.activeElement as HTMLElement);
|
||||
Event.once(tracker.onDidBlur)(() => {
|
||||
this.inputFocusedContext.set(activeElementIsInput());
|
||||
|
||||
tracker.dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private updateWorkbenchStateContextKey(): void {
|
||||
this.workbenchStateContext.set(this.getWorkbenchStateString());
|
||||
}
|
||||
|
||||
private updateWorkspaceFolderCountContextKey(): void {
|
||||
this.workspaceFolderCountContext.set(this.contextService.getWorkspace().folders.length);
|
||||
}
|
||||
|
||||
private updateSplitEditorsVerticallyContext(): void {
|
||||
const direction = preferredSideBySideGroupDirection(this.configurationService);
|
||||
this.splitEditorsVerticallyContext.set(direction === GroupDirection.DOWN);
|
||||
}
|
||||
|
||||
private getWorkbenchStateString(): string {
|
||||
switch (this.contextService.getWorkbenchState()) {
|
||||
case WorkbenchState.EMPTY: return 'empty';
|
||||
case WorkbenchState.FOLDER: return 'folder';
|
||||
case WorkbenchState.WORKSPACE: return 'workspace';
|
||||
}
|
||||
}
|
||||
|
||||
private updateSideBarContextKeys(): void {
|
||||
this.sideBarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));
|
||||
this.sidebarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extname, basename, normalize } from 'vs/base/common/paths';
|
||||
import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { normalize } from 'vs/base/common/path';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowsService, IWindowService, IURIToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
@@ -15,22 +16,19 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un
|
||||
import { DefaultEndOfLine } from 'vs/editor/common/model';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { DataTransfers } from 'vs/base/browser/dnd';
|
||||
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { normalizeDriveLetter } from 'vs/base/common/labels';
|
||||
import { MIME_BINARY } from 'vs/base/common/mime';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IRecentFile } from 'vs/platform/history/common/history';
|
||||
|
||||
export interface IDraggedResource {
|
||||
resource: URI;
|
||||
@@ -60,8 +58,8 @@ export interface IDraggedEditor extends IDraggedResource {
|
||||
|
||||
export interface ISerializedDraggedEditor {
|
||||
resource: string;
|
||||
backupResource: string;
|
||||
viewState: IEditorViewState;
|
||||
backupResource?: string;
|
||||
viewState: IEditorViewState | null;
|
||||
}
|
||||
|
||||
export const CodeDataTransfers = {
|
||||
@@ -71,7 +69,7 @@ export const CodeDataTransfers = {
|
||||
|
||||
export function extractResources(e: DragEvent, externalOnly?: boolean): Array<IDraggedResource | IDraggedEditor> {
|
||||
const resources: Array<IDraggedResource | IDraggedEditor> = [];
|
||||
if (e.dataTransfer.types.length > 0) {
|
||||
if (e.dataTransfer && e.dataTransfer.types.length > 0) {
|
||||
|
||||
// Check for window-to-window DND
|
||||
if (!externalOnly) {
|
||||
@@ -165,7 +163,7 @@ export class ResourcesDropHandler {
|
||||
) {
|
||||
}
|
||||
|
||||
handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void {
|
||||
handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): void {
|
||||
const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled);
|
||||
if (!untitledOrFileResources.length) {
|
||||
return;
|
||||
@@ -181,9 +179,9 @@ export class ResourcesDropHandler {
|
||||
}
|
||||
|
||||
// Add external ones to recently open list unless dropped resource is a workspace
|
||||
const filesToAddToHistory = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => d.resource);
|
||||
if (filesToAddToHistory.length) {
|
||||
this.windowsService.addRecentlyOpened(filesToAddToHistory);
|
||||
const recents: IRecentFile[] = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource }));
|
||||
if (recents.length) {
|
||||
this.windowsService.addRecentlyOpened(recents);
|
||||
}
|
||||
|
||||
const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({
|
||||
@@ -238,10 +236,10 @@ export class ResourcesDropHandler {
|
||||
}
|
||||
|
||||
// Resolve the contents of the dropped dirty resource from source
|
||||
return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource).then(content => {
|
||||
return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource!).then(content => {
|
||||
|
||||
// Set the contents of to the resource to the target
|
||||
return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true));
|
||||
return this.backupFileService.backupResource(droppedDirtyEditor.resource, content!.create(this.getDefaultEOL()).createSnapshot(true));
|
||||
}).then(() => false, () => false /* ignore any error */);
|
||||
}
|
||||
|
||||
@@ -255,7 +253,7 @@ export class ResourcesDropHandler {
|
||||
}
|
||||
|
||||
private handleWorkspaceFileDrop(fileOnDiskResources: URI[]): Promise<boolean> {
|
||||
const workspaceResources: { workspaces: URI[], folders: URI[] } = {
|
||||
const workspaceResources: { workspaces: IURIToOpen[], folders: IURIToOpen[] } = {
|
||||
workspaces: [],
|
||||
folders: []
|
||||
};
|
||||
@@ -263,8 +261,8 @@ export class ResourcesDropHandler {
|
||||
return Promise.all(fileOnDiskResources.map(fileOnDiskResource => {
|
||||
|
||||
// Check for Workspace
|
||||
if (extname(fileOnDiskResource.fsPath) === `.${WORKSPACE_EXTENSION}`) {
|
||||
workspaceResources.workspaces.push(fileOnDiskResource);
|
||||
if (hasWorkspaceFileExtension(fileOnDiskResource.fsPath)) {
|
||||
workspaceResources.workspaces.push({ uri: fileOnDiskResource, typeHint: 'file' });
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -272,7 +270,7 @@ export class ResourcesDropHandler {
|
||||
// Check for Folder
|
||||
return this.fileService.resolveFile(fileOnDiskResource).then(stat => {
|
||||
if (stat.isDirectory) {
|
||||
workspaceResources.folders.push(stat.resource);
|
||||
workspaceResources.folders.push({ uri: stat.resource, typeHint: 'folder' });
|
||||
}
|
||||
}, error => undefined);
|
||||
})).then(_ => {
|
||||
@@ -286,71 +284,32 @@ export class ResourcesDropHandler {
|
||||
// Pass focus to window
|
||||
this.windowService.focusWindow();
|
||||
|
||||
let workspacesToOpen: Promise<URI[]>;
|
||||
let workspacesToOpen: Promise<IURIToOpen[]> | undefined;
|
||||
|
||||
// Open in separate windows if we drop workspaces or just one folder
|
||||
if (workspaces.length > 0 || folders.length === 1) {
|
||||
workspacesToOpen = Promise.resolve([...workspaces, ...folders].map(resources => resources));
|
||||
workspacesToOpen = Promise.resolve([...workspaces, ...folders]);
|
||||
}
|
||||
|
||||
// Multiple folders: Create new workspace with folders and open
|
||||
else if (folders.length > 1) {
|
||||
workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [URI.file(workspace.configPath)]);
|
||||
workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders).then(workspace => [<IURIToOpen>{ uri: workspace.configPath, typeHint: 'file' }]);
|
||||
}
|
||||
|
||||
// Open
|
||||
workspacesToOpen.then(workspaces => {
|
||||
this.windowService.openWindow(workspaces, { forceReuseWindow: true });
|
||||
});
|
||||
if (workspacesToOpen) {
|
||||
workspacesToOpen.then(workspaces => {
|
||||
this.windowService.openWindow(workspaces, { forceReuseWindow: true });
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
|
||||
|
||||
constructor(
|
||||
private toResource: (obj: any) => URI,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
getDragURI(tree: ITree, obj: any): string {
|
||||
const resource = this.toResource(obj);
|
||||
if (resource) {
|
||||
return resource.toString();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getDragLabel(tree: ITree, elements: any[]): string {
|
||||
if (elements.length > 1) {
|
||||
return String(elements.length);
|
||||
}
|
||||
|
||||
const resource = this.toResource(elements[0]);
|
||||
if (resource) {
|
||||
return basenameOrAuthority(resource);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void {
|
||||
|
||||
// Apply some datatransfer types to allow for dragging the element outside of the application
|
||||
const resources: URI[] = data.getData().map(source => this.toResource(source));
|
||||
if (resources) {
|
||||
this.instantiationService.invokeFunction(fillResourceDataTransfers, coalesce(resources), originalEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: (URI | { resource: URI, isDirectory: boolean })[], event: DragMouseEvent | DragEvent): void {
|
||||
if (resources.length === 0) {
|
||||
if (resources.length === 0 || !event.dataTransfer) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -366,11 +325,11 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
|
||||
// Text: allows to paste into text-capable areas
|
||||
const lineDelimiter = isWindows ? '\r\n' : '\n';
|
||||
event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath), true) : source.resource.toString()).join(lineDelimiter));
|
||||
event.dataTransfer.setData(DataTransfers.TEXT, sources.map(source => source.resource.scheme === Schemas.file ? normalize(normalizeDriveLetter(source.resource.fsPath)) : source.resource.toString()).join(lineDelimiter));
|
||||
|
||||
// Download URL: enables support to drag a tab as file to desktop (only single file supported)
|
||||
if (firstSource.resource.scheme === Schemas.file) {
|
||||
event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(firstSource.resource.fsPath), firstSource.resource.toString()].join(':'));
|
||||
event.dataTransfer.setData(DataTransfers.DOWNLOAD_URL, [MIME_BINARY, basename(firstSource.resource), firstSource.resource.toString()].join(':'));
|
||||
}
|
||||
|
||||
// Resource URLs: allows to drop multiple resources to a target in VS Code (not directories)
|
||||
@@ -388,7 +347,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources:
|
||||
files.forEach(file => {
|
||||
|
||||
// Try to find editor view state from the visible editors that match given resource
|
||||
let viewState: IEditorViewState;
|
||||
let viewState: IEditorViewState | null = null;
|
||||
const textEditorWidgets = editorService.visibleTextEditorWidgets;
|
||||
for (const textEditorWidget of textEditorWidgets) {
|
||||
if (isCodeEditor(textEditorWidget)) {
|
||||
@@ -420,8 +379,8 @@ export class LocalSelectionTransfer<T> {
|
||||
|
||||
private static readonly INSTANCE = new LocalSelectionTransfer();
|
||||
|
||||
private data: T[];
|
||||
private proto: T;
|
||||
private data?: T[];
|
||||
private proto?: T;
|
||||
|
||||
private constructor() {
|
||||
// protect against external instantiation
|
||||
@@ -442,7 +401,7 @@ export class LocalSelectionTransfer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
getData(proto: T): T[] {
|
||||
getData(proto: T): T[] | undefined {
|
||||
if (this.hasData(proto)) {
|
||||
return this.data;
|
||||
}
|
||||
@@ -489,6 +448,8 @@ export class DragAndDropObserver extends Disposable {
|
||||
}));
|
||||
|
||||
this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => {
|
||||
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||
|
||||
if (this.callbacks.onDragOver) {
|
||||
this.callbacks.onDragOver(e);
|
||||
}
|
||||
|
||||
@@ -172,10 +172,10 @@ class EditorRegistry implements IEditorRegistry {
|
||||
this.editors = editorsToSet;
|
||||
}
|
||||
|
||||
getEditorInputs(): any[] {
|
||||
const inputClasses: any[] = [];
|
||||
getEditorInputs(): SyncDescriptor<EditorInput>[] {
|
||||
const inputClasses: SyncDescriptor<EditorInput>[] = [];
|
||||
for (const editor of this.editors) {
|
||||
const editorInputDescriptors = <SyncDescriptor<EditorInput>[]>editor[INPUT_DESCRIPTORS_PROPERTY];
|
||||
const editorInputDescriptors = editor[INPUT_DESCRIPTORS_PROPERTY];
|
||||
inputClasses.push(...editorInputDescriptors.map(descriptor => descriptor.ctor));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,11 @@ import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { getIconClasses, getConfiguredLangId } from 'vs/editor/common/services/getIconClasses';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export interface IResourceLabelProps {
|
||||
resource?: uri;
|
||||
name: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
@@ -223,13 +224,13 @@ class ResourceLabelWidget extends IconLabel {
|
||||
private _onDidRender = this._register(new Emitter<void>());
|
||||
get onDidRender(): Event<void> { return this._onDidRender.event; }
|
||||
|
||||
private label: IResourceLabelProps;
|
||||
private options: IResourceLabelOptions;
|
||||
private computedIconClasses: string[];
|
||||
private lastKnownConfiguredLangId: string;
|
||||
private computedPathLabel: string;
|
||||
private label?: IResourceLabelProps;
|
||||
private options?: IResourceLabelOptions;
|
||||
private computedIconClasses?: string[];
|
||||
private lastKnownConfiguredLangId?: string;
|
||||
private computedPathLabel?: string;
|
||||
|
||||
private needsRedraw: Redraw;
|
||||
private needsRedraw?: Redraw;
|
||||
private isHidden: boolean = false;
|
||||
|
||||
constructor(
|
||||
@@ -303,7 +304,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
this.render(hasResourceChanged);
|
||||
}
|
||||
|
||||
private hasResourceChanged(label: IResourceLabelProps, options: IResourceLabelOptions): boolean {
|
||||
private hasResourceChanged(label: IResourceLabelProps, options?: IResourceLabelOptions): boolean {
|
||||
const newResource = label ? label.resource : undefined;
|
||||
const oldResource = this.label ? this.label.resource : undefined;
|
||||
|
||||
@@ -331,15 +332,15 @@ class ResourceLabelWidget extends IconLabel {
|
||||
|
||||
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
|
||||
this.setResource({
|
||||
resource: toResource(editor, { supportSideBySide: true }),
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription()
|
||||
resource: withNullAsUndefined(toResource(editor, { supportSideBySide: true })),
|
||||
name: withNullAsUndefined(editor.getName()),
|
||||
description: withNullAsUndefined(editor.getDescription())
|
||||
}, options);
|
||||
}
|
||||
|
||||
setFile(resource: uri, options?: IFileLabelOptions): void {
|
||||
const hideLabel = options && options.hideLabel;
|
||||
let name: string;
|
||||
let name: string | undefined;
|
||||
if (!hideLabel) {
|
||||
if (options && options.fileKind === FileKind.ROOT_FOLDER) {
|
||||
const workspaceFolder = this.contextService.getWorkspaceFolder(resource);
|
||||
@@ -353,7 +354,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
}
|
||||
|
||||
let description: string;
|
||||
let description: string | undefined;
|
||||
const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource));
|
||||
if (!hidePath) {
|
||||
description = this.labelService.getUriLabel(resources.dirname(resource), { relative: true });
|
||||
@@ -386,7 +387,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
}
|
||||
|
||||
if (this.label) {
|
||||
const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource);
|
||||
const configuredLangId = this.label.resource ? withNullAsUndefined(getConfiguredLangId(this.modelService, this.modeService, this.label.resource)) : undefined;
|
||||
if (this.lastKnownConfiguredLangId !== configuredLangId) {
|
||||
clearIconCache = true;
|
||||
this.lastKnownConfiguredLangId = configuredLangId;
|
||||
@@ -428,7 +429,7 @@ class ResourceLabelWidget extends IconLabel {
|
||||
iconLabelOptions.extraClasses = this.computedIconClasses.slice(0);
|
||||
}
|
||||
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) {
|
||||
@@ -444,11 +445,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
738
src/vs/workbench/browser/legacyLayout.ts
Normal file
738
src/vs/workbench/browser/legacyLayout.ts
Normal file
@@ -0,0 +1,738 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
|
||||
import { IWorkbenchLayoutService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { Dimension, getClientArea, size, position, hide, show } from 'vs/base/browser/dom';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { getZoomFactor } from 'vs/base/browser/browser';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
|
||||
const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30;
|
||||
const STATUS_BAR_HEIGHT = 22;
|
||||
const ACTIVITY_BAR_WIDTH = 50;
|
||||
|
||||
const MIN_SIDEBAR_PART_WIDTH = 170;
|
||||
const DEFAULT_SIDEBAR_PART_WIDTH = 300;
|
||||
const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50;
|
||||
|
||||
const MIN_PANEL_PART_HEIGHT = 77;
|
||||
const MIN_PANEL_PART_WIDTH = 300;
|
||||
const DEFAULT_PANEL_PART_SIZE = 350;
|
||||
const DEFAULT_PANEL_SIZE_COEFFICIENT = 0.4;
|
||||
const PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY = 0.7;
|
||||
const HIDE_PANEL_HEIGHT_THRESHOLD = 50;
|
||||
const HIDE_PANEL_WIDTH_THRESHOLD = 100;
|
||||
|
||||
/**
|
||||
* @deprecated to be replaced by new Grid layout
|
||||
*/
|
||||
export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider {
|
||||
|
||||
private static readonly sashXOneWidthSettingsKey = 'workbench.sidebar.width';
|
||||
private static readonly sashXTwoWidthSettingsKey = 'workbench.panel.width';
|
||||
private static readonly sashYHeightSettingsKey = 'workbench.panel.height';
|
||||
private static readonly panelSizeBeforeMaximizedKey = 'workbench.panel.sizeBeforeMaximized';
|
||||
|
||||
private workbenchSize: Dimension;
|
||||
|
||||
private sashXOne: Sash;
|
||||
private sashXTwo: Sash;
|
||||
private sashY: Sash;
|
||||
|
||||
private _sidebarWidth: number;
|
||||
private sidebarHeight: number;
|
||||
private titlebarHeight: number;
|
||||
private statusbarHeight: number;
|
||||
private panelSizeBeforeMaximized: number;
|
||||
private panelMaximized: boolean;
|
||||
private _panelHeight: number;
|
||||
private _panelWidth: number;
|
||||
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
private workbenchContainer: HTMLElement,
|
||||
private parts: {
|
||||
titlebar: Part,
|
||||
activitybar: Part,
|
||||
editor: Part,
|
||||
sidebar: Part,
|
||||
panel: Part,
|
||||
statusbar: Part
|
||||
},
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super();
|
||||
|
||||
// Restore state
|
||||
this.restorePreviousState();
|
||||
|
||||
// Create layout sashes
|
||||
this.sashXOne = new Sash(this.workbenchContainer, this);
|
||||
this.sashXTwo = new Sash(this.workbenchContainer, this);
|
||||
this.sashY = new Sash(this.workbenchContainer, this, { orientation: Orientation.HORIZONTAL });
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private restorePreviousState(): void {
|
||||
this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.storageService.getNumber(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_SIDEBAR_PART_WIDTH));
|
||||
|
||||
this._panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, this.storageService.getNumber(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
|
||||
this._panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, this.storageService.getNumber(WorkbenchLegacyLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
|
||||
|
||||
this.panelMaximized = false;
|
||||
this.panelSizeBeforeMaximized = this.storageService.getNumber(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, StorageScope.GLOBAL, 0);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.themeService.onThemeChange(_ => this.layout()));
|
||||
this._register((this.parts.editor as any).onDidSizeConstraintsChange(() => this.onDidEditorSizeConstraintsChange()));
|
||||
|
||||
this.registerSashListeners();
|
||||
}
|
||||
|
||||
private onDidEditorSizeConstraintsChange(): void {
|
||||
if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
|
||||
if (this.editorGroupService.count > 1) {
|
||||
const minimumEditorPartSize = new Dimension(this.parts.editor.minimumWidth, this.parts.editor.minimumHeight);
|
||||
|
||||
const sidebarOverflow = this.workbenchSize.width - this.sidebarWidth < minimumEditorPartSize.width;
|
||||
|
||||
let panelOverflow = false;
|
||||
if (this.layoutService.getPanelPosition() === Position.RIGHT) {
|
||||
panelOverflow = this.workbenchSize.width - this.panelWidth - this.sidebarWidth < minimumEditorPartSize.width;
|
||||
} else {
|
||||
panelOverflow = this.workbenchSize.height - this.panelHeight < minimumEditorPartSize.height;
|
||||
}
|
||||
|
||||
// Trigger a layout if we detect that either sidebar or panel overflow
|
||||
// as a matter of a new editor group being added to the editor part
|
||||
if (sidebarOverflow || panelOverflow) {
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private get activitybarWidth(): number {
|
||||
if (this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
return this.partLayoutInfo.activitybar.width;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private get panelHeight(): number {
|
||||
const panelPosition = this.layoutService.getPanelPosition();
|
||||
if (panelPosition === Position.RIGHT) {
|
||||
return this.sidebarHeight;
|
||||
}
|
||||
|
||||
return this._panelHeight;
|
||||
}
|
||||
|
||||
private set panelHeight(value: number) {
|
||||
this._panelHeight = Math.min(this.computeMaxPanelHeight(), Math.max(this.partLayoutInfo.panel.minHeight, value));
|
||||
}
|
||||
|
||||
private get panelWidth(): number {
|
||||
const panelPosition = this.layoutService.getPanelPosition();
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
return this.workbenchSize.width - this.activitybarWidth - this.sidebarWidth;
|
||||
}
|
||||
|
||||
return this._panelWidth;
|
||||
}
|
||||
|
||||
private set panelWidth(value: number) {
|
||||
this._panelWidth = Math.min(this.computeMaxPanelWidth(), Math.max(this.partLayoutInfo.panel.minWidth, value));
|
||||
}
|
||||
|
||||
private computeMaxPanelWidth(): number {
|
||||
let minSidebarWidth: number;
|
||||
if (this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
if (this.layoutService.getSideBarPosition() === Position.LEFT) {
|
||||
minSidebarWidth = this.partLayoutInfo.sidebar.minWidth;
|
||||
} else {
|
||||
minSidebarWidth = this.sidebarWidth;
|
||||
}
|
||||
} else {
|
||||
minSidebarWidth = 0;
|
||||
}
|
||||
|
||||
return Math.max(this.partLayoutInfo.panel.minWidth, this.workbenchSize.width - this.parts.editor.minimumWidth - minSidebarWidth - this.activitybarWidth);
|
||||
}
|
||||
|
||||
private computeMaxPanelHeight(): number {
|
||||
return Math.max(this.partLayoutInfo.panel.minHeight, this.sidebarHeight /* simplification for: window.height - status.height - title-height */ - this.parts.editor.minimumHeight);
|
||||
}
|
||||
|
||||
private get sidebarWidth(): number {
|
||||
if (this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return this._sidebarWidth;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private set sidebarWidth(value: number) {
|
||||
const panelMinWidth = this.layoutService.getPanelPosition() === Position.RIGHT && this.layoutService.isVisible(Parts.PANEL_PART) ? this.partLayoutInfo.panel.minWidth : 0;
|
||||
const maxSidebarWidth = this.workbenchSize.width - this.activitybarWidth - this.parts.editor.minimumWidth - panelMinWidth;
|
||||
|
||||
this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, Math.min(maxSidebarWidth, value));
|
||||
}
|
||||
|
||||
@memoize
|
||||
public get partLayoutInfo() {
|
||||
return {
|
||||
titlebar: {
|
||||
height: TITLE_BAR_HEIGHT
|
||||
},
|
||||
activitybar: {
|
||||
width: ACTIVITY_BAR_WIDTH
|
||||
},
|
||||
sidebar: {
|
||||
minWidth: MIN_SIDEBAR_PART_WIDTH
|
||||
},
|
||||
panel: {
|
||||
minHeight: MIN_PANEL_PART_HEIGHT,
|
||||
minWidth: MIN_PANEL_PART_WIDTH
|
||||
},
|
||||
statusbar: {
|
||||
height: STATUS_BAR_HEIGHT
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private registerSashListeners(): void {
|
||||
let startX: number = 0;
|
||||
let startY: number = 0;
|
||||
let startXTwo: number = 0;
|
||||
let startSidebarWidth: number;
|
||||
let startPanelHeight: number;
|
||||
let startPanelWidth: number;
|
||||
|
||||
this._register(this.sashXOne.onDidStart((e: ISashEvent) => {
|
||||
startSidebarWidth = this.sidebarWidth;
|
||||
startX = e.startX;
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidStart((e: ISashEvent) => {
|
||||
startPanelHeight = this.panelHeight;
|
||||
startY = e.startY;
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidStart((e: ISashEvent) => {
|
||||
startPanelWidth = this.panelWidth;
|
||||
startXTwo = e.startX;
|
||||
}));
|
||||
|
||||
this._register(this.sashXOne.onDidChange((e: ISashEvent) => {
|
||||
let doLayout = false;
|
||||
let sidebarPosition = this.layoutService.getSideBarPosition();
|
||||
let isSidebarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART);
|
||||
let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX;
|
||||
|
||||
// Sidebar visible
|
||||
if (isSidebarVisible) {
|
||||
|
||||
// Automatically hide side bar when a certain threshold is met
|
||||
if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) {
|
||||
let dragCompensation = this.partLayoutInfo.sidebar.minWidth - HIDE_SIDEBAR_WIDTH_THRESHOLD;
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth);
|
||||
this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from
|
||||
}
|
||||
|
||||
// Otherwise size the sidebar accordingly
|
||||
else {
|
||||
this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashWidth); // Sidebar can not become smaller than MIN_PART_WIDTH
|
||||
doLayout = newSashWidth >= this.partLayoutInfo.sidebar.minWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar hidden
|
||||
else {
|
||||
if ((sidebarPosition === Position.LEFT && e.currentX - startX >= this.partLayoutInfo.sidebar.minWidth) ||
|
||||
(sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) {
|
||||
startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX);
|
||||
this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth;
|
||||
this.layoutService.setSideBarHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (doLayout) {
|
||||
this.layout({ source: Parts.SIDEBAR_PART });
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidChange((e: ISashEvent) => {
|
||||
let doLayout = false;
|
||||
let isPanelVisible = this.layoutService.isVisible(Parts.PANEL_PART);
|
||||
let newSashHeight = startPanelHeight - (e.currentY - startY);
|
||||
|
||||
// Panel visible
|
||||
if (isPanelVisible) {
|
||||
|
||||
// Automatically hide panel when a certain threshold is met
|
||||
if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) {
|
||||
let dragCompensation = this.partLayoutInfo.panel.minHeight - HIDE_PANEL_HEIGHT_THRESHOLD;
|
||||
this.layoutService.setPanelHidden(true);
|
||||
startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation);
|
||||
this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from
|
||||
}
|
||||
|
||||
// Otherwise size the panel accordingly
|
||||
else {
|
||||
this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashHeight); // Panel can not become smaller than MIN_PART_HEIGHT
|
||||
doLayout = newSashHeight >= this.partLayoutInfo.panel.minHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Panel hidden
|
||||
else {
|
||||
if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) {
|
||||
startPanelHeight = 0;
|
||||
this.panelHeight = this.partLayoutInfo.panel.minHeight;
|
||||
this.layoutService.setPanelHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (doLayout) {
|
||||
this.layout({ source: Parts.PANEL_PART });
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidChange((e: ISashEvent) => {
|
||||
let doLayout = false;
|
||||
let isPanelVisible = this.layoutService.isVisible(Parts.PANEL_PART);
|
||||
let newSashWidth = startPanelWidth - (e.currentX - startXTwo);
|
||||
|
||||
// Panel visible
|
||||
if (isPanelVisible) {
|
||||
|
||||
// Automatically hide panel when a certain threshold is met
|
||||
if (newSashWidth + HIDE_PANEL_WIDTH_THRESHOLD < this.partLayoutInfo.panel.minWidth) {
|
||||
let dragCompensation = this.partLayoutInfo.panel.minWidth - HIDE_PANEL_WIDTH_THRESHOLD;
|
||||
this.layoutService.setPanelHidden(true);
|
||||
startXTwo = Math.min(this.workbenchSize.width - this.activitybarWidth, e.currentX + dragCompensation);
|
||||
this.panelWidth = startPanelWidth; // when restoring panel, restore to the panel height we started from
|
||||
}
|
||||
|
||||
// Otherwise size the panel accordingly
|
||||
else {
|
||||
this.panelWidth = newSashWidth;
|
||||
doLayout = newSashWidth >= this.partLayoutInfo.panel.minWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Panel hidden
|
||||
else {
|
||||
if (startXTwo - e.currentX >= this.partLayoutInfo.panel.minWidth) {
|
||||
startPanelWidth = 0;
|
||||
this.panelWidth = this.partLayoutInfo.panel.minWidth;
|
||||
this.layoutService.setPanelHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (doLayout) {
|
||||
this.layout({ source: Parts.PANEL_PART });
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this.sashXOne.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidEnd(() => {
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
}));
|
||||
|
||||
this._register(this.sashY.onDidReset(() => {
|
||||
this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
|
||||
this.layout();
|
||||
}));
|
||||
|
||||
this._register(this.sashXOne.onDidReset(() => {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
const optimalWidth = activeViewlet ? activeViewlet.getOptimalWidth() : null;
|
||||
this.sidebarWidth = typeof optimalWidth === 'number' ? Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH) : DEFAULT_SIDEBAR_PART_WIDTH;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.layoutService.setSideBarHidden(false);
|
||||
this.layout();
|
||||
}));
|
||||
|
||||
this._register(this.sashXTwo.onDidReset(() => {
|
||||
this.panelWidth = (this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth) * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
|
||||
this.layout();
|
||||
}));
|
||||
}
|
||||
|
||||
layout(options?: ILayoutOptions): void {
|
||||
this.workbenchSize = getClientArea(this.parent);
|
||||
|
||||
const isActivityBarHidden = !this.layoutService.isVisible(Parts.ACTIVITYBAR_PART);
|
||||
const isTitlebarHidden = !this.layoutService.isVisible(Parts.TITLEBAR_PART);
|
||||
const isPanelHidden = !this.layoutService.isVisible(Parts.PANEL_PART);
|
||||
const isStatusbarHidden = !this.layoutService.isVisible(Parts.STATUSBAR_PART);
|
||||
const isSidebarHidden = !this.layoutService.isVisible(Parts.SIDEBAR_PART);
|
||||
const sidebarPosition = this.layoutService.getSideBarPosition();
|
||||
const panelPosition = this.layoutService.getPanelPosition();
|
||||
const menubarVisibility = this.layoutService.getMenubarVisibility();
|
||||
|
||||
// Sidebar
|
||||
if (this.sidebarWidth === -1) {
|
||||
this.sidebarWidth = this.workbenchSize.width / 5;
|
||||
}
|
||||
|
||||
this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height;
|
||||
this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (isMacintosh || !menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention
|
||||
|
||||
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
|
||||
let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight);
|
||||
|
||||
// Activity Bar
|
||||
let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height);
|
||||
|
||||
// Panel part
|
||||
let panelHeight: number;
|
||||
let panelWidth: number;
|
||||
const maxPanelHeight = this.computeMaxPanelHeight();
|
||||
const maxPanelWidth = this.computeMaxPanelWidth();
|
||||
|
||||
if (isPanelHidden) {
|
||||
panelHeight = 0;
|
||||
panelWidth = 0;
|
||||
} else if (panelPosition === Position.BOTTOM) {
|
||||
if (this.panelHeight > 0) {
|
||||
panelHeight = Math.min(maxPanelHeight, Math.max(this.partLayoutInfo.panel.minHeight, this.panelHeight));
|
||||
} else {
|
||||
panelHeight = sidebarSize.height * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
}
|
||||
panelWidth = this.workbenchSize.width - sidebarSize.width - activityBarSize.width;
|
||||
|
||||
if (options && options.toggleMaximizedPanel) {
|
||||
panelHeight = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minHeight, Math.min(this.panelSizeBeforeMaximized, maxPanelHeight)) : maxPanelHeight;
|
||||
}
|
||||
|
||||
this.panelMaximized = panelHeight === maxPanelHeight;
|
||||
if (panelHeight / maxPanelHeight < PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY) {
|
||||
this.panelSizeBeforeMaximized = panelHeight;
|
||||
}
|
||||
} else {
|
||||
panelHeight = sidebarSize.height;
|
||||
if (this.panelWidth > 0) {
|
||||
panelWidth = Math.min(maxPanelWidth, Math.max(this.partLayoutInfo.panel.minWidth, this.panelWidth));
|
||||
} else {
|
||||
panelWidth = (this.workbenchSize.width - activityBarSize.width - sidebarSize.width) * DEFAULT_PANEL_SIZE_COEFFICIENT;
|
||||
}
|
||||
|
||||
if (options && options.toggleMaximizedPanel) {
|
||||
panelWidth = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minWidth, Math.min(this.panelSizeBeforeMaximized, maxPanelWidth)) : maxPanelWidth;
|
||||
}
|
||||
|
||||
this.panelMaximized = panelWidth === maxPanelWidth;
|
||||
if (panelWidth / maxPanelWidth < PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY) {
|
||||
this.panelSizeBeforeMaximized = panelWidth;
|
||||
}
|
||||
}
|
||||
|
||||
this.storageService.store(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL);
|
||||
|
||||
const panelDimension = new Dimension(panelWidth, panelHeight);
|
||||
|
||||
// Editor
|
||||
let editorSize = {
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
|
||||
editorSize.width = this.workbenchSize.width - sidebarSize.width - activityBarSize.width - (panelPosition === Position.RIGHT ? panelDimension.width : 0);
|
||||
editorSize.height = sidebarSize.height - (panelPosition === Position.BOTTOM ? panelDimension.height : 0);
|
||||
|
||||
// Adjust for Editor Part minimum width
|
||||
const minimumEditorPartSize = new Dimension(this.parts.editor.minimumWidth, this.parts.editor.minimumHeight);
|
||||
if (editorSize.width < minimumEditorPartSize.width) {
|
||||
const missingPreferredEditorWidth = minimumEditorPartSize.width - editorSize.width;
|
||||
let outstandingMissingPreferredEditorWidth = missingPreferredEditorWidth;
|
||||
|
||||
// Take from Panel if Panel Position on the Right and Visible
|
||||
if (!isPanelHidden && panelPosition === Position.RIGHT && (!options || options.source !== Parts.PANEL_PART)) {
|
||||
const oldPanelWidth = panelDimension.width;
|
||||
panelDimension.width = Math.max(this.partLayoutInfo.panel.minWidth, panelDimension.width - outstandingMissingPreferredEditorWidth);
|
||||
outstandingMissingPreferredEditorWidth -= oldPanelWidth - panelDimension.width;
|
||||
}
|
||||
|
||||
// Take from Sidebar if Visible
|
||||
if (!isSidebarHidden && outstandingMissingPreferredEditorWidth > 0) {
|
||||
const oldSidebarWidth = sidebarSize.width;
|
||||
sidebarSize.width = Math.max(this.partLayoutInfo.sidebar.minWidth, sidebarSize.width - outstandingMissingPreferredEditorWidth);
|
||||
outstandingMissingPreferredEditorWidth -= oldSidebarWidth - sidebarSize.width;
|
||||
}
|
||||
|
||||
editorSize.width += missingPreferredEditorWidth - outstandingMissingPreferredEditorWidth;
|
||||
if (!isPanelHidden && panelPosition === Position.BOTTOM) {
|
||||
panelDimension.width = editorSize.width; // ensure panel width is always following editor width
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust for Editor Part minimum height
|
||||
if (editorSize.height < minimumEditorPartSize.height) {
|
||||
const missingPreferredEditorHeight = minimumEditorPartSize.height - editorSize.height;
|
||||
let outstandingMissingPreferredEditorHeight = missingPreferredEditorHeight;
|
||||
|
||||
// Take from Panel if Panel Position on the Bottom and Visible
|
||||
if (!isPanelHidden && panelPosition === Position.BOTTOM) {
|
||||
const oldPanelHeight = panelDimension.height;
|
||||
panelDimension.height = Math.max(this.partLayoutInfo.panel.minHeight, panelDimension.height - outstandingMissingPreferredEditorHeight);
|
||||
outstandingMissingPreferredEditorHeight -= oldPanelHeight - panelDimension.height;
|
||||
}
|
||||
|
||||
editorSize.height += missingPreferredEditorHeight - outstandingMissingPreferredEditorHeight;
|
||||
}
|
||||
|
||||
if (!isSidebarHidden) {
|
||||
this.sidebarWidth = sidebarSize.width;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
if (!isPanelHidden) {
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
this.panelHeight = panelDimension.height;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
|
||||
} else {
|
||||
this.panelWidth = panelDimension.width;
|
||||
this.storageService.store(WorkbenchLegacyLayout.sashXTwoWidthSettingsKey, this.panelWidth, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
// Workbench
|
||||
position(this.workbenchContainer, 0, 0, 0, 0, 'relative');
|
||||
size(this.workbenchContainer, this.workbenchSize.width, this.workbenchSize.height);
|
||||
|
||||
// Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case.
|
||||
// uses set time to ensure this happens in th next frame (RAF will be at the end of this JS time slice and we don't want that)
|
||||
setTimeout(() => {
|
||||
const workbenchContainer = this.workbenchContainer;
|
||||
if (workbenchContainer.scrollTop > 0) {
|
||||
workbenchContainer.scrollTop = 0;
|
||||
}
|
||||
if (workbenchContainer.scrollLeft > 0) {
|
||||
workbenchContainer.scrollLeft = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Title Part
|
||||
const titleContainer = this.parts.titlebar.getContainer();
|
||||
if (isTitlebarHidden) {
|
||||
hide(titleContainer);
|
||||
} else {
|
||||
show(titleContainer);
|
||||
}
|
||||
|
||||
// Editor Part and Panel part
|
||||
const editorContainer = this.parts.editor.getContainer();
|
||||
const panelContainer = this.parts.panel.getContainer();
|
||||
size(editorContainer, editorSize.width, editorSize.height);
|
||||
size(panelContainer, panelDimension.width, panelDimension.height);
|
||||
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
if (sidebarPosition === Position.LEFT) {
|
||||
position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
|
||||
position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
|
||||
} else {
|
||||
position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
|
||||
position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
|
||||
}
|
||||
} else {
|
||||
if (sidebarPosition === Position.LEFT) {
|
||||
position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
|
||||
position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
|
||||
} else {
|
||||
position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
|
||||
position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
|
||||
}
|
||||
}
|
||||
|
||||
// Activity Bar Part
|
||||
const activitybarContainer = this.parts.activitybar.getContainer();
|
||||
size(activitybarContainer, null, activityBarSize.height);
|
||||
if (sidebarPosition === Position.LEFT) {
|
||||
this.parts.activitybar.getContainer().style.right = '';
|
||||
position(activitybarContainer, this.titlebarHeight, undefined, 0, 0);
|
||||
} else {
|
||||
this.parts.activitybar.getContainer().style.left = '';
|
||||
position(activitybarContainer, this.titlebarHeight, 0, 0, undefined);
|
||||
}
|
||||
if (isActivityBarHidden) {
|
||||
hide(activitybarContainer);
|
||||
} else {
|
||||
show(activitybarContainer);
|
||||
}
|
||||
|
||||
// Sidebar Part
|
||||
const sidebarContainer = this.parts.sidebar.getContainer();
|
||||
size(sidebarContainer, sidebarSize.width, sidebarSize.height);
|
||||
const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
|
||||
if (sidebarPosition === Position.LEFT) {
|
||||
position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
|
||||
} else {
|
||||
position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
|
||||
}
|
||||
|
||||
// Statusbar Part
|
||||
const statusbarContainer = this.parts.statusbar.getContainer();
|
||||
position(statusbarContainer, this.workbenchSize.height - this.statusbarHeight);
|
||||
if (isStatusbarHidden) {
|
||||
hide(statusbarContainer);
|
||||
} else {
|
||||
show(statusbarContainer);
|
||||
}
|
||||
|
||||
// Sashes
|
||||
this.sashXOne.layout();
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
this.sashXTwo.hide();
|
||||
this.sashY.layout();
|
||||
this.sashY.show();
|
||||
} else {
|
||||
this.sashY.hide();
|
||||
this.sashXTwo.layout();
|
||||
this.sashXTwo.show();
|
||||
}
|
||||
|
||||
// Propagate to Part Layouts
|
||||
this.parts.titlebar.layout(this.workbenchSize.width, this.titlebarHeight, -1);
|
||||
this.parts.editor.layout(editorSize.width, editorSize.height, -1);
|
||||
this.parts.sidebar.layout(sidebarSize.width, sidebarSize.height, -1);
|
||||
this.parts.panel.layout(panelDimension.width, panelDimension.height, -1);
|
||||
this.parts.activitybar.layout(activityBarSize.width, activityBarSize.height, -1);
|
||||
|
||||
// Propagate to Context View
|
||||
this.contextViewService.layout();
|
||||
}
|
||||
|
||||
getVerticalSashTop(sash: Sash): number {
|
||||
return this.titlebarHeight;
|
||||
}
|
||||
|
||||
getVerticalSashLeft(sash: Sash): number {
|
||||
let sidebarPosition = this.layoutService.getSideBarPosition();
|
||||
if (sash === this.sashXOne) {
|
||||
|
||||
if (sidebarPosition === Position.LEFT) {
|
||||
return this.sidebarWidth + this.activitybarWidth;
|
||||
}
|
||||
|
||||
return this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth;
|
||||
}
|
||||
|
||||
return this.workbenchSize.width - this.panelWidth - (sidebarPosition === Position.RIGHT ? this.sidebarWidth + this.activitybarWidth : 0);
|
||||
}
|
||||
|
||||
getVerticalSashHeight(sash: Sash): number {
|
||||
if (sash === this.sashXTwo && !this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.sidebarHeight;
|
||||
}
|
||||
|
||||
getHorizontalSashTop(sash: Sash): number {
|
||||
const offset = 2; // Horizontal sash should be a bit lower than the editor area, thus add 2px #5524
|
||||
return offset + (this.layoutService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.titlebarHeight : this.sidebarHeight + this.titlebarHeight);
|
||||
}
|
||||
|
||||
getHorizontalSashLeft(sash: Sash): number {
|
||||
if (this.layoutService.getSideBarPosition() === Position.RIGHT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.sidebarWidth + this.activitybarWidth;
|
||||
}
|
||||
|
||||
getHorizontalSashWidth(sash: Sash): number {
|
||||
return this.panelWidth;
|
||||
}
|
||||
|
||||
isPanelMaximized(): boolean {
|
||||
return this.panelMaximized;
|
||||
}
|
||||
|
||||
resizePart(part: Parts, sizeChange: number): void {
|
||||
const panelPosition = this.layoutService.getPanelPosition();
|
||||
const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100);
|
||||
const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100);
|
||||
|
||||
let doLayout = false;
|
||||
switch (part) {
|
||||
case Parts.SIDEBAR_PART:
|
||||
this.sidebarWidth = this.sidebarWidth + sizeChangePxWidth; // Sidebar can not become smaller than MIN_PART_WIDTH
|
||||
|
||||
if (this.workbenchSize.width - this.sidebarWidth < this.parts.editor.minimumWidth) {
|
||||
this.sidebarWidth = this.workbenchSize.width - this.parts.editor.minimumWidth;
|
||||
}
|
||||
|
||||
doLayout = true;
|
||||
break;
|
||||
case Parts.PANEL_PART:
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
this.panelHeight = this.panelHeight + sizeChangePxHeight;
|
||||
} else if (panelPosition === Position.RIGHT) {
|
||||
this.panelWidth = this.panelWidth + sizeChangePxWidth;
|
||||
}
|
||||
|
||||
doLayout = true;
|
||||
break;
|
||||
case Parts.EDITOR_PART:
|
||||
// If we have one editor we can cheat and resize sidebar with the negative delta
|
||||
// If the sidebar is not visible and panel is, resize panel main axis with negative Delta
|
||||
if (this.editorGroupService.count === 1) {
|
||||
if (this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
this.sidebarWidth = this.sidebarWidth - sizeChangePxWidth;
|
||||
doLayout = true;
|
||||
} else if (this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
if (panelPosition === Position.BOTTOM) {
|
||||
this.panelHeight = this.panelHeight - sizeChangePxHeight;
|
||||
} else if (panelPosition === Position.RIGHT) {
|
||||
this.panelWidth = this.panelWidth - sizeChangePxWidth;
|
||||
}
|
||||
doLayout = true;
|
||||
}
|
||||
} else {
|
||||
const activeGroup = this.editorGroupService.activeGroup;
|
||||
|
||||
const activeGroupSize = this.editorGroupService.getSize(activeGroup);
|
||||
this.editorGroupService.setSize(activeGroup, activeGroupSize + sizeChangePxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (doLayout) {
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,14 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-workbench > .part {
|
||||
position: absolute; /* only position absolute when grid is not used (TODO@sbatten TODO@ben remove eventually) */
|
||||
}
|
||||
|
||||
.monaco-workbench .part {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-workbench .part > .title {
|
||||
display: none; /* Parts have to opt in to show title area */
|
||||
}
|
||||
|
||||
222
src/vs/workbench/browser/media/style.css
Normal file
222
src/vs/workbench/browser/media/style.css
Normal file
@@ -0,0 +1,222 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Font Families (with CJK support) */
|
||||
|
||||
.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; }
|
||||
.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; }
|
||||
.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; }
|
||||
.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; }
|
||||
|
||||
.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; }
|
||||
.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; }
|
||||
.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; }
|
||||
.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Meiryo", sans-serif; }
|
||||
.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; }
|
||||
|
||||
.linux { font-family: "Ubuntu", "Droid Sans", sans-serif; }
|
||||
.linux:lang(zh-Hans) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; }
|
||||
.linux:lang(zh-Hant) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; }
|
||||
.linux:lang(ja) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; }
|
||||
.linux:lang(ko) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; }
|
||||
|
||||
.mac { --monaco-monospace-font: Monaco, Menlo, Inconsolata, "Courier New", monospace; }
|
||||
.windows { --monaco-monospace-font: Consolas, Inconsolata, "Courier New", monospace; }
|
||||
.linux { --monaco-monospace-font: "Droid Sans Mono", Inconsolata, "Courier New", monospace, "Droid Sans Fallback"; }
|
||||
|
||||
/* Global Styles */
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-size: 11px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||
|
||||
.monaco-workbench {
|
||||
font-size: 13px;
|
||||
line-height: 1.4em;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monaco-workbench img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.monaco-workbench a:active {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench a.plain {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.monaco-workbench a.plain:hover,
|
||||
.monaco-workbench a.plain.hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.monaco-workbench input {
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.monaco-workbench select {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.monaco-workbench .pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench.monaco-font-aliasing-antialiased {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.monaco-workbench.monaco-font-aliasing-none {
|
||||
-webkit-font-smoothing: none;
|
||||
}
|
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
|
||||
.monaco-workbench.monaco-font-aliasing-auto {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-workbench .context-view {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical {
|
||||
padding: .5em 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-menu-item {
|
||||
height: 1.8em;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator),
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .keybinding {
|
||||
font-size: inherit;
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .menu-item-check {
|
||||
font-size: inherit;
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .action-label.separator {
|
||||
font-size: inherit;
|
||||
padding: 0.2em 0 0 0;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.monaco-workbench.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
|
||||
font-size: 60%;
|
||||
padding: 0 1.8em;
|
||||
}
|
||||
|
||||
.monaco-workbench.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator {
|
||||
height: 100%;
|
||||
-webkit-mask-size: 10px 10px;
|
||||
mask-size: 10px 10px;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-menu .action-item {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* START Keyboard Focus Indication Styles */
|
||||
|
||||
.monaco-workbench [tabindex="0"]:focus,
|
||||
.monaco-workbench .synthetic-focus,
|
||||
.monaco-workbench select:focus,
|
||||
.monaco-workbench input[type="button"]:focus,
|
||||
.monaco-workbench input[type="text"]:focus,
|
||||
.monaco-workbench textarea:focus,
|
||||
.monaco-workbench input[type="checkbox"]:focus {
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.monaco-workbench [tabindex="0"]:active,
|
||||
.monaco-workbench select:active,
|
||||
.monaco-workbench input[type="button"]:active,
|
||||
.monaco-workbench input[type="checkbox"]:active,
|
||||
.monaco-workbench .monaco-tree .monaco-tree-row
|
||||
.monaco-workbench .monaco-tree.focused.no-focused-item:active:before {
|
||||
outline: 0 !important; /* fixes some flashing outlines from showing up when clicking */
|
||||
}
|
||||
|
||||
.monaco-workbench.mac select:focus {
|
||||
border-color: transparent; /* outline is a square, but border has a radius, so we avoid this glitch when focused (https://github.com/Microsoft/vscode/issues/26045) */
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused .monaco-tree-row.focused [tabindex="0"]:focus {
|
||||
outline-width: 1px; /* higher contrast color for focusable elements in a row that shows focus feedback */
|
||||
outline-style: solid;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused.no-focused-item:focus:before,
|
||||
.monaco-workbench .monaco-list:not(.element-focused):focus:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 5; /* make sure we are on top of the tree items */
|
||||
content: "";
|
||||
pointer-events: none; /* enable click through */
|
||||
outline: 1px solid; /* we still need to handle the empty tree or no focus item case */
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.monaco-workbench .synthetic-focus :focus {
|
||||
outline: 0 !important; /* elements within widgets that draw synthetic-focus should never show focus */
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-inputbox.info.synthetic-focus,
|
||||
.monaco-workbench .monaco-inputbox.warning.synthetic-focus,
|
||||
.monaco-workbench .monaco-inputbox.error.synthetic-focus,
|
||||
.monaco-workbench .monaco-inputbox.info input[type="text"]:focus,
|
||||
.monaco-workbench .monaco-inputbox.warning input[type="text"]:focus,
|
||||
.monaco-workbench .monaco-inputbox.error input[type="text"]:focus {
|
||||
outline: 0 !important; /* outline is not going well with decoration */
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused:focus,
|
||||
.monaco-workbench .monaco-list:focus {
|
||||
outline: 0 !important; /* tree indicates focus not via outline but through the focused item */
|
||||
}
|
||||
56
src/vs/workbench/browser/nodeless.main.ts
Normal file
56
src/vs/workbench/browser/nodeless.main.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { domContentLoaded, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { SimpleLogService } from 'vs/workbench/browser/nodeless.simpleservices';
|
||||
import { Workbench } from 'vs/workbench/browser/workbench';
|
||||
|
||||
class CodeRendererMain extends Disposable {
|
||||
|
||||
private workbench: Workbench;
|
||||
|
||||
open(): Promise<void> {
|
||||
const services = this.initServices();
|
||||
|
||||
return domContentLoaded().then(() => {
|
||||
mark('willStartWorkbench');
|
||||
|
||||
// Create Workbench
|
||||
this.workbench = new Workbench(
|
||||
document.body,
|
||||
services.serviceCollection,
|
||||
services.logService
|
||||
);
|
||||
|
||||
// Layout
|
||||
this._register(addDisposableListener(window, EventType.RESIZE, () => this.workbench.layout()));
|
||||
|
||||
// Workbench Lifecycle
|
||||
this._register(this.workbench.onShutdown(() => this.dispose()));
|
||||
|
||||
// Startup
|
||||
this.workbench.startup();
|
||||
});
|
||||
}
|
||||
|
||||
private initServices(): { serviceCollection: ServiceCollection, logService: ILogService } {
|
||||
const serviceCollection = new ServiceCollection();
|
||||
|
||||
const logService = new SimpleLogService();
|
||||
serviceCollection.set(ILogService, logService);
|
||||
|
||||
return { serviceCollection, logService };
|
||||
}
|
||||
}
|
||||
|
||||
export function main(): Promise<void> {
|
||||
const renderer = new CodeRendererMain();
|
||||
|
||||
return renderer.open();
|
||||
}
|
||||
1834
src/vs/workbench/browser/nodeless.simpleservices.ts
Normal file
1834
src/vs/workbench/browser/nodeless.simpleservices.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ import { IPanel } from 'vs/workbench/common/panel';
|
||||
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
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';
|
||||
|
||||
@@ -61,6 +61,13 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
|
||||
getDefaultPanelId(): string {
|
||||
return this.defaultPanelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if a panel exists with the provided ID.
|
||||
*/
|
||||
hasPanel(id: string): boolean {
|
||||
return this.getPanels().some(panel => panel.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,40 +75,37 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
|
||||
*/
|
||||
export abstract class TogglePanelAction extends Action {
|
||||
|
||||
private panelId: string;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
panelId: string,
|
||||
private readonly panelId: string,
|
||||
protected panelService: IPanelService,
|
||||
private partService: IPartService,
|
||||
private layoutService: IWorkbenchLayoutService,
|
||||
cssClass?: string
|
||||
) {
|
||||
super(id, label, cssClass);
|
||||
this.panelId = panelId;
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
if (this.isPanelFocused()) {
|
||||
this.partService.setPanelHidden(true);
|
||||
this.layoutService.setPanelHidden(true);
|
||||
} else {
|
||||
this.panelService.openPanel(this.panelId, true);
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private isPanelActive(): boolean {
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
|
||||
return activePanel && activePanel.getId() === this.panelId;
|
||||
return !!activePanel && activePanel.getId() === this.panelId;
|
||||
}
|
||||
|
||||
private isPanelFocused(): boolean {
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
return !!(this.isPanelActive() && activeElement && isAncestor(activeElement, this.partService.getContainer(Parts.PANEL_PART)));
|
||||
return !!(this.isPanelActive() && activeElement && isAncestor(activeElement, this.layoutService.getContainer(Parts.PANEL_PART)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,26 @@ import { Component } from 'vs/workbench/common/component';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Dimension, size } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IDimension } from 'vs/platform/layout/browser/layoutService';
|
||||
import { ISerializableView, Orientation } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
export interface IPartOptions {
|
||||
hasTitle?: boolean;
|
||||
borderWidth?: () => number;
|
||||
}
|
||||
|
||||
export interface ILayoutContentResult {
|
||||
titleSize: IDimension;
|
||||
contentSize: IDimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parts are layed out in the workbench and have their own layout that
|
||||
* arranges an optional title and mandatory content area to show content.
|
||||
*/
|
||||
export abstract class Part extends Component {
|
||||
export abstract class Part extends Component implements ISerializableView {
|
||||
private parent: HTMLElement;
|
||||
private titleArea: HTMLElement | null;
|
||||
private contentArea: HTMLElement | null;
|
||||
@@ -28,9 +37,12 @@ export abstract class Part extends Component {
|
||||
id: string,
|
||||
private options: IPartOptions,
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
storageService: IStorageService,
|
||||
layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, themeService, storageService);
|
||||
|
||||
layoutService.registerPart(this);
|
||||
}
|
||||
|
||||
protected onThemeChange(theme: ITheme): void {
|
||||
@@ -41,18 +53,22 @@ export abstract class Part extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Clients should not call this method, the workbench calls this
|
||||
* method. Calling it otherwise may result in unexpected behavior.
|
||||
*
|
||||
* Called to create title and content area of the part.
|
||||
*/
|
||||
create(parent: HTMLElement): void {
|
||||
create(parent: HTMLElement, options?: object): void {
|
||||
this.parent = parent;
|
||||
this.titleArea = this.createTitleArea(parent);
|
||||
this.contentArea = this.createContentArea(parent);
|
||||
this.titleArea = this.createTitleArea(parent, options);
|
||||
this.contentArea = this.createContentArea(parent, options);
|
||||
|
||||
this.partLayout = new PartLayout(this.parent, this.options, this.titleArea, this.contentArea);
|
||||
this.partLayout = new PartLayout(this.options, this.contentArea);
|
||||
|
||||
this.updateStyles();
|
||||
}
|
||||
@@ -67,7 +83,7 @@ export abstract class Part extends Component {
|
||||
/**
|
||||
* Subclasses override to provide a title area implementation.
|
||||
*/
|
||||
protected createTitleArea(parent: HTMLElement): HTMLElement | null {
|
||||
protected createTitleArea(parent: HTMLElement, options?: object): HTMLElement | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -81,7 +97,7 @@ export abstract class Part extends Component {
|
||||
/**
|
||||
* Subclasses override to provide a content area implementation.
|
||||
*/
|
||||
protected createContentArea(parent: HTMLElement): HTMLElement | null {
|
||||
protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,22 +111,35 @@ export abstract class Part extends Component {
|
||||
/**
|
||||
* Layout title and content area in the given dimension.
|
||||
*/
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
return this.partLayout.layout(dimension);
|
||||
protected layoutContents(width: number, height: number): ILayoutContentResult {
|
||||
return this.partLayout.layout(width, height);
|
||||
}
|
||||
|
||||
//#region ISerializableView
|
||||
|
||||
private _onDidChange = this._register(new Emitter<{ width: number; height: number; }>());
|
||||
get onDidChange(): Event<{ width: number, height: number }> { return this._onDidChange.event; }
|
||||
|
||||
element: HTMLElement;
|
||||
|
||||
abstract minimumWidth: number;
|
||||
abstract maximumWidth: number;
|
||||
abstract minimumHeight: number;
|
||||
abstract maximumHeight: number;
|
||||
|
||||
abstract layout(width: number, height: number, orientation: Orientation): void;
|
||||
abstract toJSON(): object;
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
export class PartLayout {
|
||||
class PartLayout {
|
||||
|
||||
private static readonly TITLE_HEIGHT = 35;
|
||||
|
||||
constructor(container: HTMLElement, private options: IPartOptions, titleArea: HTMLElement | null, private contentArea: HTMLElement | null) { }
|
||||
constructor(private options: IPartOptions, private contentArea: HTMLElement | null) { }
|
||||
|
||||
layout(dimension: Dimension): Dimension[] {
|
||||
const { width, height } = dimension;
|
||||
|
||||
// Return the applied sizes to title and content
|
||||
const sizes: Dimension[] = [];
|
||||
layout(width: number, height: number): ILayoutContentResult {
|
||||
|
||||
// Title Size: Width (Fill), Height (Variable)
|
||||
let titleSize: Dimension;
|
||||
@@ -127,14 +156,11 @@ export class PartLayout {
|
||||
contentSize.width -= this.options.borderWidth(); // adjust for border size
|
||||
}
|
||||
|
||||
sizes.push(titleSize);
|
||||
sizes.push(contentSize);
|
||||
|
||||
// Content
|
||||
if (this.contentArea) {
|
||||
size(this.contentArea, contentSize.width, contentSize.height);
|
||||
}
|
||||
|
||||
return sizes;
|
||||
return { titleSize, contentSize };
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import 'vs/css!./media/activityaction';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
@@ -24,8 +23,8 @@ import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { IActivity, IGlobalActivity } from 'vs/workbench/common/activity';
|
||||
import { ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IActivityService } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
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';
|
||||
|
||||
export class ViewletActivityAction extends ActivityAction {
|
||||
@@ -37,7 +36,7 @@ export class ViewletActivityAction extends ActivityAction {
|
||||
constructor(
|
||||
activity: IActivity,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService
|
||||
) {
|
||||
super(activity);
|
||||
@@ -55,14 +54,14 @@ export class ViewletActivityAction extends ActivityAction {
|
||||
}
|
||||
this.lastRun = now;
|
||||
|
||||
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
|
||||
const sideBarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART);
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
|
||||
// Hide sidebar if selected viewlet already visible
|
||||
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) {
|
||||
this.logAction('hide');
|
||||
this.partService.setSideBarHidden(true);
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.logAction('show');
|
||||
@@ -84,20 +83,20 @@ export class ToggleViewletAction extends Action {
|
||||
|
||||
constructor(
|
||||
private _viewlet: ViewletDescriptor,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IViewletService private readonly viewletService: IViewletService
|
||||
) {
|
||||
super(_viewlet.id, _viewlet.name);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
|
||||
const sideBarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART);
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
|
||||
// Hide sidebar if selected viewlet already visible
|
||||
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) {
|
||||
this.partService.setSideBarHidden(true);
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.viewletService.openViewlet(this._viewlet.id, true);
|
||||
@@ -130,32 +129,29 @@ export class GlobalActivityActionItem extends ActivityActionItem {
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
this.showContextMenu({ x: event.posx, y: event.posy });
|
||||
this.showContextMenu();
|
||||
}));
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
|
||||
let event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
this.showContextMenu(this.container);
|
||||
this.showContextMenu();
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
|
||||
const event = new StandardMouseEvent(e);
|
||||
this.showContextMenu({ x: event.posx, y: event.posy });
|
||||
this.showContextMenu();
|
||||
}));
|
||||
}
|
||||
|
||||
private showContextMenu(location: HTMLElement | { x: number, y: number }): void {
|
||||
private showContextMenu(): void {
|
||||
const globalAction = this._action as GlobalActivityAction;
|
||||
const activity = globalAction.activity as IGlobalActivity;
|
||||
const actions = activity.getActions();
|
||||
const containerPosition = DOM.getDomNodePagePosition(this.container);
|
||||
const location = { x: containerPosition.left + containerPosition.width / 2, y: containerPosition.top };
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => location,
|
||||
@@ -170,10 +166,10 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction {
|
||||
constructor(
|
||||
id: string, iconUrl: URI,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService
|
||||
) {
|
||||
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, partService, telemetryService);
|
||||
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: url('${iconUrl || ''}') no-repeat 50% 50%`);
|
||||
@@ -201,19 +197,19 @@ class SwitchSideBarViewAction extends Action {
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IActivityService private readonly activityService: IActivityService
|
||||
@IActivityBarService private readonly activityBarService: IActivityBarService
|
||||
) {
|
||||
super(id, name);
|
||||
}
|
||||
|
||||
run(offset: number): Promise<any> {
|
||||
const pinnedViewletIds = this.activityService.getPinnedViewletIds();
|
||||
const pinnedViewletIds = this.activityBarService.getPinnedViewletIds();
|
||||
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
if (!activeViewlet) {
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
let targetViewletId: string;
|
||||
let targetViewletId: string | undefined;
|
||||
for (let i = 0; i < pinnedViewletIds.length; i++) {
|
||||
if (pinnedViewletIds[i] === activeViewlet.getId()) {
|
||||
targetViewletId = pinnedViewletIds[(i + pinnedViewletIds.length + offset) % pinnedViewletIds.length];
|
||||
@@ -233,9 +229,9 @@ export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IActivityService activityService: IActivityService
|
||||
@IActivityBarService activityBarService: IActivityBarService
|
||||
) {
|
||||
super(id, name, viewletService, activityService);
|
||||
super(id, name, viewletService, activityBarService);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
@@ -252,9 +248,9 @@ export class NextSideBarViewAction extends SwitchSideBarViewAction {
|
||||
id: string,
|
||||
name: string,
|
||||
@IViewletService viewletService: IViewletService,
|
||||
@IActivityService activityService: IActivityService
|
||||
@IActivityBarService activityBarService: IActivityBarService
|
||||
) {
|
||||
super(id, name, viewletService, activityService);
|
||||
super(id, name, viewletService, activityBarService);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import 'vs/css!./media/activitybarpart';
|
||||
import * as nls from 'vs/nls';
|
||||
import { illegalArgument } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -14,8 +13,8 @@ import { Part } from 'vs/workbench/browser/part';
|
||||
import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewletActivityAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
@@ -32,25 +31,34 @@ import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExte
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
|
||||
const SCM_VIEWLET_ID = 'workbench.view.scm';
|
||||
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
interface ICachedViewlet {
|
||||
id: string;
|
||||
iconUrl: UriComponents;
|
||||
iconUrl?: UriComponents;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
order?: number;
|
||||
visible: boolean;
|
||||
views?: { when: string }[];
|
||||
views?: { when?: string }[];
|
||||
}
|
||||
|
||||
export class ActivitybarPart extends Part implements ISerializableView {
|
||||
export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static readonly ACTION_HEIGHT = 50;
|
||||
private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets';
|
||||
|
||||
private dimension: Dimension;
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 50;
|
||||
readonly maximumWidth: number = 50;
|
||||
readonly minimumHeight: number = 0;
|
||||
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
|
||||
//#endregion
|
||||
|
||||
private globalActionBar: ActionBar;
|
||||
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; } = Object.create(null);
|
||||
@@ -59,27 +67,17 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
|
||||
|
||||
element: HTMLElement;
|
||||
minimumWidth: number = 50;
|
||||
maximumWidth: number = 50;
|
||||
minimumHeight: number = 0;
|
||||
maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
@IViewsService private readonly viewsService: IViewsService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
this.cachedViewlets = this.getCachedViewlets();
|
||||
for (const cachedViewlet of this.cachedViewlets) {
|
||||
@@ -97,9 +95,9 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)),
|
||||
getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))],
|
||||
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
|
||||
hidePart: () => this.partService.setSideBarHidden(true),
|
||||
hidePart: () => this.layoutService.setSideBarHidden(true),
|
||||
compositeSize: 50,
|
||||
colors: theme => this.getActivitybarItemColors(theme),
|
||||
colors: (theme: ITheme) => this.getActivitybarItemColors(theme),
|
||||
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
|
||||
}));
|
||||
|
||||
@@ -121,6 +119,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Viewlet registration
|
||||
this._register(this.viewletService.onDidViewletRegister(viewlet => this.onDidRegisterViewlets([viewlet])));
|
||||
this._register(this.viewletService.onDidViewletDeregister(({ id }) => this.removeComposite(id, true)));
|
||||
|
||||
@@ -130,6 +129,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
// Deactivate viewlet action on close
|
||||
this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId())));
|
||||
|
||||
// Extension registration
|
||||
let disposables: IDisposable[] = [];
|
||||
this._register(this.extensionService.onDidRegisterExtensions(() => {
|
||||
disposables = dispose(disposables);
|
||||
@@ -137,15 +137,17 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
this.compositeBar.onDidChange(() => this.saveCachedViewlets(), this, disposables);
|
||||
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
|
||||
}));
|
||||
|
||||
this._register(toDisposable(() => dispose(disposables)));
|
||||
}
|
||||
|
||||
private onDidRegisterExtensions(): void {
|
||||
this.removeNotExistingComposites();
|
||||
|
||||
for (const viewlet of this.viewletService.getViewlets()) {
|
||||
this.enableCompositeActions(viewlet);
|
||||
const viewContainer = this.getViewContainer(viewlet.id);
|
||||
if (viewContainer) {
|
||||
if (viewContainer && viewContainer.hideIfEmpty) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors) {
|
||||
this.onDidChangeActiveViews(viewlet, viewDescriptors);
|
||||
@@ -153,6 +155,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.saveCachedViewlets();
|
||||
}
|
||||
|
||||
@@ -165,16 +168,21 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
}
|
||||
|
||||
private onDidViewletOpen(viewlet: IViewlet): void {
|
||||
|
||||
// Update the composite bar by adding
|
||||
this.compositeBar.addComposite(this.viewletService.getViewlet(viewlet.getId()));
|
||||
const foundViewlet = this.viewletService.getViewlet(viewlet.getId());
|
||||
if (foundViewlet) {
|
||||
this.compositeBar.addComposite(foundViewlet);
|
||||
}
|
||||
this.compositeBar.activateComposite(viewlet.getId());
|
||||
const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId());
|
||||
const viewContainer = this.getViewContainer(viewletDescriptor.id);
|
||||
if (viewContainer) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
|
||||
// Update the composite bar by hiding
|
||||
this.removeComposite(viewletDescriptor.id, true);
|
||||
if (viewletDescriptor) {
|
||||
const viewContainer = this.getViewContainer(viewletDescriptor.id);
|
||||
if (viewContainer && viewContainer.hideIfEmpty) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
|
||||
this.removeComposite(viewletDescriptor.id, true); // Update the composite bar by hiding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +238,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
container.style.backgroundColor = background;
|
||||
|
||||
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder);
|
||||
const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
|
||||
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
|
||||
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : null;
|
||||
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
|
||||
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
|
||||
@@ -247,7 +255,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
|
||||
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
|
||||
dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND),
|
||||
activeBackgroundColor: null, inactiveBackgroundColor: null, activeBorderBottomColor: null,
|
||||
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,7 +267,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
.map(a => new GlobalActivityAction(a));
|
||||
|
||||
this.globalActionBar = this._register(new ActionBar(container, {
|
||||
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, theme => this.getActivitybarItemColors(theme)),
|
||||
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)),
|
||||
orientation: ActionsOrientation.VERTICAL,
|
||||
ariaLabel: nls.localize('globalActions', "Global Actions"),
|
||||
animated: false
|
||||
@@ -283,7 +291,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
} else {
|
||||
const cachedComposite = this.cachedViewlets.filter(c => c.id === compositeId)[0];
|
||||
compositeActions = {
|
||||
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite ? URI.revive(cachedComposite.iconUrl) : undefined),
|
||||
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite && cachedComposite.iconUrl ? URI.revive(cachedComposite.iconUrl) : undefined),
|
||||
pinnedAction: new PlaceHolderToggleCompositePinnedAction(compositeId, this.compositeBar)
|
||||
};
|
||||
}
|
||||
@@ -316,8 +324,12 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
}
|
||||
|
||||
private shouldBeHidden(viewletId: string, cachedViewlet: ICachedViewlet): boolean {
|
||||
const viewContainer = this.getViewContainer(viewletId);
|
||||
if (!viewContainer || !viewContainer.hideIfEmpty) {
|
||||
return false;
|
||||
}
|
||||
return cachedViewlet && cachedViewlet.views && cachedViewlet.views.length
|
||||
? cachedViewlet.views.every(({ when }) => when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when)))
|
||||
? 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 */;
|
||||
}
|
||||
|
||||
@@ -336,6 +348,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
} else {
|
||||
this.compositeBar.removeComposite(compositeId);
|
||||
}
|
||||
|
||||
const compositeActions = this.compositeActions[compositeId];
|
||||
if (compositeActions) {
|
||||
compositeActions.activityAction.dispose();
|
||||
@@ -349,6 +362,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
if (activityAction instanceof PlaceHolderViewletActivityAction) {
|
||||
activityAction.setActivity(viewlet);
|
||||
}
|
||||
|
||||
if (pinnedAction instanceof PlaceHolderToggleCompositePinnedAction) {
|
||||
pinnedAction.setActivity(viewlet);
|
||||
}
|
||||
@@ -356,38 +370,27 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
|
||||
getPinnedViewletIds(): string[] {
|
||||
const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(v => v.id);
|
||||
|
||||
return this.viewletService.getViewlets()
|
||||
.filter(v => this.compositeBar.isPinned(v.id))
|
||||
.sort((v1, v2) => pinnedCompositeIds.indexOf(v1.id) - pinnedCompositeIds.indexOf(v2.id))
|
||||
.map(v => v.id);
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
if (!this.partService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
if (dim1 instanceof Dimension) {
|
||||
return [dim1];
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass to super
|
||||
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2));
|
||||
// Layout contents
|
||||
const contentAreaSize = super.layoutContents(width, height).contentSize;
|
||||
|
||||
this.dimension = sizes[1];
|
||||
|
||||
let availableHeight = this.dimension.height;
|
||||
// Layout composite bar
|
||||
let availableHeight = contentAreaSize.height;
|
||||
if (this.globalActionBar) {
|
||||
// adjust height for global actions showing
|
||||
availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT);
|
||||
}
|
||||
this.compositeBar.layout(new Dimension(dim1 instanceof Dimension ? dim1.width : dim1, availableHeight));
|
||||
|
||||
if (dim1 instanceof Dimension) {
|
||||
return sizes;
|
||||
availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing
|
||||
}
|
||||
this.compositeBar.layout(new Dimension(width, availableHeight));
|
||||
}
|
||||
|
||||
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
|
||||
@@ -425,13 +428,14 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
|
||||
private saveCachedViewlets(): void {
|
||||
const state: ICachedViewlet[] = [];
|
||||
const compositeItems = this.compositeBar.getCompositeBarItems();
|
||||
const allViewlets = this.viewletService.getViewlets();
|
||||
|
||||
const compositeItems = this.compositeBar.getCompositeBarItems();
|
||||
for (const compositeItem of compositeItems) {
|
||||
const viewContainer = this.getViewContainer(compositeItem.id);
|
||||
const viewlet = allViewlets.filter(({ id }) => id === compositeItem.id)[0];
|
||||
if (viewlet) {
|
||||
const views: { when: string }[] = [];
|
||||
const views: { when: string | undefined }[] = [];
|
||||
if (viewContainer) {
|
||||
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
|
||||
if (viewDescriptors) {
|
||||
@@ -440,9 +444,10 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
}
|
||||
}
|
||||
}
|
||||
state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible });
|
||||
state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl && viewlet.iconUrl.scheme === Schemas.file ? viewlet.iconUrl : undefined, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible });
|
||||
}
|
||||
}
|
||||
|
||||
this.cachedViewletsValue = JSON.stringify(state);
|
||||
}
|
||||
|
||||
@@ -453,6 +458,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible;
|
||||
return serialized;
|
||||
});
|
||||
|
||||
for (const old of this.loadOldCachedViewlets()) {
|
||||
const cachedViewlet = cachedViewlets.filter(cached => cached.id === old.id)[0];
|
||||
if (cachedViewlet) {
|
||||
@@ -460,6 +466,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
cachedViewlet.views = old.views;
|
||||
}
|
||||
}
|
||||
|
||||
return cachedViewlets;
|
||||
}
|
||||
|
||||
@@ -467,14 +474,16 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
const previousState = this.storageService.get('workbench.activity.placeholderViewlets', StorageScope.GLOBAL, '[]');
|
||||
const result = (<ICachedViewlet[]>JSON.parse(previousState));
|
||||
this.storageService.remove('workbench.activity.placeholderViewlets', StorageScope.GLOBAL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _cachedViewletsValue: string;
|
||||
private _cachedViewletsValue: string | null;
|
||||
private get cachedViewletsValue(): string {
|
||||
if (!this._cachedViewletsValue) {
|
||||
this._cachedViewletsValue = this.getStoredCachedViewletsValue();
|
||||
}
|
||||
|
||||
return this._cachedViewletsValue;
|
||||
}
|
||||
|
||||
@@ -494,10 +503,6 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
}
|
||||
|
||||
private getViewContainer(viewletId: string): ViewContainer | undefined {
|
||||
// TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054
|
||||
if (viewletId === SCM_VIEWLET_ID) {
|
||||
return null;
|
||||
}
|
||||
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
return viewContainerRegistry.get(viewletId);
|
||||
}
|
||||
@@ -508,3 +513,5 @@ export class ActivitybarPart extends Part implements ISerializableView {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IActivityBarService, ActivitybarPart);
|
||||
@@ -23,9 +23,9 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
export interface ICompositeBarItem {
|
||||
id: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
order?: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
private dimension: Dimension;
|
||||
|
||||
private compositeSwitcherBar: ActionBar;
|
||||
private compositeOverflowAction: CompositeOverflowActivityAction;
|
||||
private compositeOverflowActionItem: CompositeOverflowActivityActionItem;
|
||||
private compositeOverflowAction: CompositeOverflowActivityAction | null;
|
||||
private compositeOverflowActionItem: CompositeOverflowActivityActionItem | null;
|
||||
|
||||
private model: CompositeBarModel;
|
||||
private visibleComposites: string[];
|
||||
@@ -112,7 +112,7 @@ 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;
|
||||
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
|
||||
const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1];
|
||||
@@ -174,7 +174,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
if (this.model.activate(id)) {
|
||||
// Update if current composite is neither visible nor pinned
|
||||
// or previous active composite is not pinned
|
||||
if (this.visibleComposites.indexOf(id) === - 1 || !this.model.activeItem.pinned || (previousActiveItem && !previousActiveItem.pinned)) {
|
||||
if (this.visibleComposites.indexOf(id) === - 1 || (!!this.model.activeItem && !this.model.activeItem.pinned) || (previousActiveItem && !previousActiveItem.pinned)) {
|
||||
this.updateCompositeSwitcher();
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
}
|
||||
}
|
||||
|
||||
getAction(compositeId): ActivityAction {
|
||||
getAction(compositeId: string): ActivityAction {
|
||||
const item = this.model.findItem(compositeId);
|
||||
return item && item.activityAction;
|
||||
}
|
||||
@@ -304,7 +304,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
let size = 0;
|
||||
const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height : this.dimension.width;
|
||||
for (let i = 0; i < compositesToShow.length && size <= limit; i++) {
|
||||
size += this.compositeSizeInBar.get(compositesToShow[i]);
|
||||
size += this.compositeSizeInBar.get(compositesToShow[i])!;
|
||||
if (size > limit) {
|
||||
maxVisible = i;
|
||||
}
|
||||
@@ -312,19 +312,19 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
overflows = compositesToShow.length > maxVisible;
|
||||
|
||||
if (overflows) {
|
||||
size -= this.compositeSizeInBar.get(compositesToShow[maxVisible]);
|
||||
size -= this.compositeSizeInBar.get(compositesToShow[maxVisible])!;
|
||||
compositesToShow = compositesToShow.slice(0, maxVisible);
|
||||
size += this.options.overflowActionSize;
|
||||
}
|
||||
// Check if we need to make extra room for the overflow action
|
||||
if (size > limit) {
|
||||
size -= this.compositeSizeInBar.get(compositesToShow.pop());
|
||||
size -= this.compositeSizeInBar.get(compositesToShow.pop()!)!;
|
||||
}
|
||||
|
||||
// We always try show the active composite
|
||||
if (this.model.activeItem && compositesToShow.every(compositeId => compositeId !== this.model.activeItem.id)) {
|
||||
const removedComposite = compositesToShow.pop();
|
||||
size = size - this.compositeSizeInBar.get(removedComposite) + this.compositeSizeInBar.get(this.model.activeItem.id);
|
||||
if (this.model.activeItem && compositesToShow.every(compositeId => !!this.model.activeItem && compositeId !== this.model.activeItem.id)) {
|
||||
const removedComposite = compositesToShow.pop()!;
|
||||
size = size - this.compositeSizeInBar.get(removedComposite)! + this.compositeSizeInBar.get(this.model.activeItem.id)!;
|
||||
compositesToShow.push(this.model.activeItem.id);
|
||||
}
|
||||
|
||||
@@ -342,7 +342,9 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
this.compositeOverflowAction.dispose();
|
||||
this.compositeOverflowAction = null;
|
||||
|
||||
this.compositeOverflowActionItem.dispose();
|
||||
if (this.compositeOverflowActionItem) {
|
||||
this.compositeOverflowActionItem.dispose();
|
||||
}
|
||||
this.compositeOverflowActionItem = null;
|
||||
}
|
||||
|
||||
@@ -378,7 +380,11 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
|
||||
// Add overflow action as needed
|
||||
if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) {
|
||||
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => this.compositeOverflowActionItem.showMenu());
|
||||
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => {
|
||||
if (this.compositeOverflowActionItem) {
|
||||
this.compositeOverflowActionItem.showMenu();
|
||||
}
|
||||
});
|
||||
this.compositeOverflowActionItem = this.instantiationService.createInstance(
|
||||
CompositeOverflowActivityActionItem,
|
||||
this.compositeOverflowAction,
|
||||
@@ -398,7 +404,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
private getOverflowingComposites(): { id: string, name: string }[] {
|
||||
private getOverflowingComposites(): { id: string, name?: string }[] {
|
||||
let overflowingIds = this.model.visibleItems.filter(item => item.pinned).map(item => item.id);
|
||||
|
||||
// Show the active composite even if it is not pinned
|
||||
@@ -453,7 +459,7 @@ class CompositeBarModel {
|
||||
|
||||
private _items: ICompositeBarModelItem[];
|
||||
private readonly options: ICompositeBarOptions;
|
||||
activeItem: ICompositeBarModelItem;
|
||||
activeItem?: ICompositeBarModelItem;
|
||||
|
||||
constructor(
|
||||
items: ICompositeBarItem[],
|
||||
@@ -507,7 +513,7 @@ class CompositeBarModel {
|
||||
return this.items.filter(item => item.visible && item.pinned);
|
||||
}
|
||||
|
||||
private createCompositeBarItem(id: string, name: string, order: number, pinned: boolean, visible: boolean): ICompositeBarModelItem {
|
||||
private createCompositeBarItem(id: string, name: string | undefined, order: number | undefined, pinned: boolean, visible: boolean): ICompositeBarModelItem {
|
||||
const options = this.options;
|
||||
return {
|
||||
id, name, pinned, order, visible,
|
||||
@@ -521,7 +527,7 @@ class CompositeBarModel {
|
||||
};
|
||||
}
|
||||
|
||||
add(id: string, name: string, order: number): boolean {
|
||||
add(id: string, name: string, order: number | undefined): boolean {
|
||||
const item = this.findItem(id);
|
||||
if (item) {
|
||||
let changed = false;
|
||||
@@ -541,7 +547,7 @@ class CompositeBarModel {
|
||||
this.items.push(item);
|
||||
} else {
|
||||
let index = 0;
|
||||
while (index < this.items.length && this.items[index].order < order) {
|
||||
while (index < this.items.length && typeof this.items[index].order === 'number' && this.items[index].order! < order) {
|
||||
index++;
|
||||
}
|
||||
this.items.splice(index, 0, item);
|
||||
|
||||
@@ -23,7 +23,7 @@ import { Color } from 'vs/base/common/color';
|
||||
|
||||
export interface ICompositeActivity {
|
||||
badge: IBadge;
|
||||
clazz: string;
|
||||
clazz?: string;
|
||||
priority: number;
|
||||
}
|
||||
|
||||
@@ -57,13 +57,11 @@ export class ActivityAction extends Action {
|
||||
private _onDidChangeBadge = new Emitter<this>();
|
||||
get onDidChangeBadge(): Event<this> { return this._onDidChangeBadge.event; }
|
||||
|
||||
private badge: IBadge;
|
||||
private badge?: IBadge;
|
||||
private clazz: string | undefined;
|
||||
|
||||
constructor(private _activity: IActivity) {
|
||||
super(_activity.id, _activity.name, _activity.cssClass);
|
||||
|
||||
this.badge = null;
|
||||
}
|
||||
|
||||
get activity(): IActivity {
|
||||
@@ -87,7 +85,7 @@ export class ActivityAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
getBadge(): IBadge {
|
||||
getBadge(): IBadge | undefined {
|
||||
return this.badge;
|
||||
}
|
||||
|
||||
@@ -95,7 +93,7 @@ export class ActivityAction extends Action {
|
||||
return this.clazz;
|
||||
}
|
||||
|
||||
setBadge(badge: IBadge, clazz?: string): void {
|
||||
setBadge(badge: IBadge | undefined, clazz?: string): void {
|
||||
this.badge = badge;
|
||||
this.clazz = clazz;
|
||||
this._onDidChangeBadge.fire(this);
|
||||
@@ -110,14 +108,14 @@ export class ActivityAction extends Action {
|
||||
}
|
||||
|
||||
export interface ICompositeBarColors {
|
||||
activeBackgroundColor: Color;
|
||||
inactiveBackgroundColor: Color;
|
||||
activeBorderBottomColor: Color;
|
||||
activeForegroundColor: Color;
|
||||
inactiveForegroundColor: Color;
|
||||
badgeBackground: Color;
|
||||
badgeForeground: Color;
|
||||
dragAndDropBackground: Color;
|
||||
activeBackgroundColor?: Color;
|
||||
inactiveBackgroundColor?: Color;
|
||||
activeBorderBottomColor?: Color;
|
||||
activeForegroundColor?: Color;
|
||||
inactiveForegroundColor?: Color;
|
||||
badgeBackground?: Color;
|
||||
badgeForeground?: Color;
|
||||
dragAndDropBackground?: Color;
|
||||
}
|
||||
|
||||
export interface IActivityActionItemOptions extends IBaseActionItemOptions {
|
||||
@@ -207,10 +205,10 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
}));
|
||||
|
||||
// Label
|
||||
this.label = dom.append(this.element, dom.$('a'));
|
||||
this.label = dom.append(this.element!, dom.$('a'));
|
||||
|
||||
// Badge
|
||||
this.badge = dom.append(this.element, dom.$('.badge'));
|
||||
this.badge = dom.append(this.element!, dom.$('.badge'));
|
||||
this.badgeContent = dom.append(this.badge, dom.$('.badge-content'));
|
||||
|
||||
dom.hide(this.badge);
|
||||
@@ -254,9 +252,9 @@ export class ActivityActionItem extends BaseActionItem {
|
||||
const noOfThousands = badge.number / 1000;
|
||||
const floor = Math.floor(noOfThousands);
|
||||
if (noOfThousands > floor) {
|
||||
number = nls.localize('largeNumberBadge1', '{0}k+', floor);
|
||||
number = `${floor}K+`;
|
||||
} else {
|
||||
number = nls.localize('largeNumberBadge2', '{0}k', noOfThousands);
|
||||
number = `${noOfThousands}K`;
|
||||
}
|
||||
}
|
||||
this.badgeContent.textContent = number;
|
||||
@@ -373,7 +371,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
|
||||
this.actions = this.getActions();
|
||||
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => this.element,
|
||||
getAnchor: () => this.element!,
|
||||
getActions: () => this.actions,
|
||||
onHide: () => dispose(this.actions)
|
||||
});
|
||||
@@ -385,7 +383,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
|
||||
action.radio = this.getActiveCompositeId() === action.id;
|
||||
|
||||
const badge = this.getBadge(composite.id);
|
||||
let suffix: string | number;
|
||||
let suffix: string | number | undefined;
|
||||
if (badge instanceof NumberBadge) {
|
||||
suffix = badge.number;
|
||||
} else if (badge instanceof TextBadge) {
|
||||
@@ -434,7 +432,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
|
||||
private static manageExtensionAction: ManageExtensionAction;
|
||||
|
||||
private compositeActivity: IActivity;
|
||||
private compositeActivity: IActivity | null;
|
||||
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>;
|
||||
|
||||
constructor(
|
||||
@@ -463,7 +461,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
protected get activity(): IActivity {
|
||||
if (!this.compositeActivity) {
|
||||
let activityName: string;
|
||||
const keybinding = this.getKeybindingLabel(this.compositeActivityAction.activity.keybindingId);
|
||||
const keybinding = typeof this.compositeActivityAction.activity.keybindingId === 'string' ? this.getKeybindingLabel(this.compositeActivityAction.activity.keybindingId) : null;
|
||||
if (keybinding) {
|
||||
activityName = nls.localize('titleKeybinding', "{0} ({1})", this.compositeActivityAction.activity.name, keybinding);
|
||||
} else {
|
||||
@@ -480,7 +478,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
return this.compositeActivity;
|
||||
}
|
||||
|
||||
private getKeybindingLabel(id: string): string {
|
||||
private getKeybindingLabel(id: string): string | null {
|
||||
const kb = this.keybindingService.lookupKeybinding(id);
|
||||
if (kb) {
|
||||
return kb.getLabel();
|
||||
@@ -503,7 +501,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
|
||||
// Allow to drag
|
||||
this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer!.effectAllowed = 'move';
|
||||
|
||||
// Registe as dragged to local transfer
|
||||
this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype);
|
||||
@@ -516,7 +514,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
|
||||
this._register(new DragAndDropObserver(this.container, {
|
||||
onDragEnter: e => {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) {
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id !== this.activity.id) {
|
||||
this.updateFromDragging(container, true);
|
||||
}
|
||||
},
|
||||
@@ -539,7 +537,7 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
dom.EventHelper.stop(e, true);
|
||||
|
||||
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
|
||||
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id;
|
||||
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
|
||||
if (draggedCompositeId !== this.activity.id) {
|
||||
this.updateFromDragging(container, false);
|
||||
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
|
||||
@@ -617,6 +615,10 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
}
|
||||
|
||||
protected updateEnabled(): void {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getAction().enabled) {
|
||||
dom.removeClass(this.element, 'disabled');
|
||||
} else {
|
||||
@@ -636,12 +638,12 @@ export class CompositeActionItem extends ActivityActionItem {
|
||||
export class ToggleCompositePinnedAction extends Action {
|
||||
|
||||
constructor(
|
||||
private activity: IActivity,
|
||||
private activity: IActivity | undefined,
|
||||
private compositeBar: ICompositeBar
|
||||
) {
|
||||
super('show.toggleCompositePinned', activity ? activity.name : nls.localize('toggle', "Toggle View Pinned"));
|
||||
|
||||
this.checked = this.activity && this.compositeBar.isPinned(this.activity.id);
|
||||
this.checked = !!this.activity && this.compositeBar.isPinned(this.activity.id);
|
||||
}
|
||||
|
||||
run(context: string): Promise<any> {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Part, IPartOptions } from 'vs/workbench/browser/part';
|
||||
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IComposite } from 'vs/workbench/common/composite';
|
||||
import { ScopedProgressService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -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 { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export interface ICompositeTitleLabel {
|
||||
|
||||
@@ -54,8 +55,8 @@ interface CompositeItem {
|
||||
|
||||
export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
protected _onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>());
|
||||
protected _onDidCompositeClose = this._register(new Emitter<IComposite>());
|
||||
protected readonly onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>());
|
||||
protected readonly onDidCompositeClose = this._register(new Emitter<IComposite>());
|
||||
|
||||
protected toolBar: ToolBar;
|
||||
|
||||
@@ -75,7 +76,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
protected storageService: IStorageService,
|
||||
private telemetryService: ITelemetryService,
|
||||
protected contextMenuService: IContextMenuService,
|
||||
protected partService: IPartService,
|
||||
protected layoutService: IWorkbenchLayoutService,
|
||||
protected keybindingService: IKeybindingService,
|
||||
protected instantiationService: IInstantiationService,
|
||||
themeService: IThemeService,
|
||||
@@ -84,11 +85,11 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
private defaultCompositeId: string,
|
||||
private nameForTelemetry: string,
|
||||
private compositeCSSClass: string,
|
||||
private titleForegroundColor: string,
|
||||
private titleForegroundColor: string | undefined,
|
||||
id: string,
|
||||
options: IPartOptions
|
||||
) {
|
||||
super(id, options, themeService, storageService);
|
||||
super(id, options, themeService, storageService, layoutService);
|
||||
|
||||
this.mapCompositeToCompositeContainer = {};
|
||||
this.mapActionsBindingToComposite = {};
|
||||
@@ -98,6 +99,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
}
|
||||
|
||||
protected openComposite(id: string, focus?: boolean): Composite | undefined {
|
||||
|
||||
// Check if composite already visible and just focus in that case
|
||||
if (this.activeComposite && this.activeComposite.getId() === id) {
|
||||
if (focus) {
|
||||
@@ -140,7 +142,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
composite.focus();
|
||||
}
|
||||
|
||||
this._onDidCompositeOpen.fire({ composite, focus });
|
||||
this.onDidCompositeOpen.fire({ composite, focus });
|
||||
return composite;
|
||||
}
|
||||
|
||||
@@ -152,7 +154,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
// Return with the composite that is being opened
|
||||
if (composite) {
|
||||
this._onDidCompositeOpen.fire({ composite, focus });
|
||||
this.onDidCompositeOpen.fire({ composite, focus });
|
||||
}
|
||||
|
||||
return composite;
|
||||
@@ -222,7 +224,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
// Report progress for slow loading composites (but only if we did not create the composites before already)
|
||||
const compositeItem = this.instantiatedCompositeItems.get(composite.getId());
|
||||
if (compositeItem && !compositeContainer) {
|
||||
compositeItem.progressService.showWhile(Promise.resolve(), this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */);
|
||||
compositeItem.progressService.showWhile(Promise.resolve(), this.layoutService.isRestored() ? 800 : 3200 /* less ugly initial startup */);
|
||||
}
|
||||
|
||||
// Fill Content and Actions
|
||||
@@ -244,7 +246,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
// Update title with composite title if it differs from descriptor
|
||||
const descriptor = this.registry.getComposite(composite.getId());
|
||||
if (descriptor && descriptor.name !== composite.getTitle()) {
|
||||
this.updateTitle(composite.getId(), composite.getTitle() || undefined);
|
||||
this.updateTitle(composite.getId(), withNullAsUndefined(composite.getTitle()));
|
||||
}
|
||||
|
||||
// Handle Composite Actions
|
||||
@@ -341,6 +343,9 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
primaryActions.push(...this.getActions());
|
||||
secondaryActions.push(...this.getSecondaryActions());
|
||||
|
||||
// Update context
|
||||
this.toolBar.context = this.actionsContextProvider();
|
||||
|
||||
// Return fn to set into toolbar
|
||||
return this.toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
|
||||
}
|
||||
@@ -375,7 +380,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
// Empty Actions
|
||||
this.toolBar.setActions([])();
|
||||
this._onDidCompositeClose.fire(composite);
|
||||
this.onDidCompositeClose.fire(composite);
|
||||
|
||||
return composite;
|
||||
}
|
||||
@@ -415,12 +420,12 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
},
|
||||
|
||||
updateStyles: () => {
|
||||
titleLabel.style.color = $this.getColor($this.titleForegroundColor);
|
||||
titleLabel.style.color = $this.titleForegroundColor ? $this.getColor($this.titleForegroundColor) : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
// Forward to title label
|
||||
@@ -437,6 +442,16 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected actionsContextProvider(): any {
|
||||
|
||||
// Check Active Composite
|
||||
if (this.activeComposite) {
|
||||
return this.activeComposite.getActionsContext();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
const contentContainer = append(parent, $('.content'));
|
||||
|
||||
@@ -449,6 +464,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
|
||||
getProgressIndicator(id: string): IProgressService | null {
|
||||
const compositeItem = this.instantiatedCompositeItems.get(id);
|
||||
|
||||
return compositeItem ? compositeItem.progressService : null;
|
||||
}
|
||||
|
||||
@@ -464,27 +480,20 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
return AnchorAlignment.RIGHT;
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
// Pass to super
|
||||
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2!));
|
||||
layout(width: number, height: number): void {
|
||||
|
||||
// Pass Contentsize to composite
|
||||
this.contentAreaSize = sizes[1];
|
||||
// Layout contents
|
||||
this.contentAreaSize = super.layoutContents(width, height).contentSize;
|
||||
|
||||
// Layout composite
|
||||
if (this.activeComposite) {
|
||||
this.activeComposite.layout(this.contentAreaSize);
|
||||
}
|
||||
|
||||
if (dim1 instanceof Dimension) {
|
||||
return sizes;
|
||||
}
|
||||
}
|
||||
|
||||
protected removeComposite(compositeId: string): boolean {
|
||||
if (this.activeComposite && this.activeComposite.getId() === compositeId) {
|
||||
// do not remove active compoiste
|
||||
return false;
|
||||
return false; // do not remove active composite
|
||||
}
|
||||
|
||||
delete this.mapCompositeToCompositeContainer[compositeId];
|
||||
@@ -495,6 +504,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
|
||||
dispose(compositeItem.disposable);
|
||||
this.instantiatedCompositeItems.delete(compositeId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { EditorInput, EditorOptions, IEditor, GroupIdentifier, IEditorMemento }
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { localize } from 'vs/nls';
|
||||
import { IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { GroupIdentifier } from 'vs/workbench/common/editor';
|
||||
|
||||
@@ -19,7 +19,7 @@ export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditor
|
||||
|
||||
export interface IBreadcrumbsService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface IBreadcrumbsService {
|
||||
|
||||
export class BreadcrumbsService implements IBreadcrumbsService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private readonly _map = new Map<number, BreadcrumbsWidget>();
|
||||
|
||||
|
||||
@@ -41,10 +41,12 @@ import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workb
|
||||
import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
|
||||
import { SideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
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, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
|
||||
class Item extends BreadcrumbsItem {
|
||||
|
||||
@@ -148,6 +150,7 @@ export class BreadcrumbsControl {
|
||||
private _disposables = new Array<IDisposable>();
|
||||
private _breadcrumbsDisposables = new Array<IDisposable>();
|
||||
private _breadcrumbsPickerShowing = false;
|
||||
private _breadcrumbsPickerIgnoreOnceItem: BreadcrumbsItem | undefined;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@@ -196,7 +199,7 @@ export class BreadcrumbsControl {
|
||||
this.domNode.remove();
|
||||
}
|
||||
|
||||
layout(dim: dom.Dimension): void {
|
||||
layout(dim: dom.Dimension | undefined): void {
|
||||
this._widget.layout(dim);
|
||||
}
|
||||
|
||||
@@ -219,7 +222,7 @@ export class BreadcrumbsControl {
|
||||
input = input.master;
|
||||
}
|
||||
|
||||
if (!input || !input.getResource() || (input.getResource().scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()))) {
|
||||
if (!input || !input.getResource() || (input.getResource()!.scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()!))) {
|
||||
// cleanup and return when there is no input or when
|
||||
// we cannot handle this input
|
||||
this._ckBreadcrumbsPossible.set(false);
|
||||
@@ -236,7 +239,7 @@ export class BreadcrumbsControl {
|
||||
this._ckBreadcrumbsPossible.set(true);
|
||||
|
||||
let editor = this._getActiveCodeEditor();
|
||||
let model = new EditorBreadcrumbsModel(input.getResource(), editor, this._workspaceService, this._configurationService);
|
||||
let model = new EditorBreadcrumbsModel(input.getResource()!, editor, this._workspaceService, this._configurationService);
|
||||
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
|
||||
|
||||
let updateBreadcrumbs = () => {
|
||||
@@ -260,9 +263,12 @@ export class BreadcrumbsControl {
|
||||
return true;
|
||||
}
|
||||
|
||||
private _getActiveCodeEditor(): ICodeEditor {
|
||||
private _getActiveCodeEditor(): ICodeEditor | undefined {
|
||||
if (!this._editorGroup.activeControl) {
|
||||
return undefined;
|
||||
}
|
||||
let control = this._editorGroup.activeControl.getControl();
|
||||
let editor: ICodeEditor;
|
||||
let editor: ICodeEditor | undefined;
|
||||
if (isCodeEditor(control)) {
|
||||
editor = control as ICodeEditor;
|
||||
} else if (isDiffEditor(control)) {
|
||||
@@ -282,6 +288,13 @@ export class BreadcrumbsControl {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.item === this._breadcrumbsPickerIgnoreOnceItem) {
|
||||
this._breadcrumbsPickerIgnoreOnceItem = undefined;
|
||||
this._widget.setFocused(undefined);
|
||||
this._widget.setSelection(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const { element } = event.item as Item;
|
||||
this._editorGroup.focus();
|
||||
|
||||
@@ -313,7 +326,7 @@ export class BreadcrumbsControl {
|
||||
let picker: BreadcrumbsPicker;
|
||||
let editor = this._getActiveCodeEditor();
|
||||
let editorDecorations: string[] = [];
|
||||
let editorViewState: ICodeEditorViewState;
|
||||
let editorViewState: ICodeEditorViewState | undefined;
|
||||
|
||||
this._contextViewService.showContextView({
|
||||
render: (parent: HTMLElement) => {
|
||||
@@ -336,7 +349,7 @@ export class BreadcrumbsControl {
|
||||
return;
|
||||
}
|
||||
if (!editorViewState) {
|
||||
editorViewState = editor.saveViewState();
|
||||
editorViewState = withNullAsUndefined(editor.saveViewState());
|
||||
}
|
||||
const { symbol } = data.target;
|
||||
editor.revealRangeInCenter(symbol.range, ScrollType.Smooth);
|
||||
@@ -347,12 +360,29 @@ export class BreadcrumbsControl {
|
||||
isWholeLine: true
|
||||
}
|
||||
}]);
|
||||
|
||||
});
|
||||
|
||||
let zoomListener = onDidChangeZoomLevel(() => {
|
||||
this._contextViewService.hideContextView(this);
|
||||
});
|
||||
|
||||
let focusTracker = dom.trackFocus(parent);
|
||||
let blurListener = focusTracker.onDidBlur(() => {
|
||||
this._breadcrumbsPickerIgnoreOnceItem = this._widget.isDOMFocused() ? event.item : undefined;
|
||||
this._contextViewService.hideContextView(this);
|
||||
});
|
||||
|
||||
this._breadcrumbsPickerShowing = true;
|
||||
this._updateCkBreadcrumbsActive();
|
||||
|
||||
return combinedDisposable([selectListener, focusListener, picker]);
|
||||
return combinedDisposable([
|
||||
picker,
|
||||
selectListener,
|
||||
focusListener,
|
||||
zoomListener,
|
||||
focusTracker,
|
||||
blurListener
|
||||
]);
|
||||
},
|
||||
getAnchor: () => {
|
||||
let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/;
|
||||
@@ -381,7 +411,7 @@ export class BreadcrumbsControl {
|
||||
} else {
|
||||
pickerArrowOffset = (data.left + (data.width * 0.3)) - x;
|
||||
}
|
||||
picker.setInput(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
|
||||
picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
|
||||
return { x, y };
|
||||
},
|
||||
onHide: (data) => {
|
||||
@@ -406,7 +436,7 @@ export class BreadcrumbsControl {
|
||||
this._ckBreadcrumbsActive.set(value);
|
||||
}
|
||||
|
||||
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, pinned: boolean = false): void {
|
||||
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined, pinned: boolean = false): void {
|
||||
if (element instanceof FileElement) {
|
||||
if (element.kind === FileKind.FILE) {
|
||||
// open file in any editor
|
||||
@@ -421,14 +451,16 @@ export class BreadcrumbsControl {
|
||||
|
||||
} else if (element instanceof OutlineElement) {
|
||||
// open symbol in code editor
|
||||
let model = OutlineModel.get(element);
|
||||
this._codeEditorService.openCodeEditor({
|
||||
resource: model.textModel.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
}
|
||||
}, this._getActiveCodeEditor(), group === SIDE_GROUP);
|
||||
const model = OutlineModel.get(element);
|
||||
if (model) {
|
||||
this._codeEditorService.openCodeEditor({
|
||||
resource: model.textModel.uri,
|
||||
options: {
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
revealInCenterIfOutsideViewport: true
|
||||
}
|
||||
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,7 +481,7 @@ export class BreadcrumbsControl {
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: 'breadcrumbs.toggle',
|
||||
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'Toggle Breadcrumbs' },
|
||||
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'View: Toggle Breadcrumbs' },
|
||||
category: localize('cmd.category', "View")
|
||||
}
|
||||
});
|
||||
@@ -540,7 +572,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget.focusNext();
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -556,7 +592,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget.focusPrev();
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -569,6 +609,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
});
|
||||
@@ -582,6 +625,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Reveal);
|
||||
}
|
||||
});
|
||||
@@ -593,9 +639,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler(accessor) {
|
||||
const groups = accessor.get(IEditorGroupsService);
|
||||
const breadcrumbs = accessor.get(IBreadcrumbsService);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
|
||||
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
|
||||
groups.activeGroup.activeControl.focus();
|
||||
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
widget.setFocused(undefined);
|
||||
widget.setSelection(undefined);
|
||||
if (groups.activeGroup.activeControl) {
|
||||
groups.activeGroup.activeControl.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
@@ -606,15 +658,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
handler(accessor) {
|
||||
const editors = accessor.get(IEditorService);
|
||||
const lists = accessor.get(IListService);
|
||||
const element = <OutlineElement | IFileStat>lists.lastFocusedList.getFocus();
|
||||
const element = lists.lastFocusedList ? <OutlineElement | IFileStat>lists.lastFocusedList.getFocus() : undefined;
|
||||
if (element instanceof OutlineElement) {
|
||||
const outlineElement = OutlineModel.get(element);
|
||||
if (!outlineElement) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// open symbol in editor
|
||||
return editors.openEditor({
|
||||
resource: OutlineModel.get(element).textModel.uri,
|
||||
resource: outlineElement.textModel.uri,
|
||||
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
|
||||
}, SIDE_GROUP);
|
||||
|
||||
} else if (URI.isUri(element.resource)) {
|
||||
} else if (element && URI.isUri(element.resource)) {
|
||||
// open file in editor
|
||||
return editors.openEditor({
|
||||
resource: element.resource,
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export class FileElement {
|
||||
constructor(
|
||||
@@ -105,7 +106,7 @@ export class EditorBreadcrumbsModel {
|
||||
}
|
||||
|
||||
let info: FileInfo = {
|
||||
folder: workspaceService.getWorkspaceFolder(uri) || undefined,
|
||||
folder: withNullAsUndefined(workspaceService.getWorkspaceFolder(uri)),
|
||||
path: []
|
||||
};
|
||||
|
||||
@@ -117,7 +118,7 @@ export class EditorBreadcrumbsModel {
|
||||
info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
|
||||
let prevPathLength = uriPrefix.path.length;
|
||||
uriPrefix = dirname(uriPrefix);
|
||||
if (!uriPrefix || uriPrefix.path.length === prevPathLength) {
|
||||
if (uriPrefix.path.length === prevPathLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,35 +3,38 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { compareFileNames } from 'vs/base/common/comparers';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { createMatches, FuzzyScore, fuzzyScore } from 'vs/base/common/filters';
|
||||
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { posix } from 'vs/base/common/path';
|
||||
import { basename, dirname, isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import 'vs/css!./media/breadcrumbscontrol';
|
||||
import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
|
||||
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
|
||||
import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService';
|
||||
import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels';
|
||||
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, IDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
|
||||
|
||||
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
|
||||
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
|
||||
const ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement
|
||||
? BreadcrumbsFilePicker
|
||||
: BreadcrumbsOutlinePicker;
|
||||
|
||||
return instantiationService.createInstance(ctor, parent);
|
||||
}
|
||||
|
||||
@@ -43,16 +46,17 @@ interface ILayoutInfo {
|
||||
inputHeight: number;
|
||||
}
|
||||
|
||||
type Tree<I, E> = WorkbenchDataTree<I, E, FuzzyScore> | WorkbenchAsyncDataTree<I, E, FuzzyScore>;
|
||||
|
||||
export abstract class BreadcrumbsPicker {
|
||||
|
||||
protected readonly _disposables = new Array<IDisposable>();
|
||||
protected readonly _domNode: HTMLDivElement;
|
||||
protected readonly _arrow: HTMLDivElement;
|
||||
protected readonly _treeContainer: HTMLDivElement;
|
||||
protected readonly _tree: HighlightingWorkbenchTree;
|
||||
protected readonly _focus: dom.IFocusTracker;
|
||||
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
|
||||
private _layoutInfo: ILayoutInfo;
|
||||
protected _arrow: HTMLDivElement;
|
||||
protected _treeContainer: HTMLDivElement;
|
||||
protected _tree: Tree<any, any>;
|
||||
protected _fakeEvent = new UIEvent('fakeEvent');
|
||||
protected _layoutInfo: ILayoutInfo;
|
||||
|
||||
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
|
||||
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
|
||||
@@ -64,154 +68,111 @@ export abstract class BreadcrumbsPicker {
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
|
||||
@IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IConfigurationService protected readonly _configurationService: IConfigurationService,
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
|
||||
parent.appendChild(this._domNode);
|
||||
|
||||
this._focus = dom.trackFocus(this._domNode);
|
||||
this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables);
|
||||
this._disposables.push(onDidChangeZoomLevel(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined })));
|
||||
|
||||
const theme = this._themeService.getTheme();
|
||||
const color = theme.getColor(breadcrumbsPickerBackground);
|
||||
|
||||
this._arrow = document.createElement('div');
|
||||
this._arrow.className = 'arrow';
|
||||
this._arrow.style.borderColor = `transparent transparent ${color.toString()}`;
|
||||
this._domNode.appendChild(this._arrow);
|
||||
|
||||
this._treeContainer = document.createElement('div');
|
||||
this._treeContainer.style.background = color.toString();
|
||||
this._treeContainer.style.paddingTop = '2px';
|
||||
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
|
||||
this._domNode.appendChild(this._treeContainer);
|
||||
|
||||
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
|
||||
|
||||
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
|
||||
this._disposables.push(filterConfig);
|
||||
|
||||
const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined });
|
||||
this._tree = this._instantiationService.createInstance(
|
||||
HighlightingWorkbenchTree,
|
||||
this._treeContainer,
|
||||
treeConfig,
|
||||
<IHighlightingTreeOptions>{ useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 },
|
||||
{ placeholder: localize('placeholder', "Find") }
|
||||
);
|
||||
this._disposables.push(this._tree.onDidChangeSelection(e => {
|
||||
if (e.payload !== this._tree) {
|
||||
const target = this._getTargetFromEvent(e.selection[0], e.payload);
|
||||
if (target) {
|
||||
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
|
||||
this._onDidPickElement.fire({ target, payload: e.payload });
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidChangeFocus(e => {
|
||||
const target = this._getTargetFromEvent(e.focus, e.payload);
|
||||
if (target) {
|
||||
this._onDidFocusElement.fire({ target, payload: e.payload });
|
||||
}
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidStartFiltering(() => {
|
||||
this._layoutInfo.inputHeight = 36;
|
||||
this._layout();
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidExpandItem(() => {
|
||||
this._layout();
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidCollapseItem(() => {
|
||||
this._layout();
|
||||
}));
|
||||
|
||||
// tree icon theme specials
|
||||
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
|
||||
dom.addClass(this._treeContainer, 'show-file-icons');
|
||||
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
|
||||
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
|
||||
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
|
||||
};
|
||||
this._disposables.push(_themeService.onDidFileIconThemeChange(onFileIconThemeChange));
|
||||
onFileIconThemeChange(_themeService.getFileIconTheme());
|
||||
|
||||
this._domNode.focus();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
dispose(this._disposables);
|
||||
this._onDidPickElement.dispose();
|
||||
this._tree.dispose();
|
||||
this._focus.dispose();
|
||||
this._symbolSortOrder.dispose();
|
||||
}
|
||||
|
||||
setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
|
||||
let actualInput = this._getInput(input);
|
||||
this._tree.setInput(actualInput).then(() => {
|
||||
show(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
|
||||
|
||||
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
|
||||
this._layout();
|
||||
const theme = this._themeService.getTheme();
|
||||
const color = theme.getColor(breadcrumbsPickerBackground);
|
||||
|
||||
// use proper selection, reveal
|
||||
let selection = this._getInitialSelection(this._tree, input);
|
||||
if (selection) {
|
||||
return this._tree.reveal(selection, 0.5).then(() => {
|
||||
this._tree.setSelection([selection], this._tree);
|
||||
this._tree.setFocus(selection);
|
||||
this._tree.domFocus();
|
||||
});
|
||||
} else {
|
||||
this._tree.focusFirst();
|
||||
this._tree.setSelection([this._tree.getFocus()], this._tree);
|
||||
this._tree.domFocus();
|
||||
return Promise.resolve(null);
|
||||
this._arrow = document.createElement('div');
|
||||
this._arrow.className = 'arrow';
|
||||
this._arrow.style.borderColor = `transparent transparent ${color ? color.toString() : ''}`;
|
||||
this._domNode.appendChild(this._arrow);
|
||||
|
||||
this._treeContainer = document.createElement('div');
|
||||
this._treeContainer.style.background = color ? color.toString() : '';
|
||||
this._treeContainer.style.paddingTop = '2px';
|
||||
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
|
||||
this._domNode.appendChild(this._treeContainer);
|
||||
|
||||
|
||||
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
|
||||
this._disposables.push(filterConfig);
|
||||
|
||||
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
|
||||
this._tree = this._createTree(this._treeContainer);
|
||||
|
||||
this._disposables.push(this._tree.onDidChangeSelection(e => {
|
||||
if (e.browserEvent !== this._fakeEvent) {
|
||||
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
|
||||
if (target) {
|
||||
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
|
||||
this._onDidPickElement.fire({ target, payload: undefined });
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}, onUnexpectedError);
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidChangeFocus(e => {
|
||||
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
|
||||
if (target) {
|
||||
this._onDidFocusElement.fire({ target, payload: undefined });
|
||||
}
|
||||
}));
|
||||
this._disposables.push(this._tree.onDidChangeContentHeight(() => {
|
||||
this._layout();
|
||||
}));
|
||||
|
||||
// filter on type: state
|
||||
const cfgFilterOnType = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
|
||||
this._tree.updateOptions({ filterOnType: cfgFilterOnType.getValue() });
|
||||
this._disposables.push(this._tree.onDidUpdateOptions(e => {
|
||||
this._configurationService.updateValue(cfgFilterOnType.name, e.filterOnType, ConfigurationTarget.MEMORY);
|
||||
}));
|
||||
|
||||
this._domNode.focus();
|
||||
|
||||
this._setInput(input).then(() => {
|
||||
this._layout();
|
||||
}).catch(onUnexpectedError);
|
||||
}
|
||||
|
||||
private _layout(info: ILayoutInfo = this._layoutInfo): void {
|
||||
protected _layout(): void {
|
||||
|
||||
let count = 0;
|
||||
let nav = this._tree.getNavigator(undefined, false);
|
||||
while (nav.next() && count < 13) { count += 1; }
|
||||
|
||||
let headerHeight = 2 * info.arrowSize;
|
||||
let treeHeight = Math.min(info.maxHeight - headerHeight, count * 22);
|
||||
let totalHeight = treeHeight + headerHeight;
|
||||
const headerHeight = 2 * this._layoutInfo.arrowSize;
|
||||
const treeHeight = Math.min(this._layoutInfo.maxHeight - headerHeight, this._tree.contentHeight);
|
||||
const totalHeight = treeHeight + headerHeight;
|
||||
|
||||
this._domNode.style.height = `${totalHeight}px`;
|
||||
this._domNode.style.width = `${info.width}px`;
|
||||
this._arrow.style.top = `-${2 * info.arrowSize}px`;
|
||||
this._arrow.style.borderWidth = `${info.arrowSize}px`;
|
||||
this._arrow.style.marginLeft = `${info.arrowOffset}px`;
|
||||
this._domNode.style.width = `${this._layoutInfo.width}px`;
|
||||
this._arrow.style.top = `-${2 * this._layoutInfo.arrowSize}px`;
|
||||
this._arrow.style.borderWidth = `${this._layoutInfo.arrowSize}px`;
|
||||
this._arrow.style.marginLeft = `${this._layoutInfo.arrowOffset}px`;
|
||||
this._treeContainer.style.height = `${treeHeight}px`;
|
||||
this._treeContainer.style.width = `${info.width}px`;
|
||||
this._tree.layout();
|
||||
this._layoutInfo = info;
|
||||
this._treeContainer.style.width = `${this._layoutInfo.width}px`;
|
||||
this._tree.layout(treeHeight, this._layoutInfo.width);
|
||||
|
||||
}
|
||||
|
||||
protected abstract _getInput(input: BreadcrumbElement): any;
|
||||
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
|
||||
protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration;
|
||||
protected abstract _getTargetFromEvent(element: any, payload: any): any | undefined;
|
||||
protected abstract _setInput(element: BreadcrumbElement): Promise<void>;
|
||||
protected abstract _createTree(container: HTMLElement): Tree<any, any>;
|
||||
protected abstract _getTargetFromEvent(element: any, payload: UIEvent | undefined): any | undefined;
|
||||
}
|
||||
|
||||
//#region - Files
|
||||
|
||||
export class FileDataSource implements IDataSource {
|
||||
class FileVirtualDelegate implements IListVirtualDelegate<IFileStat | IWorkspaceFolder> {
|
||||
getHeight(_element: IFileStat | IWorkspaceFolder) {
|
||||
return 22;
|
||||
}
|
||||
getTemplateId(_element: IFileStat | IWorkspaceFolder): string {
|
||||
return 'FileStat';
|
||||
}
|
||||
}
|
||||
|
||||
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
) { }
|
||||
|
||||
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
|
||||
class FileIdentityProvider implements IIdentityProvider<IWorkspace | IWorkspaceFolder | IFileStat | URI> {
|
||||
getId(element: IWorkspace | IWorkspaceFolder | IFileStat | URI): { toString(): string; } {
|
||||
if (URI.isUri(element)) {
|
||||
return element.toString();
|
||||
} else if (IWorkspace.isIWorkspace(element)) {
|
||||
@@ -222,12 +183,26 @@ export class FileDataSource implements IDataSource {
|
||||
return element.resource.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
|
||||
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
|
||||
|
||||
class FileDataSource implements IAsyncDataSource<IWorkspace | URI, IWorkspaceFolder | IFileStat> {
|
||||
|
||||
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
) { }
|
||||
|
||||
hasChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): boolean {
|
||||
return URI.isUri(element)
|
||||
|| IWorkspace.isIWorkspace(element)
|
||||
|| IWorkspaceFolder.isIWorkspaceFolder(element)
|
||||
|| element.isDirectory;
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): Promise<IWorkspaceFolder[] | IFileStat[]> {
|
||||
getChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<(IWorkspaceFolder | IFileStat)[]> {
|
||||
|
||||
if (IWorkspace.isIWorkspace(element)) {
|
||||
return Promise.resolve(element.folders).then(folders => {
|
||||
for (let child of folders) {
|
||||
@@ -245,19 +220,62 @@ export class FileDataSource implements IDataSource {
|
||||
uri = element.resource;
|
||||
}
|
||||
return this._fileService.resolveFile(uri).then(stat => {
|
||||
for (let child of stat.children) {
|
||||
for (const child of stat.children || []) {
|
||||
this._parents.set(stat, child);
|
||||
}
|
||||
return stat.children;
|
||||
return stat.children || [];
|
||||
});
|
||||
}
|
||||
|
||||
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<IWorkspaceFolder | IFileStat> {
|
||||
return Promise.resolve(this._parents.get(element));
|
||||
}
|
||||
}
|
||||
|
||||
export class FileFilter implements IFilter {
|
||||
class FileRenderer implements ITreeRenderer<IFileStat | IWorkspaceFolder, FuzzyScore, IResourceLabel> {
|
||||
|
||||
readonly templateId: string = 'FileStat';
|
||||
|
||||
constructor(
|
||||
private readonly _labels: ResourceLabels,
|
||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||
) { }
|
||||
|
||||
|
||||
renderTemplate(container: HTMLElement): IResourceLabel {
|
||||
return this._labels.create(container, { supportHighlights: true });
|
||||
}
|
||||
|
||||
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 { element } = node;
|
||||
let resource: URI;
|
||||
let fileKind: FileKind;
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
resource = element.uri;
|
||||
fileKind = FileKind.ROOT_FOLDER;
|
||||
} else {
|
||||
resource = element.resource;
|
||||
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
|
||||
}
|
||||
templateData.setFile(resource, {
|
||||
fileKind,
|
||||
hidePath: true,
|
||||
fileDecorations: fileDecorations,
|
||||
matches: createMatches(node.filterData),
|
||||
extraClasses: ['picker-item']
|
||||
});
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IResourceLabel): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class FileNavigationLabelProvider implements IKeyboardNavigationLabelProvider<IWorkspaceFolder | IFileStat> {
|
||||
|
||||
getKeyboardNavigationLabel(element: IWorkspaceFolder | IFileStat): { toString(): string; } {
|
||||
return element.name;
|
||||
}
|
||||
}
|
||||
|
||||
class FileFilter implements ITreeFilter<IWorkspaceFolder | IFileStat> {
|
||||
|
||||
private readonly _cachedExpressions = new Map<string, glob.ParsedExpression>();
|
||||
private readonly _disposables: IDisposable[] = [];
|
||||
@@ -281,7 +299,7 @@ export class FileFilter implements IFilter {
|
||||
continue;
|
||||
}
|
||||
let patternAbs = pattern.indexOf('**/') !== 0
|
||||
? join(folder.uri.path, pattern)
|
||||
? posix.join(folder.uri.path, pattern)
|
||||
: pattern;
|
||||
|
||||
adjustedConfig[patternAbs] = excludesConfig[pattern];
|
||||
@@ -301,7 +319,7 @@ export class FileFilter implements IFilter {
|
||||
dispose(this._disposables);
|
||||
}
|
||||
|
||||
isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean {
|
||||
filter(element: IWorkspaceFolder | IFileStat, _parentVisibility: TreeVisibility): boolean {
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
// not a file
|
||||
return true;
|
||||
@@ -312,77 +330,24 @@ export class FileFilter implements IFilter {
|
||||
return true;
|
||||
}
|
||||
|
||||
const expression = this._cachedExpressions.get(folder.uri.toString());
|
||||
const expression = this._cachedExpressions.get(folder.uri.toString())!;
|
||||
return !expression(element.resource.path, basename(element.resource));
|
||||
}
|
||||
}
|
||||
|
||||
export class FileHighlighter implements IHighlighter {
|
||||
getHighlightsStorageKey(element: IFileStat | IWorkspaceFolder): string {
|
||||
return IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString();
|
||||
}
|
||||
getHighlights(tree: ITree, element: IFileStat | IWorkspaceFolder, pattern: string): FuzzyScore {
|
||||
return fuzzyScore(pattern, pattern.toLowerCase(), 0, element.name, element.name.toLowerCase(), 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
export class FileRenderer implements IRenderer {
|
||||
|
||||
constructor(
|
||||
private readonly _labels: ResourceLabels,
|
||||
@IConfigurationService private readonly _configService: IConfigurationService,
|
||||
) { }
|
||||
|
||||
getHeight(tree: ITree, element: any): number {
|
||||
return 22;
|
||||
}
|
||||
|
||||
getTemplateId(tree: ITree, element: any): string {
|
||||
return 'FileStat';
|
||||
}
|
||||
|
||||
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
|
||||
return this._labels.create(container, { supportHighlights: true });
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void {
|
||||
let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
|
||||
let resource: URI;
|
||||
let fileKind: FileKind;
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
|
||||
resource = element.uri;
|
||||
fileKind = FileKind.ROOT_FOLDER;
|
||||
} else {
|
||||
resource = element.resource;
|
||||
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
|
||||
}
|
||||
templateData.setFile(resource, {
|
||||
fileKind,
|
||||
hidePath: true,
|
||||
fileDecorations: fileDecorations,
|
||||
matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)),
|
||||
extraClasses: ['picker-item']
|
||||
});
|
||||
}
|
||||
|
||||
disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class FileSorter implements ISorter {
|
||||
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
|
||||
export class FileSorter implements ITreeSorter<IFileStat | IWorkspaceFolder> {
|
||||
compare(a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
|
||||
return a.index - b.index;
|
||||
}
|
||||
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
|
||||
// same type -> compare on names
|
||||
return compareFileNames(a.name, b.name);
|
||||
} else if ((a as IFileStat).isDirectory) {
|
||||
return -1;
|
||||
} else {
|
||||
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
|
||||
// same type -> compare on names
|
||||
return compareFileNames(a.name, b.name);
|
||||
} else if ((a as IFileStat).isDirectory) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -399,44 +364,69 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
super(parent, instantiationService, themeService, configService);
|
||||
}
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
let { uri, kind } = (input as FileElement);
|
||||
if (kind === FileKind.ROOT_FOLDER) {
|
||||
return this._workspaceService.getWorkspace();
|
||||
} else {
|
||||
return dirname(uri);
|
||||
}
|
||||
}
|
||||
_createTree(container: HTMLElement) {
|
||||
|
||||
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
|
||||
let { uri } = (input as FileElement);
|
||||
let nav = tree.getNavigator();
|
||||
while (nav.next()) {
|
||||
let cur = nav.current();
|
||||
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
|
||||
if (isEqual(uri, candidate)) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// tree icon theme specials
|
||||
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
|
||||
dom.addClass(this._treeContainer, 'show-file-icons');
|
||||
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
|
||||
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
|
||||
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
|
||||
};
|
||||
this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange));
|
||||
onFileIconThemeChange(this._themeService.getFileIconTheme());
|
||||
|
||||
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
|
||||
// todo@joh reuse explorer implementations?
|
||||
const filter = this._instantiationService.createInstance(FileFilter);
|
||||
this._disposables.push(filter);
|
||||
|
||||
config.dataSource = this._instantiationService.createInstance(FileDataSource);
|
||||
const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */);
|
||||
this._disposables.push(labels);
|
||||
config.renderer = this._instantiationService.createInstance(FileRenderer, labels);
|
||||
config.sorter = new FileSorter();
|
||||
config.highlighter = new FileHighlighter();
|
||||
config.filter = filter;
|
||||
return config;
|
||||
|
||||
return this._instantiationService.createInstance(
|
||||
WorkbenchAsyncDataTree,
|
||||
container,
|
||||
new FileVirtualDelegate(),
|
||||
[this._instantiationService.createInstance(FileRenderer, labels)],
|
||||
this._instantiationService.createInstance(FileDataSource),
|
||||
{
|
||||
filterOnType: true,
|
||||
multipleSelectionSupport: false,
|
||||
sorter: new FileSorter(),
|
||||
filter: this._instantiationService.createInstance(FileFilter),
|
||||
identityProvider: new FileIdentityProvider(),
|
||||
keyboardNavigationLabelProvider: new FileNavigationLabelProvider()
|
||||
}
|
||||
) as WorkbenchAsyncDataTree<BreadcrumbElement, any, FuzzyScore>;
|
||||
}
|
||||
|
||||
_setInput(element: BreadcrumbElement): Promise<void> {
|
||||
const { uri, kind } = (element as FileElement);
|
||||
let input: IWorkspace | URI;
|
||||
if (kind === FileKind.ROOT_FOLDER) {
|
||||
input = this._workspaceService.getWorkspace();
|
||||
} else {
|
||||
input = dirname(uri);
|
||||
}
|
||||
|
||||
const tree = this._tree as WorkbenchAsyncDataTree<IWorkspace | URI, IWorkspaceFolder | IFileStat, FuzzyScore>;
|
||||
return tree.setInput(input).then(() => {
|
||||
let focusElement: IWorkspaceFolder | IFileStat | undefined;
|
||||
for (const { element } of tree.getNode().children) {
|
||||
if (IWorkspaceFolder.isIWorkspaceFolder(element) && isEqual(element.uri, uri)) {
|
||||
focusElement = element;
|
||||
break;
|
||||
} else if (isEqual((element as IFileStat).resource, uri)) {
|
||||
focusElement = element as IFileStat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (focusElement) {
|
||||
tree.reveal(focusElement, 0.5);
|
||||
tree.setFocus([focusElement], this._fakeEvent);
|
||||
}
|
||||
tree.domFocus();
|
||||
});
|
||||
}
|
||||
|
||||
protected _getTargetFromEvent(element: any, _payload: any): any | undefined {
|
||||
// todo@joh
|
||||
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
|
||||
return new FileElement((element as IFileStat).resource, FileKind.FILE);
|
||||
}
|
||||
@@ -446,52 +436,84 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
|
||||
|
||||
//#region - Symbols
|
||||
|
||||
class OutlineHighlighter implements IHighlighter {
|
||||
getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore {
|
||||
OutlineModel.get(element).updateMatches(pattern);
|
||||
return element.score;
|
||||
}
|
||||
}
|
||||
|
||||
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
|
||||
protected _getInput(input: BreadcrumbElement): any {
|
||||
let element = input as TreeElement;
|
||||
let model = OutlineModel.get(element);
|
||||
model.updateMatches('');
|
||||
return model;
|
||||
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super(parent, instantiationService, themeService, configurationService);
|
||||
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
|
||||
}
|
||||
|
||||
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
|
||||
return input instanceof OutlineModel ? undefined : input;
|
||||
protected _createTree(container: HTMLElement) {
|
||||
return this._instantiationService.createInstance<
|
||||
HTMLElement,
|
||||
IListVirtualDelegate<OutlineItem>,
|
||||
ITreeRenderer<any, FuzzyScore, any>[],
|
||||
IDataSource<OutlineModel, OutlineItem>,
|
||||
IDataTreeOptions<OutlineItem, FuzzyScore>,
|
||||
WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>
|
||||
>(
|
||||
WorkbenchDataTree,
|
||||
container,
|
||||
new OutlineVirtualDelegate(),
|
||||
[new OutlineGroupRenderer(), this._instantiationService.createInstance(OutlineElementRenderer)],
|
||||
new OutlineDataSource(),
|
||||
{
|
||||
filterOnType: true,
|
||||
expandOnlyOnTwistieClick: true,
|
||||
multipleSelectionSupport: false,
|
||||
sorter: new OutlineItemComparator(this._getOutlineItemCompareType()),
|
||||
identityProvider: new OutlineIdentityProvider(),
|
||||
keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider)
|
||||
}
|
||||
) as WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>;
|
||||
}
|
||||
|
||||
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
|
||||
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
|
||||
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
|
||||
config.sorter = new OutlineItemComparator(this._getOutlineItemComparator());
|
||||
config.highlighter = new OutlineHighlighter();
|
||||
return config;
|
||||
dispose(): void {
|
||||
this._symbolSortOrder.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
protected _getTargetFromEvent(element: any, payload: any): any | undefined {
|
||||
if (payload && payload.didClickOnTwistie) {
|
||||
return;
|
||||
protected _setInput(input: BreadcrumbElement): Promise<void> {
|
||||
const element = input as TreeElement;
|
||||
const model = OutlineModel.get(element)!;
|
||||
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
|
||||
tree.setInput(model);
|
||||
|
||||
let focusElement: TreeElement;
|
||||
if (element === model) {
|
||||
focusElement = tree.navigate().first();
|
||||
} else {
|
||||
focusElement = element;
|
||||
}
|
||||
tree.reveal(focusElement, 0.5);
|
||||
tree.setFocus([focusElement], this._fakeEvent);
|
||||
tree.domFocus();
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
protected _getTargetFromEvent(element: any): any | undefined {
|
||||
if (element instanceof OutlineElement) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
private _getOutlineItemComparator(): OutlineItemCompareType {
|
||||
private _getOutlineItemCompareType(): OutlineSortOrder {
|
||||
switch (this._symbolSortOrder.getValue()) {
|
||||
case 'name':
|
||||
return OutlineItemCompareType.ByName;
|
||||
return OutlineSortOrder.ByName;
|
||||
case 'type':
|
||||
return OutlineItemCompareType.ByKind;
|
||||
return OutlineSortOrder.ByKind;
|
||||
case 'position':
|
||||
default:
|
||||
return OutlineItemCompareType.ByPosition;
|
||||
return OutlineSortOrder.ByPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
|
||||
interface ISerializedUntitledEditorInput {
|
||||
resource: string;
|
||||
resourceJSON: object;
|
||||
modeId: string;
|
||||
modeId: string | null;
|
||||
encoding: string;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) { }
|
||||
|
||||
serialize(editorInput: EditorInput): string {
|
||||
serialize(editorInput: EditorInput): string | null {
|
||||
if (!this.textFileService.isHotExitEnabled) {
|
||||
return null; // never restore untitled unless hot exit is enabled
|
||||
}
|
||||
@@ -170,7 +170,7 @@ interface ISerializedSideBySideEditorInput {
|
||||
// Register Side by Side Editor Input Factory
|
||||
class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
serialize(editorInput: EditorInput): string {
|
||||
serialize(editorInput: EditorInput): string | null {
|
||||
const input = <SideBySideEditorInput>editorInput;
|
||||
|
||||
if (input.details && input.master) {
|
||||
@@ -198,7 +198,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
|
||||
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null {
|
||||
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
|
||||
|
||||
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
|
||||
@@ -266,7 +266,7 @@ export class QuickOpenActionContributor extends ActionBarContributor {
|
||||
return actions;
|
||||
}
|
||||
|
||||
private getEntry(context: any): IEditorQuickOpenEntry {
|
||||
private getEntry(context: any): IEditorQuickOpenEntry | null {
|
||||
if (!context || !context.element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor } from 'vs/workbench/common/editor';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
@@ -18,13 +18,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
|
||||
export const EDITOR_TITLE_HEIGHT = 35;
|
||||
|
||||
export interface IEditorPartCreationOptions {
|
||||
restorePreviousState: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
|
||||
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
|
||||
|
||||
export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
|
||||
iconTheme?: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
|
||||
showTabs: true,
|
||||
highlightModifiedTabs: false,
|
||||
@@ -77,7 +77,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
|
||||
* to return a promise that resolves to NULL to prevent the opening
|
||||
* alltogether.
|
||||
*/
|
||||
prevent(callback: () => Promise<IEditor>): void;
|
||||
prevent(callback: () => undefined | Promise<IEditor | undefined>): void;
|
||||
}
|
||||
|
||||
export interface IEditorGroupsAccessor {
|
||||
@@ -87,7 +87,7 @@ export interface IEditorGroupsAccessor {
|
||||
readonly partOptions: IEditorPartOptions;
|
||||
readonly onDidEditorPartOptionsChange: Event<IEditorPartOptionsChangeEvent>;
|
||||
|
||||
getGroup(identifier: GroupIdentifier): IEditorGroupView;
|
||||
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined;
|
||||
getGroups(order: GroupsOrder): IEditorGroupView[];
|
||||
|
||||
activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView;
|
||||
@@ -146,15 +146,3 @@ export interface EditorServiceImpl extends IEditorService {
|
||||
*/
|
||||
readonly onDidOpenEditorFail: Event<IEditorIdentifier>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A sub-interface of IEditorGroupsService to hide some workbench-core specific
|
||||
* methods from clients.
|
||||
*/
|
||||
export interface EditorGroupsServiceImpl extends IEditorGroupsService {
|
||||
|
||||
/**
|
||||
* A promise that resolves when groups have been restored.
|
||||
*/
|
||||
readonly whenRestored: Promise<void>;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IEditorInput, EditorInput, IEditorIdentifier, ConfirmResult, IEditorCom
|
||||
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, MOVE_ACTIVE_EDITOR_COMMAND_ID, NAVIGATE_IN_ACTIVE_GROUP_PREFIX, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
@@ -188,20 +188,22 @@ export class JoinTwoGroupsAction extends Action {
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
let sourceGroup: IEditorGroup;
|
||||
let sourceGroup: IEditorGroup | undefined;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = this.editorGroupService.getGroup(context.groupId);
|
||||
} else {
|
||||
sourceGroup = this.editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
|
||||
for (const targetGroupDirection of targetGroupDirections) {
|
||||
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
|
||||
if (targetGroup && sourceGroup !== targetGroup) {
|
||||
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
|
||||
if (sourceGroup) {
|
||||
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
|
||||
for (const targetGroupDirection of targetGroupDirections) {
|
||||
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
|
||||
if (targetGroup && sourceGroup !== targetGroup) {
|
||||
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
|
||||
|
||||
return Promise.resolve(true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +430,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
|
||||
if (entry) {
|
||||
const input = entry.getInput();
|
||||
if (input instanceof EditorInput) {
|
||||
return this.editorService.openEditor(input, entry.getOptions(), SIDE_GROUP);
|
||||
return this.editorService.openEditor(input, entry.getOptions() || undefined, SIDE_GROUP);
|
||||
}
|
||||
|
||||
const resourceInput = input as IResourceInput;
|
||||
@@ -441,7 +443,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry {
|
||||
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry | null {
|
||||
|
||||
// QuickOpenEntryGroup
|
||||
if (element instanceof QuickOpenEntryGroup) {
|
||||
@@ -491,13 +493,13 @@ export class CloseOneEditorAction extends Action {
|
||||
}
|
||||
|
||||
run(context?: IEditorCommandsContext): Promise<any> {
|
||||
let group: IEditorGroup;
|
||||
let editorIndex: number;
|
||||
let group: IEditorGroup | undefined;
|
||||
let editorIndex: number | undefined;
|
||||
if (context) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +581,7 @@ export class CloseLeftEditorsInGroupAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput, group: IEditorGroup } {
|
||||
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput | null, group: IEditorGroup | undefined } {
|
||||
if (context) {
|
||||
return { editor: context.editor, group: editorGroupService.getGroup(context.groupId) };
|
||||
}
|
||||
@@ -593,7 +595,7 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
clazz: string,
|
||||
clazz: string | undefined,
|
||||
private textFileService: ITextFileService,
|
||||
protected editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
@@ -629,9 +631,9 @@ export abstract class BaseCloseAllAction extends Action {
|
||||
|
||||
let saveOrRevertPromise: Promise<boolean>;
|
||||
if (confirm === ConfirmResult.DONT_SAVE) {
|
||||
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
|
||||
saveOrRevertPromise = this.textFileService.revertAll(undefined, { soft: true }).then(() => true);
|
||||
} else {
|
||||
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => r.success));
|
||||
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => !!r.success));
|
||||
}
|
||||
|
||||
return saveOrRevertPromise.then(success => {
|
||||
@@ -703,8 +705,8 @@ export class CloseEditorsInOtherGroupsAction extends Action {
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
const groupToSkip = context ? this.editorGroupService.getGroup(context.groupId) : this.editorGroupService.activeGroup;
|
||||
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => {
|
||||
if (g.id === groupToSkip.id) {
|
||||
return Promise.resolve(null);
|
||||
if (groupToSkip && g.id === groupToSkip.id) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return g.closeAllEditors();
|
||||
@@ -732,7 +734,7 @@ export class CloseEditorInAllGroupsAction extends Action {
|
||||
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => g.closeEditor(activeEditor)));
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,22 +750,24 @@ export class BaseMoveGroupAction extends Action {
|
||||
}
|
||||
|
||||
run(context?: IEditorIdentifier): Promise<any> {
|
||||
let sourceGroup: IEditorGroup;
|
||||
let sourceGroup: IEditorGroup | undefined;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = this.editorGroupService.getGroup(context.groupId);
|
||||
} else {
|
||||
sourceGroup = this.editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
const targetGroup = this.findTargetGroup(sourceGroup);
|
||||
if (targetGroup) {
|
||||
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
|
||||
if (sourceGroup) {
|
||||
const targetGroup = this.findTargetGroup(sourceGroup);
|
||||
if (targetGroup) {
|
||||
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup {
|
||||
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup | undefined {
|
||||
const targetNeighbours: GroupDirection[] = [this.direction];
|
||||
|
||||
// Allow the target group to be in alternative locations to support more
|
||||
@@ -882,14 +886,14 @@ export class ResetGroupSizesAction extends Action {
|
||||
export class MaximizeGroupAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.maximizeEditor';
|
||||
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Sidebar");
|
||||
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -897,7 +901,7 @@ export class MaximizeGroupAction extends Action {
|
||||
run(): Promise<any> {
|
||||
if (this.editorService.activeEditor) {
|
||||
this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS);
|
||||
this.partService.setSideBarHidden(true);
|
||||
this.layoutService.setSideBarHidden(true);
|
||||
}
|
||||
|
||||
return Promise.resolve(false);
|
||||
@@ -927,10 +931,14 @@ export abstract class BaseNavigateEditorAction extends Action {
|
||||
}
|
||||
|
||||
const group = this.editorGroupService.getGroup(groupId);
|
||||
return group.openEditor(editor);
|
||||
if (group) {
|
||||
return group.openEditor(editor);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
protected abstract navigate(): IEditorIdentifier;
|
||||
protected abstract navigate(): IEditorIdentifier | undefined;
|
||||
}
|
||||
|
||||
export class OpenNextEditor extends BaseNavigateEditorAction {
|
||||
@@ -947,12 +955,12 @@ export class OpenNextEditor extends BaseNavigateEditorAction {
|
||||
super(id, label, editorGroupService, editorService);
|
||||
}
|
||||
|
||||
protected navigate(): IEditorIdentifier {
|
||||
protected navigate(): IEditorIdentifier | undefined {
|
||||
|
||||
// Navigate in active group if possible
|
||||
const activeGroup = this.editorGroupService.activeGroup;
|
||||
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
|
||||
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
|
||||
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
|
||||
if (activeEditorIndex + 1 < activeGroupEditors.length) {
|
||||
return { editor: activeGroupEditors[activeEditorIndex + 1], groupId: activeGroup.id };
|
||||
}
|
||||
@@ -982,12 +990,12 @@ export class OpenPreviousEditor extends BaseNavigateEditorAction {
|
||||
super(id, label, editorGroupService, editorService);
|
||||
}
|
||||
|
||||
protected navigate(): IEditorIdentifier {
|
||||
protected navigate(): IEditorIdentifier | undefined {
|
||||
|
||||
// Navigate in active group if possible
|
||||
const activeGroup = this.editorGroupService.activeGroup;
|
||||
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
|
||||
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
|
||||
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
|
||||
if (activeEditorIndex > 0) {
|
||||
return { editor: activeGroupEditors[activeEditorIndex - 1], groupId: activeGroup.id };
|
||||
}
|
||||
@@ -1020,7 +1028,7 @@ export class OpenNextEditorInGroup extends BaseNavigateEditorAction {
|
||||
protected navigate(): IEditorIdentifier {
|
||||
const group = this.editorGroupService.activeGroup;
|
||||
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
|
||||
const index = editors.indexOf(group.activeEditor);
|
||||
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
|
||||
|
||||
return { editor: index + 1 < editors.length ? editors[index + 1] : editors[0], groupId: group.id };
|
||||
}
|
||||
@@ -1043,7 +1051,7 @@ export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction {
|
||||
protected navigate(): IEditorIdentifier {
|
||||
const group = this.editorGroupService.activeGroup;
|
||||
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
|
||||
const index = editors.indexOf(group.activeEditor);
|
||||
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
|
||||
|
||||
return { editor: index > 0 ? editors[index - 1] : editors[editors.length - 1], groupId: group.id };
|
||||
}
|
||||
@@ -1105,7 +1113,7 @@ export class NavigateForwardAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.forward();
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1121,7 +1129,7 @@ export class NavigateBackwardsAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.back();
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1137,7 +1145,7 @@ export class NavigateToLastEditLocationAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.openLastEditLocation();
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1153,7 +1161,7 @@ export class NavigateLastAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.last();
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1296,7 +1304,7 @@ export class OpenPreviousEditorFromHistoryAction extends Action {
|
||||
run(): Promise<any> {
|
||||
const keys = this.keybindingService.lookupKeybindings(this.id);
|
||||
|
||||
this.quickOpenService.show(null, { quickNavigateConfiguration: { keybindings: keys } });
|
||||
this.quickOpenService.show(undefined, { quickNavigateConfiguration: { keybindings: keys } });
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -1314,7 +1322,7 @@ export class OpenNextRecentlyUsedEditorAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.forward(true);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1330,7 +1338,7 @@ export class OpenPreviousRecentlyUsedEditorAction extends Action {
|
||||
run(): Promise<any> {
|
||||
this.historyService.back(true);
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as types from 'vs/base/common/types';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService, IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
|
||||
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
|
||||
@@ -16,8 +16,8 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IListService } from 'vs/platform/list/browser/listService';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
@@ -52,9 +52,9 @@ export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
|
||||
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';
|
||||
|
||||
export interface ActiveEditorMoveArguments {
|
||||
to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
|
||||
by?: 'tab' | 'group';
|
||||
value?: number;
|
||||
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
|
||||
by: 'tab' | 'group';
|
||||
value: number;
|
||||
}
|
||||
|
||||
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
|
||||
@@ -90,7 +90,24 @@ function registerActiveEditorMoveCommand(): void {
|
||||
{
|
||||
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
|
||||
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
|
||||
constraint: isActiveEditorMoveArg
|
||||
constraint: isActiveEditorMoveArg,
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'required': ['to'],
|
||||
'properties': {
|
||||
'to': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right']
|
||||
},
|
||||
'by': {
|
||||
'type': 'string',
|
||||
'enum': ['tab', 'group']
|
||||
},
|
||||
'value': {
|
||||
'type': 'number'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -113,7 +130,7 @@ function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null),
|
||||
}
|
||||
}
|
||||
|
||||
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
|
||||
function moveActiveTab(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
|
||||
const group = control.group;
|
||||
let index = group.getIndexOfEditor(control.input);
|
||||
switch (args.to) {
|
||||
@@ -141,12 +158,12 @@ function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, access
|
||||
group.moveEditor(control.input, group, { index });
|
||||
}
|
||||
|
||||
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
|
||||
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
const sourceGroup = control.group;
|
||||
let targetGroup: IEditorGroup;
|
||||
let targetGroup: IEditorGroup | undefined;
|
||||
|
||||
switch (args.to) {
|
||||
case 'left':
|
||||
@@ -340,7 +357,7 @@ function registerOpenEditorAtIndexCommands(): void {
|
||||
case 9: return KeyCode.KEY_9;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
throw new Error('invalid index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,7 +409,7 @@ function registerFocusEditorGroupAtIndexCommands(): void {
|
||||
case 7: return 'workbench.action.focusEighthEditorGroup';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
throw new Error('Invalid index');
|
||||
}
|
||||
|
||||
function toKeyCode(index: number): KeyCode {
|
||||
@@ -406,23 +423,27 @@ function registerFocusEditorGroupAtIndexCommands(): void {
|
||||
case 7: return KeyCode.KEY_8;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
throw new Error('Invalid index');
|
||||
}
|
||||
}
|
||||
|
||||
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
|
||||
let sourceGroup: IEditorGroup;
|
||||
let sourceGroup: IEditorGroup | undefined;
|
||||
if (context && typeof context.groupId === 'number') {
|
||||
sourceGroup = editorGroupService.getGroup(context.groupId);
|
||||
} else {
|
||||
sourceGroup = editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
if (!sourceGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add group
|
||||
const newGroup = editorGroupService.addGroup(sourceGroup, direction);
|
||||
|
||||
// Split editor (if it can be split)
|
||||
let editorToCopy: IEditorInput;
|
||||
let editorToCopy: IEditorInput | null;
|
||||
if (context && typeof context.editorIndex === 'number') {
|
||||
editorToCopy = sourceGroup.getEditor(context.editorIndex);
|
||||
} else {
|
||||
@@ -466,9 +487,14 @@ function registerCloseEditorCommands() {
|
||||
contexts.push({ groupId: activeGroup.id }); // active group as fallback
|
||||
}
|
||||
|
||||
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
|
||||
));
|
||||
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
if (group) {
|
||||
return group.closeEditors({ savedOnly: true });
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -486,9 +512,14 @@ function registerCloseEditorCommands() {
|
||||
distinctGroupIds.push(editorGroupService.activeGroup.id);
|
||||
}
|
||||
|
||||
return Promise.all(distinctGroupIds.map(groupId =>
|
||||
editorGroupService.getGroup(groupId).closeAllEditors()
|
||||
));
|
||||
return Promise.all(distinctGroupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
if (group) {
|
||||
return group.closeAllEditors();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -511,11 +542,15 @@ function registerCloseEditorCommands() {
|
||||
|
||||
return Promise.all(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
if (group) {
|
||||
const editors = coalesce(contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor));
|
||||
|
||||
return group.closeEditors(editors);
|
||||
return group.closeEditors(editors);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
});
|
||||
@@ -530,14 +565,16 @@ function registerCloseEditorCommands() {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const commandsContext = getCommandsContext(resourceOrContext, context);
|
||||
|
||||
let group: IEditorGroup;
|
||||
let group: IEditorGroup | undefined;
|
||||
if (commandsContext && typeof commandsContext.groupId === 'number') {
|
||||
group = editorGroupService.getGroup(commandsContext.groupId);
|
||||
} else {
|
||||
group = editorGroupService.activeGroup;
|
||||
}
|
||||
|
||||
editorGroupService.removeGroup(group);
|
||||
if (group) {
|
||||
editorGroupService.removeGroup(group);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -560,12 +597,16 @@ function registerCloseEditorCommands() {
|
||||
|
||||
return Promise.all(groupIds.map(groupId => {
|
||||
const group = editorGroupService.getGroup(groupId);
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
|
||||
if (group) {
|
||||
const editors = contexts
|
||||
.filter(context => context.groupId === groupId)
|
||||
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
|
||||
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
|
||||
|
||||
return group.closeEditors(editorsToClose);
|
||||
return group.closeEditors(editorsToClose);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}));
|
||||
}
|
||||
});
|
||||
@@ -619,7 +660,10 @@ function registerCloseEditorCommands() {
|
||||
|
||||
const commandsContext = getCommandsContext(resourceOrContext, context);
|
||||
if (commandsContext && typeof commandsContext.groupId === 'number') {
|
||||
editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
|
||||
const group = editorGroupService.getGroup(commandsContext.groupId);
|
||||
if (group) {
|
||||
editorGroupService.activateGroup(group); // we need the group to be active
|
||||
}
|
||||
}
|
||||
|
||||
return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
|
||||
@@ -642,7 +686,7 @@ function registerCloseEditorCommands() {
|
||||
});
|
||||
}
|
||||
|
||||
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
|
||||
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
|
||||
if (URI.isUri(resourceOrContext)) {
|
||||
return context;
|
||||
}
|
||||
@@ -658,11 +702,11 @@ function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, con
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
|
||||
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor?: IEditorInput, control?: IEditor } {
|
||||
|
||||
// Resolve from context
|
||||
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
|
||||
let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
|
||||
let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditor(context.editorIndex)) : undefined;
|
||||
let control = group ? group.activeControl : undefined;
|
||||
|
||||
// Fallback to active group as needed
|
||||
@@ -675,7 +719,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex
|
||||
return { group, editor, control };
|
||||
}
|
||||
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
|
||||
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext | undefined, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
|
||||
|
||||
// First check for a focused list to return the selected items from
|
||||
const list = listService.lastFocusedList;
|
||||
@@ -685,7 +729,9 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
|
||||
return { groupId: element.id, editorIndex: undefined };
|
||||
}
|
||||
|
||||
return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
|
||||
const group = editorGroupService.getGroup(element.groupId);
|
||||
|
||||
return { groupId: element.groupId, editorIndex: group ? group.getIndexOfEditor(element.editor) : -1 };
|
||||
};
|
||||
|
||||
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);
|
||||
@@ -697,7 +743,14 @@ 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 => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
|
||||
if (selection && selection.some(s => {
|
||||
if (isEditorGroup(s)) {
|
||||
return s.id === focus.groupId;
|
||||
}
|
||||
|
||||
const group = editorGroupService.getGroup(s.groupId);
|
||||
return s.groupId === focus.groupId && (group ? group.getIndexOfEditor(s.editor) : -1) === focus.editorIndex;
|
||||
})) {
|
||||
return selection.map(elementToContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
|
||||
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 { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
|
||||
export interface IOpenEditorResult {
|
||||
readonly control: BaseEditor;
|
||||
@@ -27,7 +29,7 @@ export class EditorControl extends Disposable {
|
||||
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
|
||||
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
|
||||
|
||||
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
|
||||
|
||||
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>());
|
||||
@@ -43,7 +45,7 @@ export class EditorControl extends Disposable {
|
||||
constructor(
|
||||
private parent: HTMLElement,
|
||||
private groupView: IEditorGroupView,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IProgressService progressService: IProgressService
|
||||
) {
|
||||
@@ -52,8 +54,8 @@ export class EditorControl extends Disposable {
|
||||
this.editorOperation = this._register(new LongRunningOperation(progressService));
|
||||
}
|
||||
|
||||
get activeControl() {
|
||||
return this._activeControl;
|
||||
get activeControl(): IVisibleEditor | null {
|
||||
return this._activeControl as IVisibleEditor | null;
|
||||
}
|
||||
|
||||
openEditor(editor: EditorInput, options?: EditorOptions): Promise<IOpenEditorResult> {
|
||||
@@ -66,7 +68,7 @@ export class EditorControl extends Disposable {
|
||||
const control = this.doShowEditorControl(descriptor);
|
||||
|
||||
// Set input
|
||||
return this.doSetInput(control, editor, options || null).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
|
||||
return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
|
||||
}
|
||||
|
||||
private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {
|
||||
@@ -170,7 +172,7 @@ export class EditorControl extends Disposable {
|
||||
|
||||
// Show progress while setting input after a certain timeout. If the workbench is opening
|
||||
// be more relaxed about progress showing by increasing the delay a little bit to reduce flicker.
|
||||
const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
|
||||
const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200);
|
||||
|
||||
// Call into editor control
|
||||
const editorWillChange = !inputMatches;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
@@ -28,7 +28,7 @@ class DropOverlay extends Themable {
|
||||
private container: HTMLElement;
|
||||
private overlay: HTMLElement;
|
||||
|
||||
private currentDropOperation: IDropOperation;
|
||||
private currentDropOperation?: IDropOperation;
|
||||
private _disposed: boolean;
|
||||
|
||||
private cleanupOverlayScheduler: RunOnceScheduler;
|
||||
@@ -103,12 +103,12 @@ class DropOverlay extends Themable {
|
||||
|
||||
// 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 (!isDraggingEditor && !isDraggingGroup) {
|
||||
if (!isDraggingEditor && !isDraggingGroup && e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
// Find out if operation is valid
|
||||
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true;
|
||||
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true;
|
||||
if (!isCopy) {
|
||||
const sourceGroupView = this.findSourceGroupView();
|
||||
if (sourceGroupView === this.groupView) {
|
||||
@@ -158,16 +158,16 @@ class DropOverlay extends Themable {
|
||||
}));
|
||||
}
|
||||
|
||||
private findSourceGroupView(): IEditorGroupView {
|
||||
private findSourceGroupView(): IEditorGroupView | undefined {
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
|
||||
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![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);
|
||||
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -189,59 +189,66 @@ class DropOverlay extends Themable {
|
||||
|
||||
// Check for group transfer
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier;
|
||||
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier;
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
|
||||
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
|
||||
return;
|
||||
}
|
||||
if (sourceGroup) {
|
||||
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Split to new group
|
||||
let targetGroup: IEditorGroupView;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
this.accessor.activateGroup(targetGroup);
|
||||
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 draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
const targetGroup = ensureTargetGroup();
|
||||
|
||||
// Return if the drop is a no-op
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
if (sourceGroup === targetGroup) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
// 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();
|
||||
// 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);
|
||||
// Close in source group unless we copy
|
||||
const copyEditor = this.isCopyOperation(event, draggedEditor);
|
||||
if (!copyEditor) {
|
||||
sourceGroup.closeEditor(draggedEditor.editor);
|
||||
}
|
||||
}
|
||||
|
||||
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
|
||||
@@ -250,7 +257,7 @@ class DropOverlay extends Themable {
|
||||
// Check for URI transfer
|
||||
else {
|
||||
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
|
||||
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus());
|
||||
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup!.focus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +305,7 @@ class DropOverlay extends Themable {
|
||||
// child.style.top = edgeHeightThreshold + 'px';
|
||||
|
||||
// No split if mouse is above certain threshold in the center of the view
|
||||
let splitDirection: GroupDirection;
|
||||
let splitDirection: GroupDirection | undefined;
|
||||
if (
|
||||
mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold &&
|
||||
mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold
|
||||
@@ -429,7 +436,7 @@ class DropOverlay extends Themable {
|
||||
|
||||
export class EditorDropTarget extends Themable {
|
||||
|
||||
private _overlay: DropOverlay;
|
||||
private _overlay?: DropOverlay;
|
||||
|
||||
private counter = 0;
|
||||
|
||||
@@ -447,7 +454,7 @@ export class EditorDropTarget extends Themable {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private get overlay(): DropOverlay {
|
||||
private get overlay(): DropOverlay | undefined {
|
||||
if (this._overlay && !this._overlay.disposed) {
|
||||
return this._overlay;
|
||||
}
|
||||
@@ -468,7 +475,7 @@ export class EditorDropTarget extends Themable {
|
||||
if (
|
||||
!this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) &&
|
||||
!this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) &&
|
||||
!event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
|
||||
event.dataTransfer && !event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
|
||||
) {
|
||||
event.dataTransfer.dropEffect = 'none';
|
||||
return; // unsupported transfer
|
||||
@@ -510,7 +517,7 @@ export class EditorDropTarget extends Themable {
|
||||
this.disposeOverlay();
|
||||
}
|
||||
|
||||
private findTargetGroupView(child: HTMLElement): IEditorGroupView {
|
||||
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
|
||||
const groups = this.accessor.groups;
|
||||
for (const groupView of groups) {
|
||||
if (isAncestor(child, groupView.element)) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/editorgroupview';
|
||||
|
||||
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor } from 'vs/workbench/common/editor';
|
||||
import { Event, Emitter, Relay } from 'vs/base/common/event';
|
||||
@@ -16,12 +17,11 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
|
||||
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { localize } from 'vs/nls';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -33,7 +33,6 @@ import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch
|
||||
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ActionRunner, IAction, Action } from 'vs/base/common/actions';
|
||||
@@ -45,10 +44,14 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
|
||||
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
import { guessMimeTypes } from 'vs/base/common/mime';
|
||||
import { extname } from 'vs/base/common/path';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
@@ -70,25 +73,25 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
//#region events
|
||||
|
||||
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
|
||||
|
||||
private _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onWillDispose(): Event<void> { return this._onWillDispose.event; }
|
||||
|
||||
private _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
|
||||
private readonly _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
|
||||
get onDidGroupChange(): Event<IGroupChangeEvent> { return this._onDidGroupChange.event; }
|
||||
|
||||
private _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
|
||||
private readonly _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
|
||||
get onWillOpenEditor(): Event<IEditorOpeningEvent> { return this._onWillOpenEditor.event; }
|
||||
|
||||
private _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
|
||||
private readonly _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
|
||||
get onDidOpenEditorFail(): Event<EditorInput> { return this._onDidOpenEditorFail.event; }
|
||||
|
||||
private _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
private readonly _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
get onWillCloseEditor(): Event<IEditorCloseEvent> { return this._onWillCloseEditor.event; }
|
||||
|
||||
private _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
private readonly _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
|
||||
get onDidCloseEditor(): Event<IEditorCloseEvent> { return this._onDidCloseEditor.event; }
|
||||
|
||||
//#endregion
|
||||
@@ -112,7 +115,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
private editorContainer: HTMLElement;
|
||||
private editorControl: EditorControl;
|
||||
|
||||
private ignoreOpenEditorErrors: boolean;
|
||||
private disposedEditorsWorker: RunOnceWorker<EditorInput>;
|
||||
|
||||
private mapEditorToPendingConfirmation: Map<EditorInput, Promise<boolean>> = new Map<EditorInput, Promise<boolean>>();
|
||||
@@ -130,6 +132,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IHashService private readonly hashService: IHashService,
|
||||
// {{SQL CARBON EDIT}}
|
||||
@ICommandService private commandService: ICommandService
|
||||
) {
|
||||
@@ -417,6 +420,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
const activeEditor = this._group.activeEditor;
|
||||
if (!activeEditor) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
options.pinned = this._group.isPinned(activeEditor); // preserve pinned state
|
||||
options.preserveFocus = true; // handle focus after editor is opened
|
||||
|
||||
@@ -458,14 +465,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
private onDidEditorOpen(editor: EditorInput): void {
|
||||
/* __GDPR__
|
||||
"editorOpened" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryDescriptor}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('editorOpened', editor.getTelemetryDescriptor());
|
||||
|
||||
// Telemetry
|
||||
this.toEditorTelemetryDescriptor(editor).then(descriptor => {
|
||||
/* __GDPR__
|
||||
"editorOpened" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryDescriptor}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('editorOpened', descriptor);
|
||||
});
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
@@ -496,14 +507,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
});
|
||||
|
||||
/* __GDPR__
|
||||
"editorClosed" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryDescriptor}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('editorClosed', event.editor.getTelemetryDescriptor());
|
||||
// Telemetry
|
||||
this.toEditorTelemetryDescriptor(event.editor).then(descriptor => {
|
||||
/* __GDPR__
|
||||
"editorClosed" : {
|
||||
"${include}": [
|
||||
"${EditorTelemetryDescriptor}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog('editorClosed', descriptor);
|
||||
});
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
@@ -513,6 +527,26 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_CLOSE, editor, editorIndex: event.index });
|
||||
}
|
||||
|
||||
private toEditorTelemetryDescriptor(editor: EditorInput): Thenable<object> {
|
||||
const descriptor = editor.getTelemetryDescriptor();
|
||||
|
||||
const resource = editor.getResource();
|
||||
if (resource && resource.fsPath) {
|
||||
return this.hashService.createSHA1(resource.fsPath).then(hashedPath => {
|
||||
descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hashedPath };
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"EditorTelemetryDescriptor" : {
|
||||
"resource": { "${inline}": [ "${URIDescriptor}" ] }
|
||||
}
|
||||
*/
|
||||
return descriptor;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(descriptor);
|
||||
}
|
||||
|
||||
private onDidEditorDispose(editor: EditorInput): void {
|
||||
|
||||
// To prevent race conditions, we handle disposed editors in our worker with a timeout
|
||||
@@ -524,7 +558,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
private handleDisposedEditors(editors: EditorInput[]): void {
|
||||
|
||||
// Split between visible and hidden editors
|
||||
let activeEditor: EditorInput;
|
||||
let activeEditor: EditorInput | undefined;
|
||||
const inactiveEditors: EditorInput[] = [];
|
||||
editors.forEach(editor => {
|
||||
if (this._group.isActive(editor)) {
|
||||
@@ -567,7 +601,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// Pin preview editor once user disables preview
|
||||
if (event.oldPartOptions.enablePreview && !event.newPartOptions.enablePreview) {
|
||||
this.pinEditor(this._group.previewEditor);
|
||||
if (this._group.previewEditor) {
|
||||
this.pinEditor(this._group.previewEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,15 +694,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return this._group.count;
|
||||
}
|
||||
|
||||
get activeControl(): BaseEditor {
|
||||
return this.editorControl ? this.editorControl.activeControl : undefined;
|
||||
get activeControl(): IVisibleEditor | undefined {
|
||||
return this.editorControl ? withNullAsUndefined(this.editorControl.activeControl) : undefined;
|
||||
}
|
||||
|
||||
get activeEditor(): EditorInput {
|
||||
get activeEditor(): EditorInput | null {
|
||||
return this._group.activeEditor;
|
||||
}
|
||||
|
||||
get previewEditor(): EditorInput {
|
||||
get previewEditor(): EditorInput | null {
|
||||
return this._group.previewEditor;
|
||||
}
|
||||
|
||||
@@ -686,7 +722,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return this.editors;
|
||||
}
|
||||
|
||||
getEditor(index: number): EditorInput {
|
||||
getEditor(index: number): EditorInput | null {
|
||||
return this._group.getEditor(index);
|
||||
}
|
||||
|
||||
@@ -711,7 +747,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._onDidFocus.fire();
|
||||
}
|
||||
|
||||
pinEditor(editor: EditorInput = this.activeEditor): void {
|
||||
pinEditor(editor: EditorInput | undefined = this.activeEditor || undefined): void {
|
||||
if (editor && !this._group.isPinned(editor)) {
|
||||
|
||||
// Update model
|
||||
@@ -749,7 +785,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return this.doOpenEditor(editor, options);
|
||||
}
|
||||
|
||||
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor> {
|
||||
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor | null> {
|
||||
|
||||
// Determine options
|
||||
const openEditorOptions: IEditorOpenOptions = {
|
||||
@@ -758,7 +794,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
active: this._group.count === 0 || !options || !options.inactive
|
||||
};
|
||||
|
||||
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.isPreview(this._group.activeEditor)) {
|
||||
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.activeEditor && this._group.isPreview(this._group.activeEditor)) {
|
||||
// Special case: we are to open an editor inactive and not pinned, but the current active
|
||||
// editor is also not pinned, which means it will get replaced with this one. As such,
|
||||
// the editor can only be active.
|
||||
@@ -787,13 +823,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this._group.openEditor(editor, openEditorOptions);
|
||||
|
||||
// Show editor
|
||||
return this.doShowEditor(editor, openEditorOptions.active, options);
|
||||
return this.doShowEditor(editor, !!openEditorOptions.active, options);
|
||||
}
|
||||
|
||||
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor> {
|
||||
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor | null> {
|
||||
|
||||
// Show in editor control if the active editor changed
|
||||
let openEditorPromise: Promise<IEditor>;
|
||||
let openEditorPromise: Promise<IEditor | null>;
|
||||
if (active) {
|
||||
openEditorPromise = this.editorControl.openEditor(editor, options).then(result => {
|
||||
|
||||
@@ -824,7 +860,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// 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) && !this.ignoreOpenEditorErrors) {
|
||||
if (this.isRestored && !isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
if (isErrorWithActions(error)) {
|
||||
actions.primary = (error as IErrorWithActions).actions;
|
||||
@@ -836,7 +872,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
actions
|
||||
});
|
||||
|
||||
Event.once(handle.onDidClose)(() => dispose(actions.primary));
|
||||
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
|
||||
}
|
||||
|
||||
// Event
|
||||
@@ -861,10 +897,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Do not modify original array
|
||||
editors = editors.slice(0);
|
||||
|
||||
let result: IEditor;
|
||||
let result: IEditor | null;
|
||||
|
||||
// Use the first editor as active editor
|
||||
const { editor, options } = editors.shift();
|
||||
const { editor, options } = editors.shift()!;
|
||||
return this.openEditor(editor, options).then(activeEditor => {
|
||||
result = activeEditor; // this can be NULL if the opening failed
|
||||
|
||||
@@ -964,7 +1000,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
//#region closeEditor()
|
||||
|
||||
closeEditor(editor: EditorInput = this.activeEditor): Promise<void> {
|
||||
closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise<void> {
|
||||
if (!editor) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -976,11 +1012,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
// Do close
|
||||
this.doCloseEditor(editor);
|
||||
this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined);
|
||||
});
|
||||
}
|
||||
|
||||
private doCloseEditor(editor: EditorInput, focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
|
||||
private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
|
||||
|
||||
// Closing the active editor of the group is a bit more work
|
||||
if (this._group.isActive(editor)) {
|
||||
@@ -996,7 +1032,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
this.titleAreaControl.closeEditor(editor);
|
||||
}
|
||||
|
||||
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
|
||||
private doCloseActiveEditor(focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
|
||||
const editorToClose = this.activeEditor;
|
||||
const restoreFocus = this.shouldRestoreFocus(this.element);
|
||||
|
||||
@@ -1021,32 +1057,34 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
// Update model
|
||||
this._group.closeEditor(editorToClose);
|
||||
if (editorToClose) {
|
||||
this._group.closeEditor(editorToClose);
|
||||
}
|
||||
|
||||
// Open next active if there are more to show
|
||||
const nextActiveEditor = this._group.activeEditor;
|
||||
if (nextActiveEditor) {
|
||||
const options = EditorOptions.create({ preserveFocus: !focusNext });
|
||||
|
||||
// When closing an editor due to an error we can end up in a loop where we continue closing
|
||||
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
|
||||
// repeated errors in this case to the user. As such, if we open the next editor and we are
|
||||
// in a scope of a previous editor failing, we silence the input errors until the editor is
|
||||
// opened.
|
||||
// opened by setting ignoreError: true.
|
||||
if (fromError) {
|
||||
this.ignoreOpenEditorErrors = true;
|
||||
options.ignoreError = true;
|
||||
}
|
||||
|
||||
const options = !focusNext ? EditorOptions.create({ preserveFocus: true }) : undefined;
|
||||
this.openEditor(nextActiveEditor, options).then(() => {
|
||||
this.ignoreOpenEditorErrors = false;
|
||||
});
|
||||
this.openEditor(nextActiveEditor, options);
|
||||
}
|
||||
|
||||
// Otherwise we are empty, so clear from editor control and send event
|
||||
else {
|
||||
|
||||
// Forward to editor control
|
||||
this.editorControl.closeEditor(editorToClose);
|
||||
if (editorToClose) {
|
||||
this.editorControl.closeEditor(editorToClose);
|
||||
}
|
||||
|
||||
// Restore focus to group container as needed unless group gets closed
|
||||
if (restoreFocus && !closeEmptyGroup) {
|
||||
@@ -1085,7 +1123,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return Promise.resolve(false); // no veto
|
||||
}
|
||||
|
||||
const editor = editors.shift();
|
||||
const editor = editors.shift()!;
|
||||
|
||||
// To prevent multiple confirmation dialogs from showing up one after the other
|
||||
// we check if a pending confirmation is currently showing and if so, join that
|
||||
@@ -1157,7 +1195,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
//#region closeEditors()
|
||||
|
||||
closeEditors(args: EditorInput[] | ICloseEditorsFilter): Promise<void> {
|
||||
closeEditors(args: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise<void> {
|
||||
if (this.isEmpty()) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -1171,7 +1209,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
}
|
||||
|
||||
// Do close
|
||||
this.doCloseEditors(editors);
|
||||
this.doCloseEditors(editors, options);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1205,7 +1243,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
return editorsToClose;
|
||||
}
|
||||
|
||||
private doCloseEditors(editors: EditorInput[]): void {
|
||||
private doCloseEditors(editors: EditorInput[], options?: ICloseEditorOptions): void {
|
||||
|
||||
// Close all inactive editors first
|
||||
let closeActiveEditor = false;
|
||||
@@ -1219,7 +1257,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
// Close active editor last if contained in editors list to close
|
||||
if (closeActiveEditor) {
|
||||
this.doCloseActiveEditor();
|
||||
this.doCloseActiveEditor(options && options.preserveFocus ? false : undefined);
|
||||
}
|
||||
|
||||
// Forward to title control
|
||||
@@ -1278,7 +1316,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
replaceEditors(editors: EditorReplacement[]): Promise<void> {
|
||||
|
||||
// Extract active vs. inactive replacements
|
||||
let activeReplacement: EditorReplacement;
|
||||
let activeReplacement: EditorReplacement | undefined;
|
||||
const inactiveReplacements: EditorReplacement[] = [];
|
||||
editors.forEach(({ editor, replacement, options }) => {
|
||||
if (editor.isDirty()) {
|
||||
@@ -1314,11 +1352,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Open inactive editor
|
||||
this.doOpenEditor(replacement, options);
|
||||
|
||||
// Close replaced inactive edior
|
||||
this.doCloseInactiveEditor(editor);
|
||||
|
||||
// Forward to title control
|
||||
this.titleAreaControl.closeEditor(editor);
|
||||
// Close replaced inactive editor unless they match
|
||||
if (!editor.matches(replacement)) {
|
||||
this.doCloseInactiveEditor(editor);
|
||||
this.titleAreaControl.closeEditor(editor);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle active last
|
||||
@@ -1327,11 +1365,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
// Open replacement as active editor
|
||||
const openEditorResult = this.doOpenEditor(activeReplacement.replacement, activeReplacement.options);
|
||||
|
||||
// Close previous active editor
|
||||
this.doCloseInactiveEditor(activeReplacement.editor);
|
||||
|
||||
// Forward to title control
|
||||
this.titleAreaControl.closeEditor(activeReplacement.editor);
|
||||
// Close replaced active editor unless they match
|
||||
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
|
||||
this.doCloseInactiveEditor(activeReplacement.editor);
|
||||
this.titleAreaControl.closeEditor(activeReplacement.editor);
|
||||
}
|
||||
|
||||
return openEditorResult.then(() => undefined);
|
||||
}
|
||||
@@ -1384,8 +1422,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
get maximumWidth(): number { return this.editorControl.maximumWidth; }
|
||||
get maximumHeight(): number { return this.editorControl.maximumHeight; }
|
||||
|
||||
private _onDidChange = this._register(new Relay<{ width: number; height: number; }>());
|
||||
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
|
||||
private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
|
||||
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
this.dimension = new Dimension(width, height);
|
||||
@@ -1430,7 +1468,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
constructor(
|
||||
private _group: GroupIdentifier,
|
||||
private _editor: EditorInput,
|
||||
private _options: EditorOptions
|
||||
private _options: EditorOptions | undefined
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -1442,7 +1480,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
|
||||
return this._editor;
|
||||
}
|
||||
|
||||
get options(): EditorOptions {
|
||||
get options(): EditorOptions | undefined {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
@@ -1464,10 +1502,10 @@ export interface EditorReplacement {
|
||||
registerThemingParticipant((theme, collector, environment) => {
|
||||
|
||||
// Letterpress
|
||||
const letterpress = `resources/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
|
||||
const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
|
||||
collector.addRule(`
|
||||
.monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress {
|
||||
background-image: url('${URI.file(join(environment.appRoot, letterpress)).toString()}')
|
||||
background-image: url('${require.toUrl(letterpress)}')
|
||||
}
|
||||
`);
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ import { Part } from 'vs/workbench/browser/part';
|
||||
import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom';
|
||||
import { Event, Emitter, Relay } from 'vs/base/common/event';
|
||||
import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid, ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
|
||||
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid';
|
||||
import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, IEditorPartCreationOptions } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -29,7 +29,8 @@ import { Color } from 'vs/base/common/color';
|
||||
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
|
||||
import { IView, orthogonal, LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
|
||||
@@ -50,8 +51,8 @@ class GridWidgetView<T extends IView> implements IView {
|
||||
get minimumHeight(): number { return this.gridWidget ? this.gridWidget.minimumHeight : 0; }
|
||||
get maximumHeight(): number { return this.gridWidget ? this.gridWidget.maximumHeight : Number.POSITIVE_INFINITY; }
|
||||
|
||||
private _onDidChange = new Relay<{ width: number; height: number; }>();
|
||||
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
|
||||
private _onDidChange = new Relay<{ width: number; height: number; } | undefined>();
|
||||
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
|
||||
|
||||
private _gridWidget: Grid<T>;
|
||||
|
||||
@@ -83,44 +84,43 @@ class GridWidgetView<T extends IView> implements IView {
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor, ISerializableView {
|
||||
export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state';
|
||||
private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview';
|
||||
|
||||
//#region Events
|
||||
|
||||
private _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
|
||||
private readonly _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
|
||||
get onDidLayout(): Event<Dimension> { return this._onDidLayout.event; }
|
||||
|
||||
private _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
private readonly _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidActiveGroupChange(): Event<IEditorGroupView> { return this._onDidActiveGroupChange.event; }
|
||||
|
||||
private _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
private readonly _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
|
||||
|
||||
private readonly _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidAddGroup(): Event<IEditorGroupView> { return this._onDidAddGroup.event; }
|
||||
|
||||
private _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
private readonly _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidRemoveGroup(): Event<IEditorGroupView> { return this._onDidRemoveGroup.event; }
|
||||
|
||||
private _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
private readonly _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidMoveGroup(): Event<IEditorGroupView> { return this._onDidMoveGroup.event; }
|
||||
|
||||
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; }>());
|
||||
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; }>());
|
||||
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
|
||||
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>());
|
||||
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 _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidPreferredSizeChange(): Event<void> { return this._onDidPreferredSizeChange.event; }
|
||||
|
||||
private _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
|
||||
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
|
||||
|
||||
//#endregion
|
||||
|
||||
private dimension: Dimension;
|
||||
private _preferredSize: Dimension;
|
||||
private _preferredSize: Dimension | undefined;
|
||||
|
||||
private workspaceMemento: object;
|
||||
private globalMemento: object;
|
||||
@@ -139,22 +139,14 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
private _whenRestored: Promise<void>;
|
||||
private whenRestoredResolve: () => void;
|
||||
|
||||
element: HTMLElement;
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
priority: LayoutPriority = LayoutPriority.High;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
private restorePreviousState: boolean,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
super(Parts.EDITOR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
|
||||
|
||||
@@ -172,7 +164,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
|
||||
private enforcedPartOptions: IEditorPartOptions[] = [];
|
||||
|
||||
private _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
|
||||
private readonly _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
|
||||
get onDidEditorPartOptionsChange(): Event<IEditorPartOptionsChangeEvent> { return this._onDidEditorPartOptionsChange.event; }
|
||||
|
||||
private registerListeners(): void {
|
||||
@@ -216,6 +208,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
|
||||
//#region IEditorGroupsService
|
||||
|
||||
private _dimension: Dimension;
|
||||
get dimension(): Dimension { return this._dimension; }
|
||||
|
||||
get activeGroup(): IEditorGroupView {
|
||||
return this._activeGroup;
|
||||
}
|
||||
@@ -229,11 +224,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
get orientation(): GroupOrientation {
|
||||
if (!this.gridWidget) {
|
||||
return undefined; // we have not been created yet
|
||||
}
|
||||
|
||||
return this.gridWidget.orientation === Orientation.VERTICAL ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
|
||||
return (this.gridWidget && this.gridWidget.orientation === Orientation.VERTICAL) ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
|
||||
}
|
||||
|
||||
get whenRestored(): Promise<void> {
|
||||
@@ -246,7 +237,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return this.groups;
|
||||
|
||||
case GroupsOrder.MOST_RECENTLY_ACTIVE:
|
||||
const mostRecentActive = this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId));
|
||||
const mostRecentActive = coalesce(this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId)));
|
||||
|
||||
// there can be groups that got never active, even though they exist. in this case
|
||||
// make sure to ust append them at the end so that all groups are returned properly
|
||||
@@ -270,7 +261,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
}
|
||||
|
||||
getGroup(identifier: GroupIdentifier): IEditorGroupView {
|
||||
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined {
|
||||
return this.groupViews.get(identifier);
|
||||
}
|
||||
|
||||
@@ -282,7 +273,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
// by location
|
||||
return this.doFindGroupByLocation(scope.location, source, wrap);
|
||||
if (typeof scope.location === 'number') {
|
||||
return this.doFindGroupByLocation(scope.location, source, wrap);
|
||||
}
|
||||
|
||||
throw new Error('invalid arguments');
|
||||
}
|
||||
|
||||
private doFindGroupByDirection(direction: GroupDirection, source: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView {
|
||||
@@ -422,7 +417,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.doCreateGridControlWithState(gridDescriptor, activeGroup.id, currentGroupViews);
|
||||
|
||||
// Layout
|
||||
this.doLayout(this.dimension);
|
||||
this.doLayout(this._dimension);
|
||||
|
||||
// Update container
|
||||
this.updateContainer();
|
||||
@@ -506,7 +501,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return newGroupView;
|
||||
}
|
||||
|
||||
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup): IEditorGroupView {
|
||||
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup | null): IEditorGroupView {
|
||||
|
||||
// Label: just use the number of existing groups as label
|
||||
const label = this.getGroupLabel(this.count + 1);
|
||||
@@ -601,7 +596,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
}
|
||||
|
||||
private toGridViewOrientation(orientation: GroupOrientation, fallback?: Orientation): Orientation {
|
||||
private toGridViewOrientation(orientation: GroupOrientation, fallback: Orientation): Orientation {
|
||||
if (typeof orientation === 'number') {
|
||||
return orientation === GroupOrientation.HORIZONTAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
|
||||
}
|
||||
@@ -742,21 +737,26 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
}
|
||||
|
||||
private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
|
||||
let groupView: IEditorGroupView | undefined;
|
||||
if (typeof group === 'number') {
|
||||
group = this.getGroup(group);
|
||||
groupView = this.getGroup(group);
|
||||
} else {
|
||||
groupView = group;
|
||||
}
|
||||
|
||||
if (!group) {
|
||||
if (!groupView) {
|
||||
throw new Error('Invalid editor group provided!');
|
||||
}
|
||||
|
||||
return group;
|
||||
return groupView;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Part
|
||||
|
||||
readonly priority: LayoutPriority = LayoutPriority.High;
|
||||
|
||||
get minimumWidth(): number { return this.centeredLayoutWidget.minimumWidth; }
|
||||
get maximumWidth(): number { return this.centeredLayoutWidget.maximumWidth; }
|
||||
get minimumHeight(): number { return this.centeredLayoutWidget.minimumHeight; }
|
||||
@@ -783,7 +783,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return this.theme.getColor(EDITOR_GROUP_BORDER) || this.theme.getColor(contrastBorder) || Color.transparent;
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
this.container.style.backgroundColor = this.getColor(editorBackground);
|
||||
|
||||
const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder, background: this.theme.getColor(EDITOR_PANE_BACKGROUND) || Color.transparent };
|
||||
@@ -791,7 +791,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.centeredLayoutWidget.styles(separatorBorderStyle);
|
||||
}
|
||||
|
||||
createContentArea(parent: HTMLElement): HTMLElement {
|
||||
createContentArea(parent: HTMLElement, options?: IEditorPartCreationOptions): HTMLElement {
|
||||
|
||||
// Container
|
||||
this.element = parent;
|
||||
@@ -800,7 +800,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
parent.appendChild(this.container);
|
||||
|
||||
// Grid control with center layout
|
||||
this.doCreateGridControl();
|
||||
this.doCreateGridControl(options);
|
||||
|
||||
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
|
||||
|
||||
@@ -819,15 +819,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return this.centeredLayoutWidget.isActive();
|
||||
}
|
||||
|
||||
private doCreateGridControl(): void {
|
||||
private doCreateGridControl(options?: IEditorPartCreationOptions): void {
|
||||
|
||||
// Grid Widget (with previous UI state)
|
||||
if (this.restorePreviousState) {
|
||||
this.doCreateGridControlWithPreviousState();
|
||||
let restoreError = false;
|
||||
if (!options || options.restorePreviousState) {
|
||||
restoreError = !this.doCreateGridControlWithPreviousState();
|
||||
}
|
||||
|
||||
// Grid Widget (no previous UI state or failed to restore)
|
||||
if (!this.gridWidget) {
|
||||
if (!this.gridWidget || restoreError) {
|
||||
const initialGroup = this.doCreateGroupView();
|
||||
this.doSetGridWidget(new SerializableGrid(initialGroup));
|
||||
|
||||
@@ -842,7 +843,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.updateContainer();
|
||||
}
|
||||
|
||||
private doCreateGridControlWithPreviousState(): void {
|
||||
private doCreateGridControlWithPreviousState(): boolean {
|
||||
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
|
||||
if (uiState && uiState.serializedGrid) {
|
||||
try {
|
||||
@@ -856,25 +857,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
// Ensure last active group has focus
|
||||
this._activeGroup.focus();
|
||||
} catch (error) {
|
||||
this.handleGridRestoreError(error, uiState);
|
||||
|
||||
// Log error
|
||||
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(uiState)})`));
|
||||
|
||||
// Clear any state we have from the failing restore
|
||||
this.groupViews.forEach(group => group.dispose());
|
||||
this.groupViews.clear();
|
||||
this.mostRecentActiveGroups = [];
|
||||
|
||||
return false; // failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleGridRestoreError(error: Error, state: IEditorPartUIState): void {
|
||||
|
||||
// Log error
|
||||
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(state)})`));
|
||||
|
||||
// Clear any state we have from the failing restore
|
||||
if (this.gridWidget) {
|
||||
this.doSetGridWidget();
|
||||
}
|
||||
|
||||
this.groupViews.forEach(group => group.dispose());
|
||||
this.groupViews.clear();
|
||||
this._activeGroup = undefined;
|
||||
this.mostRecentActiveGroups = [];
|
||||
return true; // success
|
||||
}
|
||||
|
||||
private doCreateGridControlWithState(serializedGrid: ISerializedGrid, activeGroupId: GroupIdentifier, editorGroupViewsToReuse?: IEditorGroupView[]): void {
|
||||
@@ -893,7 +889,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => {
|
||||
let groupView: IEditorGroupView;
|
||||
if (reuseGroupViews.length > 0) {
|
||||
groupView = reuseGroupViews.shift();
|
||||
groupView = reuseGroupViews.shift()!;
|
||||
} else {
|
||||
groupView = this.doCreateGroupView(serializedEditorGroup);
|
||||
}
|
||||
@@ -924,7 +920,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.doSetGridWidget(gridWidget);
|
||||
}
|
||||
|
||||
private doSetGridWidget(gridWidget?: SerializableGrid<IEditorGroupView>): void {
|
||||
private doSetGridWidget(gridWidget: SerializableGrid<IEditorGroupView>): void {
|
||||
if (this.gridWidget) {
|
||||
this.gridWidget.dispose();
|
||||
}
|
||||
@@ -932,9 +928,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
this.gridWidget = gridWidget;
|
||||
this.gridWidgetView.gridWidget = gridWidget;
|
||||
|
||||
if (gridWidget) {
|
||||
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
|
||||
}
|
||||
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
|
||||
|
||||
this.onDidSetGridWidget.fire(undefined);
|
||||
}
|
||||
@@ -962,23 +956,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
return this.groupViews.size === 1 && this._activeGroup.isEmpty();
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2));
|
||||
layout(width: number, height: number): void {
|
||||
|
||||
this.doLayout(sizes[1]);
|
||||
// Layout contents
|
||||
const contentAreaSize = super.layoutContents(width, height).contentSize;
|
||||
|
||||
if (dim1 instanceof Dimension) {
|
||||
return sizes;
|
||||
}
|
||||
// Layout editor container
|
||||
this.doLayout(contentAreaSize);
|
||||
}
|
||||
|
||||
private doLayout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
this._dimension = dimension;
|
||||
|
||||
// Layout Grid
|
||||
this.centeredLayoutWidget.layout(this.dimension.width, this.dimension.height);
|
||||
this.centeredLayoutWidget.layout(this._dimension.width, this._dimension.height);
|
||||
|
||||
// Event
|
||||
this._onDidLayout.fire(dimension);
|
||||
@@ -1039,3 +1030,5 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IEditorGroupsService, EditorPart);
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import 'vs/css!./media/editorpicker';
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
|
||||
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
@@ -14,7 +13,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
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/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorInput, toResource } from 'vs/workbench/common/editor';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
@@ -33,12 +32,12 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
|
||||
getLabelOptions(): IIconLabelValueOptions {
|
||||
return {
|
||||
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
|
||||
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource() || undefined),
|
||||
italic: !this._group.isPinned(this.editor)
|
||||
};
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
getLabel() {
|
||||
return this.editor.getName();
|
||||
}
|
||||
|
||||
@@ -50,7 +49,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
return this._group;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
getResource() {
|
||||
return toResource(this.editor, { supportSideBySide: true });
|
||||
}
|
||||
|
||||
@@ -58,7 +57,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel());
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
getDescription() {
|
||||
return this.editor.getDescription();
|
||||
}
|
||||
|
||||
@@ -109,7 +108,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
|
||||
e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -7,16 +7,15 @@ import 'vs/css!./media/editorstatus';
|
||||
import * as nls from 'vs/nls';
|
||||
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { extname, basename } from 'vs/base/common/resources';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { Language } from 'vs/base/common/platform';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
|
||||
@@ -27,7 +26,6 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
|
||||
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||
@@ -41,7 +39,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
|
||||
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
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 { Schemas } from 'vs/base/common/network';
|
||||
@@ -51,6 +49,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
|
||||
|
||||
class SideBySideEditorEncodingSupport implements IEncodingSupport {
|
||||
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
|
||||
@@ -64,10 +66,7 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport {
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
|
||||
|
||||
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
|
||||
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | null {
|
||||
|
||||
// Untitled Editor
|
||||
if (input instanceof UntitledEditorInput) {
|
||||
@@ -104,25 +103,14 @@ interface IEditorSelectionStatus {
|
||||
class StateChange {
|
||||
_stateChangeBrand: void;
|
||||
|
||||
indentation: boolean;
|
||||
selectionStatus: boolean;
|
||||
mode: boolean;
|
||||
encoding: boolean;
|
||||
EOL: boolean;
|
||||
tabFocusMode: boolean;
|
||||
screenReaderMode: boolean;
|
||||
metadata: boolean;
|
||||
|
||||
constructor() {
|
||||
this.indentation = false;
|
||||
this.selectionStatus = false;
|
||||
this.mode = false;
|
||||
this.encoding = false;
|
||||
this.EOL = false;
|
||||
this.tabFocusMode = false;
|
||||
this.screenReaderMode = false;
|
||||
this.metadata = false;
|
||||
}
|
||||
indentation: boolean = false;
|
||||
selectionStatus: boolean = false;
|
||||
mode: boolean = false;
|
||||
encoding: boolean = false;
|
||||
EOL: boolean = false;
|
||||
tabFocusMode: boolean = false;
|
||||
screenReaderMode: boolean = false;
|
||||
metadata: boolean = false;
|
||||
|
||||
combine(other: StateChange) {
|
||||
this.indentation = this.indentation || other.indentation;
|
||||
@@ -134,6 +122,17 @@ class StateChange {
|
||||
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
|
||||
this.metadata = this.metadata || other.metadata;
|
||||
}
|
||||
|
||||
public hasChanges(): boolean {
|
||||
return this.indentation
|
||||
|| this.selectionStatus
|
||||
|| this.mode
|
||||
|| this.encoding
|
||||
|| this.EOL
|
||||
|| this.tabFocusMode
|
||||
|| this.screenReaderMode
|
||||
|| this.metadata;
|
||||
}
|
||||
}
|
||||
|
||||
interface StateDelta {
|
||||
@@ -144,33 +143,33 @@ interface StateDelta {
|
||||
indentation?: string;
|
||||
tabFocusMode?: boolean;
|
||||
screenReaderMode?: boolean;
|
||||
metadata?: string;
|
||||
metadata?: string | null;
|
||||
}
|
||||
|
||||
class State {
|
||||
private _selectionStatus: string;
|
||||
get selectionStatus(): string { return this._selectionStatus; }
|
||||
private _selectionStatus: string | null | undefined;
|
||||
get selectionStatus(): string | null | undefined { return this._selectionStatus; }
|
||||
|
||||
private _mode: string;
|
||||
get mode(): string { return this._mode; }
|
||||
private _mode: string | null | undefined;
|
||||
get mode(): string | null | undefined { return this._mode; }
|
||||
|
||||
private _encoding: string;
|
||||
get encoding(): string { return this._encoding; }
|
||||
private _encoding: string | null | undefined;
|
||||
get encoding(): string | null | undefined { return this._encoding; }
|
||||
|
||||
private _EOL: string;
|
||||
get EOL(): string { return this._EOL; }
|
||||
private _EOL: string | null | undefined;
|
||||
get EOL(): string | null | undefined { return this._EOL; }
|
||||
|
||||
private _indentation: string;
|
||||
get indentation(): string { return this._indentation; }
|
||||
private _indentation: string | null | undefined;
|
||||
get indentation(): string | null | undefined { return this._indentation; }
|
||||
|
||||
private _tabFocusMode: boolean;
|
||||
get tabFocusMode(): boolean { return this._tabFocusMode; }
|
||||
private _tabFocusMode: boolean | null | undefined;
|
||||
get tabFocusMode(): boolean | null | undefined { return this._tabFocusMode; }
|
||||
|
||||
private _screenReaderMode: boolean;
|
||||
get screenReaderMode(): boolean { return this._screenReaderMode; }
|
||||
private _screenReaderMode: boolean | null | undefined;
|
||||
get screenReaderMode(): boolean | null | undefined { return this._screenReaderMode; }
|
||||
|
||||
private _metadata: string;
|
||||
get metadata(): string { return this._metadata; }
|
||||
private _metadata: string | null | undefined;
|
||||
get metadata(): string | null | undefined { return this._metadata; }
|
||||
|
||||
constructor() {
|
||||
this._selectionStatus = null;
|
||||
@@ -183,70 +182,58 @@ class State {
|
||||
}
|
||||
|
||||
update(update: StateDelta): StateChange {
|
||||
const e = new StateChange();
|
||||
let somethingChanged = false;
|
||||
const change = new StateChange();
|
||||
|
||||
if (typeof update.selectionStatus !== 'undefined') {
|
||||
if ('selectionStatus' in update) {
|
||||
if (this._selectionStatus !== update.selectionStatus) {
|
||||
this._selectionStatus = update.selectionStatus;
|
||||
somethingChanged = true;
|
||||
e.selectionStatus = true;
|
||||
change.selectionStatus = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.indentation !== 'undefined') {
|
||||
if ('indentation' in update) {
|
||||
if (this._indentation !== update.indentation) {
|
||||
this._indentation = update.indentation;
|
||||
somethingChanged = true;
|
||||
e.indentation = true;
|
||||
change.indentation = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.mode !== 'undefined') {
|
||||
if ('mode' in update) {
|
||||
if (this._mode !== update.mode) {
|
||||
this._mode = update.mode;
|
||||
somethingChanged = true;
|
||||
e.mode = true;
|
||||
change.mode = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.encoding !== 'undefined') {
|
||||
if ('encoding' in update) {
|
||||
if (this._encoding !== update.encoding) {
|
||||
this._encoding = update.encoding;
|
||||
somethingChanged = true;
|
||||
e.encoding = true;
|
||||
change.encoding = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.EOL !== 'undefined') {
|
||||
if ('EOL' in update) {
|
||||
if (this._EOL !== update.EOL) {
|
||||
this._EOL = update.EOL;
|
||||
somethingChanged = true;
|
||||
e.EOL = true;
|
||||
change.EOL = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.tabFocusMode !== 'undefined') {
|
||||
if ('tabFocusMode' in update) {
|
||||
if (this._tabFocusMode !== update.tabFocusMode) {
|
||||
this._tabFocusMode = update.tabFocusMode;
|
||||
somethingChanged = true;
|
||||
e.tabFocusMode = true;
|
||||
change.tabFocusMode = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.screenReaderMode !== 'undefined') {
|
||||
if ('screenReaderMode' in update) {
|
||||
if (this._screenReaderMode !== update.screenReaderMode) {
|
||||
this._screenReaderMode = update.screenReaderMode;
|
||||
somethingChanged = true;
|
||||
e.screenReaderMode = true;
|
||||
change.screenReaderMode = true;
|
||||
}
|
||||
}
|
||||
if (typeof update.metadata !== 'undefined') {
|
||||
if ('metadata' in update) {
|
||||
if (this._metadata !== update.metadata) {
|
||||
this._metadata = update.metadata;
|
||||
somethingChanged = true;
|
||||
e.metadata = true;
|
||||
change.metadata = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (somethingChanged) {
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,34 +247,51 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
|
||||
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
|
||||
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
|
||||
|
||||
function setDisplay(el: HTMLElement, desiredValue: string): void {
|
||||
if (el.style.display !== desiredValue) {
|
||||
el.style.display = desiredValue;
|
||||
|
||||
class StatusBarItem {
|
||||
private _showing = true;
|
||||
|
||||
constructor(
|
||||
private readonly element: HTMLElement,
|
||||
title: string,
|
||||
) {
|
||||
this.setVisible(false);
|
||||
this.element.title = title;
|
||||
}
|
||||
|
||||
public set textContent(value: string) {
|
||||
this.element.textContent = value;
|
||||
}
|
||||
|
||||
public set onclick(value: () => void) {
|
||||
this.element.onclick = value;
|
||||
}
|
||||
|
||||
public setVisible(shouldShow: boolean): void {
|
||||
if (shouldShow !== this._showing) {
|
||||
this._showing = shouldShow;
|
||||
this.element.style.display = shouldShow ? '' : 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
function show(el: HTMLElement): void {
|
||||
setDisplay(el, '');
|
||||
}
|
||||
function hide(el: HTMLElement): void {
|
||||
setDisplay(el, 'none');
|
||||
}
|
||||
|
||||
|
||||
export class EditorStatus implements IStatusbarItem {
|
||||
private state: State;
|
||||
private element: HTMLElement;
|
||||
private tabFocusModeElement: HTMLElement;
|
||||
private screenRedearModeElement: HTMLElement;
|
||||
private indentationElement: HTMLElement;
|
||||
private selectionElement: HTMLElement;
|
||||
private encodingElement: HTMLElement;
|
||||
private eolElement: HTMLElement;
|
||||
private modeElement: HTMLElement;
|
||||
private metadataElement: HTMLElement;
|
||||
private tabFocusModeElement: StatusBarItem;
|
||||
private screenRedearModeElement: StatusBarItem;
|
||||
private indentationElement: StatusBarItem;
|
||||
private selectionElement: StatusBarItem;
|
||||
private encodingElement: StatusBarItem;
|
||||
private eolElement: StatusBarItem;
|
||||
private modeElement: StatusBarItem;
|
||||
private metadataElement: StatusBarItem;
|
||||
private toDispose: IDisposable[];
|
||||
private activeEditorListeners: IDisposable[];
|
||||
private delayedRender: IDisposable;
|
||||
private toRender: StateChange;
|
||||
private screenReaderNotification: INotificationHandle;
|
||||
private delayedRender: IDisposable | null;
|
||||
private toRender: StateChange | null;
|
||||
private screenReaderNotification: INotificationHandle | null;
|
||||
|
||||
constructor(
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@@ -296,8 +300,9 @@ export class EditorStatus implements IStatusbarItem {
|
||||
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
|
||||
@INotificationService private readonly notificationService: INotificationService
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
|
||||
) {
|
||||
this.toDispose = [];
|
||||
this.activeEditorListeners = [];
|
||||
@@ -307,59 +312,57 @@ export class EditorStatus implements IStatusbarItem {
|
||||
render(container: HTMLElement): IDisposable {
|
||||
this.element = append(container, $('.editor-statusbar-item'));
|
||||
|
||||
this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info'));
|
||||
this.tabFocusModeElement.title = nls.localize('disableTabMode', "Disable Accessibility Mode");
|
||||
this.tabFocusModeElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')),
|
||||
nls.localize('disableTabMode', "Disable Accessibility Mode"));
|
||||
this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick();
|
||||
this.tabFocusModeElement.textContent = nlsTabFocusMode;
|
||||
hide(this.tabFocusModeElement);
|
||||
|
||||
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
|
||||
this.screenRedearModeElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-screenreadermode.status-bar-info')),
|
||||
nlsScreenReaderDetectedTitle);
|
||||
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
|
||||
this.screenRedearModeElement.title = nlsScreenReaderDetectedTitle;
|
||||
this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick();
|
||||
hide(this.screenRedearModeElement);
|
||||
|
||||
this.selectionElement = append(this.element, $('a.editor-status-selection'));
|
||||
this.selectionElement.title = nls.localize('gotoLine', "Go to Line");
|
||||
this.selectionElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-selection')),
|
||||
nls.localize('gotoLine', "Go to Line"));
|
||||
this.selectionElement.onclick = () => this.onSelectionClick();
|
||||
hide(this.selectionElement);
|
||||
|
||||
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
|
||||
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
|
||||
this.indentationElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-indentation')),
|
||||
nls.localize('selectIndentation', "Select Indentation"));
|
||||
this.indentationElement.onclick = () => this.onIndentationClick();
|
||||
hide(this.indentationElement);
|
||||
|
||||
this.encodingElement = append(this.element, $('a.editor-status-encoding'));
|
||||
this.encodingElement.title = nls.localize('selectEncoding', "Select Encoding");
|
||||
this.encodingElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-encoding')),
|
||||
nls.localize('selectEncoding', "Select Encoding"));
|
||||
this.encodingElement.onclick = () => this.onEncodingClick();
|
||||
hide(this.encodingElement);
|
||||
|
||||
this.eolElement = append(this.element, $('a.editor-status-eol'));
|
||||
this.eolElement.title = nls.localize('selectEOL', "Select End of Line Sequence");
|
||||
this.eolElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-eol')),
|
||||
nls.localize('selectEOL', "Select End of Line Sequence"));
|
||||
this.eolElement.onclick = () => this.onEOLClick();
|
||||
hide(this.eolElement);
|
||||
|
||||
this.modeElement = append(this.element, $('a.editor-status-mode'));
|
||||
this.modeElement.title = nls.localize('selectLanguageMode', "Select Language Mode");
|
||||
this.modeElement = new StatusBarItem(
|
||||
append(this.element, $('a.editor-status-mode')),
|
||||
nls.localize('selectLanguageMode', "Select Language Mode"));
|
||||
this.modeElement.onclick = () => this.onModeClick();
|
||||
hide(this.modeElement);
|
||||
|
||||
this.metadataElement = append(this.element, $('span.editor-status-metadata'));
|
||||
this.metadataElement.title = nls.localize('fileInfo', "File Information");
|
||||
hide(this.metadataElement);
|
||||
this.metadataElement = new StatusBarItem(
|
||||
append(this.element, $('span.editor-status-metadata')),
|
||||
nls.localize('fileInfo', "File Information"));
|
||||
|
||||
this.delayedRender = null;
|
||||
this.toRender = null;
|
||||
|
||||
this.toDispose.push(
|
||||
{
|
||||
dispose: () => {
|
||||
if (this.delayedRender) {
|
||||
this.delayedRender.dispose();
|
||||
this.delayedRender = null;
|
||||
}
|
||||
toDisposable(() => {
|
||||
if (this.delayedRender) {
|
||||
this.delayedRender.dispose();
|
||||
this.delayedRender = null;
|
||||
}
|
||||
},
|
||||
}),
|
||||
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
|
||||
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
|
||||
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
|
||||
@@ -371,7 +374,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
|
||||
private updateState(update: StateDelta): void {
|
||||
const changed = this.state.update(update);
|
||||
if (!changed) {
|
||||
if (!changed.hasChanges()) {
|
||||
// Nothing really changed
|
||||
return;
|
||||
}
|
||||
@@ -382,7 +385,9 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.delayedRender = null;
|
||||
const toRender = this.toRender;
|
||||
this.toRender = null;
|
||||
this._renderNow(toRender);
|
||||
if (toRender) {
|
||||
this._renderNow(toRender);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.toRender.combine(changed);
|
||||
@@ -391,79 +396,71 @@ export class EditorStatus implements IStatusbarItem {
|
||||
|
||||
private _renderNow(changed: StateChange): void {
|
||||
if (changed.tabFocusMode) {
|
||||
if (this.state.tabFocusMode && this.state.tabFocusMode === true) {
|
||||
show(this.tabFocusModeElement);
|
||||
} else {
|
||||
hide(this.tabFocusModeElement);
|
||||
}
|
||||
this.tabFocusModeElement.setVisible(!!this.state.tabFocusMode);
|
||||
}
|
||||
|
||||
if (changed.screenReaderMode) {
|
||||
if (this.state.screenReaderMode && this.state.screenReaderMode === true) {
|
||||
show(this.screenRedearModeElement);
|
||||
} else {
|
||||
hide(this.screenRedearModeElement);
|
||||
}
|
||||
this.screenRedearModeElement.setVisible(!!this.state.screenReaderMode);
|
||||
}
|
||||
|
||||
if (changed.indentation) {
|
||||
if (this.state.indentation) {
|
||||
this.indentationElement.textContent = this.state.indentation;
|
||||
show(this.indentationElement);
|
||||
this.indentationElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.indentationElement);
|
||||
this.indentationElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed.selectionStatus) {
|
||||
if (this.state.selectionStatus && !this.state.screenReaderMode) {
|
||||
this.selectionElement.textContent = this.state.selectionStatus;
|
||||
show(this.selectionElement);
|
||||
this.selectionElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.selectionElement);
|
||||
this.selectionElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed.encoding) {
|
||||
if (this.state.encoding) {
|
||||
this.encodingElement.textContent = this.state.encoding;
|
||||
show(this.encodingElement);
|
||||
this.encodingElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.encodingElement);
|
||||
this.encodingElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed.EOL) {
|
||||
if (this.state.EOL) {
|
||||
this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF;
|
||||
show(this.eolElement);
|
||||
this.eolElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.eolElement);
|
||||
this.eolElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed.mode) {
|
||||
if (this.state.mode) {
|
||||
this.modeElement.textContent = this.state.mode;
|
||||
show(this.modeElement);
|
||||
this.modeElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.modeElement);
|
||||
this.modeElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed.metadata) {
|
||||
if (this.state.metadata) {
|
||||
this.metadataElement.textContent = this.state.metadata;
|
||||
show(this.metadataElement);
|
||||
this.metadataElement.setVisible(true);
|
||||
} else {
|
||||
hide(this.metadataElement);
|
||||
this.metadataElement.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getSelectionLabel(info: IEditorSelectionStatus): string {
|
||||
private getSelectionLabel(info: IEditorSelectionStatus): string | undefined {
|
||||
if (!info || !info.selections) {
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (info.selections.length === 1) {
|
||||
@@ -482,7 +479,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
return strings.format(nlsMultiSelection, info.selections.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private onModeClick(): void {
|
||||
@@ -502,8 +499,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
if (!this.screenReaderNotification) {
|
||||
this.screenReaderNotification = this.notificationService.prompt(
|
||||
Severity.Info,
|
||||
// {{SQL CARBON EDIT}}
|
||||
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio?"),
|
||||
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio? (Certain features like folding, minimap or word wrap are disabled when using a screen reader)"),
|
||||
[{
|
||||
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
|
||||
run: () => {
|
||||
@@ -548,7 +544,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
|
||||
private updateStatusBar(): void {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
const activeCodeEditor = activeControl ? getCodeEditor(activeControl.getControl()) : undefined;
|
||||
const activeCodeEditor = activeControl ? types.withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
|
||||
|
||||
// Update all states
|
||||
this.onScreenReaderModeChange(activeCodeEditor);
|
||||
@@ -586,11 +582,13 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelContent((e) => {
|
||||
this.onEOLChange(activeCodeEditor);
|
||||
|
||||
let selections = activeCodeEditor.getSelections();
|
||||
for (const change of e.changes) {
|
||||
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
break;
|
||||
const selections = activeCodeEditor.getSelections();
|
||||
if (selections) {
|
||||
for (const change of e.changes) {
|
||||
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
|
||||
this.onSelectionChange(activeCodeEditor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -630,8 +628,8 @@ export class EditorStatus implements IStatusbarItem {
|
||||
}
|
||||
}
|
||||
|
||||
private onModeChange(editorWidget: ICodeEditor): void {
|
||||
let info: StateDelta = { mode: null };
|
||||
private onModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
let info: StateDelta = { mode: undefined };
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget) {
|
||||
@@ -639,15 +637,15 @@ export class EditorStatus implements IStatusbarItem {
|
||||
if (textModel) {
|
||||
// Compute mode
|
||||
const modeId = textModel.getLanguageIdentifier().language;
|
||||
info = { mode: this.modeService.getLanguageName(modeId) };
|
||||
info = { mode: this.modeService.getLanguageName(modeId) || undefined };
|
||||
}
|
||||
}
|
||||
|
||||
this.updateState(info);
|
||||
}
|
||||
|
||||
private onIndentationChange(editorWidget: ICodeEditor): void {
|
||||
const update: StateDelta = { indentation: null };
|
||||
private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const update: StateDelta = { indentation: undefined };
|
||||
|
||||
if (editorWidget) {
|
||||
const model = editorWidget.getModel();
|
||||
@@ -655,7 +653,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
const modelOpts = model.getOptions();
|
||||
update.indentation = (
|
||||
modelOpts.insertSpaces
|
||||
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.tabSize)
|
||||
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.indentSize)
|
||||
: nls.localize({ key: 'tabSize', comment: ['Tab corresponds to the tab key'] }, "Tab Size: {0}", modelOpts.tabSize)
|
||||
);
|
||||
}
|
||||
@@ -664,8 +662,8 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState(update);
|
||||
}
|
||||
|
||||
private onMetadataChange(editor: IBaseEditor): void {
|
||||
const update: StateDelta = { metadata: null };
|
||||
private onMetadataChange(editor: IBaseEditor | undefined): void {
|
||||
const update: StateDelta = { metadata: undefined };
|
||||
|
||||
if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) {
|
||||
update.metadata = editor.getMetadata();
|
||||
@@ -676,12 +674,12 @@ export class EditorStatus implements IStatusbarItem {
|
||||
|
||||
private _promptedScreenReader: boolean = false;
|
||||
|
||||
private onScreenReaderModeChange(editorWidget: ICodeEditor): void {
|
||||
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
|
||||
let screenReaderMode = false;
|
||||
|
||||
// We only support text based editors
|
||||
if (editorWidget) {
|
||||
const screenReaderDetected = (browser.getAccessibilitySupport() === AccessibilitySupport.Enabled);
|
||||
const screenReaderDetected = (this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled);
|
||||
if (screenReaderDetected) {
|
||||
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
|
||||
if (screenReaderConfiguration === 'auto') {
|
||||
@@ -705,7 +703,7 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState({ screenReaderMode: screenReaderMode });
|
||||
}
|
||||
|
||||
private onSelectionChange(editorWidget: ICodeEditor): void {
|
||||
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const info: IEditorSelectionStatus = {};
|
||||
|
||||
// We only support text based editors
|
||||
@@ -719,13 +717,13 @@ export class EditorStatus implements IStatusbarItem {
|
||||
const textModel = editorWidget.getModel();
|
||||
if (textModel) {
|
||||
info.selections.forEach(selection => {
|
||||
info.charactersSelected += textModel.getValueLengthInRange(selection);
|
||||
info.charactersSelected! += textModel.getValueLengthInRange(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 visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition()!);
|
||||
|
||||
let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
|
||||
selectionClone = new Selection(
|
||||
@@ -742,8 +740,8 @@ export class EditorStatus implements IStatusbarItem {
|
||||
this.updateState({ selectionStatus: this.getSelectionLabel(info) });
|
||||
}
|
||||
|
||||
private onEOLChange(editorWidget: ICodeEditor): void {
|
||||
const info: StateDelta = { EOL: null };
|
||||
private onEOLChange(editorWidget: ICodeEditor | undefined): void {
|
||||
const info: StateDelta = { EOL: undefined };
|
||||
|
||||
if (editorWidget && !editorWidget.getConfiguration().readOnly) {
|
||||
const codeEditorModel = editorWidget.getModel();
|
||||
@@ -760,11 +758,11 @@ export class EditorStatus implements IStatusbarItem {
|
||||
return;
|
||||
}
|
||||
|
||||
const info: StateDelta = { encoding: null };
|
||||
const info: StateDelta = { encoding: undefined };
|
||||
|
||||
// We only support text based editors
|
||||
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
|
||||
const encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(e.input);
|
||||
const encodingSupport: IEncodingSupport | null = e.input ? toEditorWithEncodingSupport(e.input) : null;
|
||||
if (encodingSupport) {
|
||||
const rawEncoding = encodingSupport.getEncoding();
|
||||
const encodingInfo = SUPPORTED_ENCODINGS[rawEncoding];
|
||||
@@ -798,11 +796,11 @@ export class EditorStatus implements IStatusbarItem {
|
||||
private isActiveEditor(control: IBaseEditor): boolean {
|
||||
const activeControl = this.editorService.activeControl;
|
||||
|
||||
return activeControl && activeControl === control;
|
||||
return !!activeControl && activeControl === control;
|
||||
}
|
||||
}
|
||||
|
||||
function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
|
||||
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
|
||||
if (!codeEditor) {
|
||||
return false;
|
||||
}
|
||||
@@ -811,7 +809,7 @@ function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
|
||||
}
|
||||
|
||||
function isWritableBaseEditor(e: IBaseEditor): boolean {
|
||||
return e && isWritableCodeEditor(getCodeEditor(e.getControl()));
|
||||
return e && isWritableCodeEditor(getCodeEditor(e.getControl()) || undefined);
|
||||
}
|
||||
|
||||
export class ShowLanguageExtensionsAction extends Action {
|
||||
@@ -844,7 +842,7 @@ export class ChangeModeAction extends Action {
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@@ -860,19 +858,19 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
const textModel = activeTextEditorWidget.getModel();
|
||||
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
|
||||
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: true }) : null;
|
||||
|
||||
let hasLanguageSupport = !!resource;
|
||||
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
if (resource && resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
|
||||
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
|
||||
}
|
||||
|
||||
// Compute mode
|
||||
let currentModeId: string;
|
||||
let currentModeId: string | undefined;
|
||||
let modeId: string;
|
||||
if (textModel) {
|
||||
modeId = textModel.getLanguageIdentifier().language;
|
||||
currentModeId = this.modeService.getLanguageName(modeId);
|
||||
currentModeId = this.modeService.getLanguageName(modeId) || undefined;
|
||||
}
|
||||
|
||||
// All languages are valid picks
|
||||
@@ -886,7 +884,7 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// construct a fake resource to be able to show nice icons if any
|
||||
let fakeResource: uri;
|
||||
let fakeResource: uri | undefined;
|
||||
const extensions = this.modeService.getExtensions(lang);
|
||||
if (extensions && extensions.length) {
|
||||
fakeResource = uri.file(extensions[0]);
|
||||
@@ -912,8 +910,8 @@ export class ChangeModeAction extends Action {
|
||||
let configureModeAssociations: IQuickPickItem;
|
||||
let configureModeSettings: IQuickPickItem;
|
||||
let galleryAction: Action;
|
||||
if (hasLanguageSupport) {
|
||||
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
|
||||
if (hasLanguageSupport && resource) {
|
||||
const ext = extname(resource) || basename(resource);
|
||||
|
||||
galleryAction = this.instantiationService.createInstance(ShowLanguageExtensionsAction, ext);
|
||||
if (galleryAction.enabled) {
|
||||
@@ -947,7 +945,9 @@ export class ChangeModeAction extends Action {
|
||||
|
||||
// User decided to permanently configure associations, return right after
|
||||
if (pick === configureModeAssociations) {
|
||||
this.configureFileAssociation(resource);
|
||||
if (resource) {
|
||||
this.configureFileAssociation(resource);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -980,10 +980,15 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// Find mode
|
||||
let languageSelection: ILanguageSelection;
|
||||
let languageSelection: ILanguageSelection | undefined;
|
||||
if (pick === autoDetectMode) {
|
||||
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
|
||||
languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
|
||||
if (textModel) {
|
||||
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
|
||||
const resource = toResource(activeEditor.input, { supportSideBySide: true });
|
||||
if (resource) {
|
||||
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
languageSelection = this.modeService.createByLanguageName(pick.label);
|
||||
}
|
||||
@@ -991,10 +996,9 @@ export class ChangeModeAction extends Action {
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Change mode
|
||||
models.forEach(textModel => {
|
||||
let self = this;
|
||||
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then((newTextModel) => {
|
||||
if (newTextModel) {
|
||||
self.modelService.setMode(newTextModel, languageSelection);
|
||||
this.modelService.setMode(newTextModel, languageSelection);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1002,9 +1006,9 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
private configureFileAssociation(resource: uri): void {
|
||||
const extension = paths.extname(resource.fsPath);
|
||||
const basename = paths.basename(resource.fsPath);
|
||||
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(basename);
|
||||
const extension = extname(resource);
|
||||
const base = basename(resource);
|
||||
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(base);
|
||||
|
||||
const languages = this.modeService.getRegisteredLanguageNames();
|
||||
const picks: IQuickPickItem[] = languages.sort().map((lang, index) => {
|
||||
@@ -1018,15 +1022,15 @@ export class ChangeModeAction extends Action {
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).then(language => {
|
||||
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }).then(language => {
|
||||
if (language) {
|
||||
const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG);
|
||||
|
||||
let associationKey: string;
|
||||
if (extension && basename[0] !== '.') {
|
||||
if (extension && base[0] !== '.') {
|
||||
associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of <name>.<ext>
|
||||
} else {
|
||||
associationKey = basename; // otherwise use the basename (e.g. .gitignore, Dockerfile)
|
||||
associationKey = base; // otherwise use the basename (e.g. .gitignore, Dockerfile)
|
||||
}
|
||||
|
||||
// If the association is already being made in the workspace, make sure to target workspace settings
|
||||
@@ -1036,11 +1040,7 @@ export class ChangeModeAction extends Action {
|
||||
}
|
||||
|
||||
// Make sure to write into the value of the target and not the merged value from USER and WORKSPACE config
|
||||
let currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user);
|
||||
if (!currentAssociations) {
|
||||
currentAssociations = Object.create(null);
|
||||
}
|
||||
|
||||
const currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user) || Object.create(null);
|
||||
currentAssociations[associationKey] = language.id;
|
||||
|
||||
this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target);
|
||||
@@ -1089,7 +1089,7 @@ class ChangeIndentationAction extends Action {
|
||||
return {
|
||||
id: a.id,
|
||||
label: a.label,
|
||||
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
|
||||
detail: Language.isDefaultVariant() ? undefined : a.alias,
|
||||
run: () => {
|
||||
activeTextEditorWidget.focus();
|
||||
a.run();
|
||||
@@ -1140,7 +1140,7 @@ export class ChangeEOLAction extends Action {
|
||||
return this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }).then(eol => {
|
||||
if (eol) {
|
||||
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
|
||||
if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) {
|
||||
if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
|
||||
const textModel = activeCodeEditor.getModel();
|
||||
textModel.pushEOL(eol.eol);
|
||||
}
|
||||
@@ -1170,15 +1170,18 @@ export class ChangeEncodingAction extends Action {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
|
||||
let activeControl = this.editorService.activeControl;
|
||||
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeControl.input);
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (!activeControl) {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
|
||||
}
|
||||
const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input);
|
||||
if (!encodingSupport) {
|
||||
return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
|
||||
}
|
||||
|
||||
let saveWithEncodingPick: IQuickPickItem;
|
||||
let reopenWithEncodingPick: IQuickPickItem;
|
||||
if (language === LANGUAGE_DEFAULT) {
|
||||
if (Language.isDefaultVariant()) {
|
||||
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
|
||||
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
|
||||
} else {
|
||||
@@ -1200,7 +1203,7 @@ export class ChangeEncodingAction extends Action {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resource = toResource(activeControl.input, { supportSideBySide: true });
|
||||
const resource = toResource(activeControl!.input, { supportSideBySide: true });
|
||||
|
||||
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
|
||||
.then(() => {
|
||||
@@ -1213,10 +1216,10 @@ export class ChangeEncodingAction extends Action {
|
||||
.then((guessedEncoding: string) => {
|
||||
const isReopenWithEncoding = (action === reopenWithEncodingPick);
|
||||
|
||||
const configuredEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding');
|
||||
const configuredEncoding = this.textResourceConfigurationService.getValue(types.withNullAsUndefined(resource), 'files.encoding');
|
||||
|
||||
let directMatchIndex: number;
|
||||
let aliasMatchIndex: number;
|
||||
let directMatchIndex: number | undefined;
|
||||
let aliasMatchIndex: number | undefined;
|
||||
|
||||
// All encodings are valid picks
|
||||
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
|
||||
@@ -1258,12 +1261,16 @@ export class ChangeEncodingAction extends Action {
|
||||
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
|
||||
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
|
||||
}).then(encoding => {
|
||||
if (encoding) {
|
||||
activeControl = this.editorService.activeControl;
|
||||
encodingSupport = toEditorWithEncodingSupport(activeControl.input);
|
||||
if (encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
|
||||
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
|
||||
}
|
||||
if (!encoding) {
|
||||
return;
|
||||
}
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (!activeControl) {
|
||||
return;
|
||||
}
|
||||
const encodingSupport = toEditorWithEncodingSupport(activeControl.input);
|
||||
if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
|
||||
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,17 +14,16 @@ import { buttonBackground, buttonForeground, editorBackground, editorForeground,
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extname } from 'vs/base/common/paths';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { Disposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
|
||||
export class FloatingClickWidget extends Widget implements IOverlayWidget {
|
||||
|
||||
private _onClick: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onClick: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onClick(): Event<void> { return this._onClick.event; }
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
@@ -108,7 +107,8 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
|
||||
private editor: ICodeEditor,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -139,8 +139,12 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
|
||||
return false; // we need a model
|
||||
}
|
||||
|
||||
if (model.uri.scheme !== Schemas.file || extname(model.uri.fsPath) !== `.${WORKSPACE_EXTENSION}`) {
|
||||
return false; // we need a local workspace file
|
||||
if (!hasWorkspaceFileExtension(model.uri.fsPath)) {
|
||||
return false; // we need a workspace file
|
||||
}
|
||||
|
||||
if (!this.fileService.canHandleResource(model.uri)) {
|
||||
return false; // needs to be backed by a file service
|
||||
}
|
||||
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
@@ -159,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
|
||||
this._register(this.openWorkspaceButton.onClick(() => {
|
||||
const model = this.editor.getModel();
|
||||
if (model) {
|
||||
this.windowService.openWindow([model.uri]);
|
||||
this.windowService.openWindow([{ uri: model.uri, typeHint: 'file' }]);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.2;}
|
||||
.st1{fill:#646464;}
|
||||
</style>
|
||||
<g class="st0">
|
||||
<path class="st1" d="M12.7,19.2L12.1,19H7l-1.4,3.8L3.1,16L2,19H0v1.5C0,22.4,3.1,24,7,24c2.3,0,4.3-0.6,5.6-1.4l-0.1-0.3l-0.4-0.9
|
||||
l-0.4-0.9l0.9-0.4l0.3-0.1c0-0.1,0-0.2,0-0.2c0-0.1,0-0.2,0-0.2L12.7,19.2z"/>
|
||||
<path class="st1" d="M12.5,11c-1.3,0.8-3.3,1.3-5.5,1.3c-3.9,0-7-1.6-7-3.5V18h1.3l1.8-5l2.5,6.8L6.3,18h5.8l0-0.1l0.4-0.9l0.4-0.9
|
||||
l0.9,0.4l0.2,0.1V11L12.5,11L12.5,11z"/>
|
||||
<g>
|
||||
<path class="st1" d="M19,4c0.4,0,0.8,0.1,1.2,0.2c0.4,0.2,0.7,0.4,1,0.6c0.3,0.3,0.5,0.6,0.6,0.9C21.9,6.2,22,6.6,22,7
|
||||
c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.6C19.8,9.9,19.4,10,19,10h-9c-0.6,0-1.1-0.1-1.6-0.3
|
||||
C8,9.5,7.5,9.2,7.2,8.8C6.8,8.5,6.5,8,6.3,7.6C6.1,7.1,6,6.6,6,6c0-0.6,0.1-1.1,0.3-1.6C6.5,4,6.8,3.5,7.2,3.2
|
||||
c0.4-0.4,0.8-0.6,1.3-0.9C8.9,2.1,9.4,2,10,2c0.2,0,0.5,0,0.7,0.1c0.2-0.3,0.4-0.6,0.7-0.9c0.3-0.3,0.6-0.5,0.9-0.7
|
||||
c0.3-0.2,0.7-0.3,1-0.4C13.7,0.1,14.1,0,14.5,0c0.6,0,1.1,0.1,1.6,0.3c0.5,0.2,1,0.5,1.4,0.8c0.4,0.4,0.7,0.8,1,1.3
|
||||
C18.7,2.9,18.9,3.4,19,4z"/>
|
||||
</g>
|
||||
<path class="st1" d="M21,19.5c0,0.3,0,0.6-0.1,0.9l1.1,0.4l-0.4,0.9l-1.1-0.4c-0.3,0.5-0.7,0.9-1.2,1.2l0.4,1.1L18.8,24l-0.4-1.1
|
||||
C18.1,23,17.8,23,17.5,23s-0.6,0-0.9-0.1L16.2,24l-0.9-0.4l0.4-1.1c-0.5-0.3-0.9-0.7-1.2-1.2l-1.1,0.4L13,20.8l1.1-0.4
|
||||
C14,20.1,14,19.8,14,19.5s0-0.6,0.1-0.9L13,18.2l0.4-0.9l1.1,0.4c0.3-0.5,0.7-0.9,1.2-1.2l-0.4-1.1l0.9-0.4l0.4,1.1
|
||||
c0.3-0.1,0.6-0.1,0.9-0.1s0.6,0,0.9,0.1l0.4-1.1l0.9,0.4l-0.4,1.1c0.5,0.3,0.9,0.7,1.2,1.2l1.1-0.4l0.4,0.9l-1.1,0.4
|
||||
C21,19,21,19.2,21,19.5z M17.5,22c0.3,0,0.7-0.1,1-0.2c0.3-0.1,0.6-0.3,0.8-0.5s0.4-0.5,0.5-0.8c0.1-0.3,0.2-0.6,0.2-1
|
||||
s-0.1-0.7-0.2-1c-0.1-0.3-0.3-0.6-0.5-0.8s-0.5-0.4-0.8-0.5c-0.3-0.1-0.6-0.2-1-0.2c-0.3,0-0.7,0.1-1,0.2c-0.3,0.1-0.6,0.3-0.8,0.5
|
||||
c-0.2,0.2-0.4,0.5-0.5,0.8c-0.1,0.3-0.2,0.6-0.2,1c0,0.3,0.1,0.7,0.2,1c0.1,0.3,0.3,0.6,0.5,0.8s0.5,0.4,0.8,0.5
|
||||
C16.8,22,17.1,22,17.5,22z"/>
|
||||
<path class="st1" d="M9.9,11c-0.6,0-1.3-0.1-1.8-0.4C7.4,10.4,6.9,10,6.5,9.5C6,9.1,5.7,8.6,5.4,8C5.2,7.5,5.1,7,5,6.5
|
||||
C2.5,6.9,1,8,1,8.8c0,1,2.3,2.5,6,2.5C8,11.3,9,11.2,9.9,11z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
|
||||
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
|
||||
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
|
||||
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.175h1.296
|
||||
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
|
||||
<g>
|
||||
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
|
||||
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.578,21.99,7c0,0.418-0.078,0.806-0.234,1.168
|
||||
c-0.155,0.362-0.371,0.68-0.645,0.952c-0.273,0.273-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
|
||||
c-0.551,0-1.069-0.104-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833c-0.36-0.36-0.646-0.786-0.854-1.272
|
||||
c-0.209-0.487-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
|
||||
C7.526,2.809,7.95,2.523,8.437,2.314c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
|
||||
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.651c0.321-0.176,0.666-0.312,1.028-0.407
|
||||
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.101,1.636,0.305c0.513,0.203,0.968,0.483,1.362,0.839
|
||||
c0.396,0.357,0.723,0.781,0.98,1.271C18.729,2.906,18.893,3.437,18.968,4.002z"/>
|
||||
</g>
|
||||
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
|
||||
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
|
||||
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
|
||||
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
|
||||
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
|
||||
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
|
||||
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
|
||||
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
|
||||
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
|
||||
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
|
||||
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
|
||||
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
|
||||
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.136-1.839-0.383C7.443,10.351,6.91,9.992,6.458,9.54
|
||||
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.492,5.094,6.997,5.045,6.487C2.525,6.88,1,7.981,1,8.833c0,1.044,2.281,2.5,5.997,2.5
|
||||
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
39
src/vs/workbench/browser/parts/editor/media/letterpress.svg
Normal file
39
src/vs/workbench/browser/parts/editor/media/letterpress.svg
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
||||
<g style="opacity:0.2;">
|
||||
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
|
||||
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
|
||||
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
|
||||
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.174h1.296
|
||||
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
|
||||
<g>
|
||||
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
|
||||
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.579,21.99,7c0,0.418-0.078,0.806-0.234,1.168
|
||||
c-0.155,0.363-0.371,0.68-0.645,0.953s-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
|
||||
c-0.551,0-1.069-0.103-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833C6.804,8.473,6.518,8.047,6.31,7.561
|
||||
c-0.209-0.488-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
|
||||
C7.526,2.809,7.95,2.524,8.437,2.315c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
|
||||
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.652c0.321-0.176,0.666-0.312,1.028-0.407
|
||||
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.1,1.636,0.304c0.513,0.203,0.968,0.483,1.362,0.839
|
||||
c0.396,0.357,0.723,0.782,0.98,1.271C18.729,2.907,18.893,3.437,18.968,4.002z"/>
|
||||
</g>
|
||||
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
|
||||
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
|
||||
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
|
||||
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
|
||||
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
|
||||
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
|
||||
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
|
||||
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
|
||||
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
|
||||
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
|
||||
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
|
||||
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
|
||||
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.135-1.839-0.382C7.443,10.351,6.91,9.992,6.458,9.541
|
||||
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.493,5.094,6.997,5.045,6.488C2.525,6.88,1,7.982,1,8.833c0,1.044,2.281,2.5,5.997,2.5
|
||||
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -54,15 +54,14 @@
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* {{SQL CARBON EDIT}} */
|
||||
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
|
||||
content: '/';
|
||||
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
|
||||
content: '\\';
|
||||
}
|
||||
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before,
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before,
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before,
|
||||
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
|
||||
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
|
||||
/* workspace folder, item following workspace folder, or relative path -> hide first seperator */
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,20 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/notabstitlecontrol';
|
||||
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { toResource, Verbosity, IEditorInput, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
|
||||
import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
|
||||
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
|
||||
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';
|
||||
|
||||
interface IRenderedEditorLabel {
|
||||
editor: IEditorInput;
|
||||
editor?: IEditorInput;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
@@ -72,8 +73,16 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
|
||||
|
||||
// Context Menu
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
|
||||
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
|
||||
if (this.group.activeEditor) {
|
||||
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
|
||||
}
|
||||
}));
|
||||
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => {
|
||||
if (this.group.activeEditor) {
|
||||
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private onTitleLabelClick(e: MouseEvent): void {
|
||||
@@ -95,7 +104,9 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
|
||||
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
|
||||
|
||||
this.group.closeEditor(this.group.activeEditor);
|
||||
if (this.group.activeEditor) {
|
||||
this.group.closeEditor(this.group.activeEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +178,7 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
if (
|
||||
!this.activeLabel.editor && this.group.activeEditor || // active editor changed from null => editor
|
||||
this.activeLabel.editor && !this.group.activeEditor || // active editor changed from editor => null
|
||||
!this.group.isActive(this.activeLabel.editor) // active editor changed from editorA => editorB
|
||||
(!this.activeLabel.editor || !this.group.isActive(this.activeLabel.editor)) // active editor changed from editorA => editorB
|
||||
) {
|
||||
fn();
|
||||
|
||||
@@ -195,9 +206,9 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
private redraw(): void {
|
||||
const editor = this.group.activeEditor;
|
||||
const editor = withNullAsUndefined(this.group.activeEditor);
|
||||
|
||||
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
|
||||
const isEditorPinned = this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false;
|
||||
const isGroupActive = this.accessor.activeGroup === this.group;
|
||||
|
||||
this.activeLabel = { editor, pinned: isEditorPinned };
|
||||
@@ -244,7 +255,7 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
title = ''; // dont repeat what is already shown
|
||||
}
|
||||
|
||||
this.editorLabel.setResource({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
|
||||
this.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);
|
||||
} else {
|
||||
@@ -256,7 +267,7 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
}
|
||||
}
|
||||
|
||||
private getVerbosity(style: string): Verbosity {
|
||||
private getVerbosity(style: string | undefined): Verbosity {
|
||||
switch (style) {
|
||||
case 'short': return Verbosity.SHORT;
|
||||
case 'long': return Verbosity.LONG;
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface IResourceDescriptor {
|
||||
readonly resource: URI;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly etag: string;
|
||||
readonly etag?: string;
|
||||
readonly mime: string;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ export class ZoomStatusbarItem extends Themable implements IStatusbarItem {
|
||||
|
||||
static instance: ZoomStatusbarItem;
|
||||
|
||||
showTimeout: any;
|
||||
private showTimeout: any;
|
||||
|
||||
private statusBarItem: HTMLElement;
|
||||
private onSelectScale?: (scale: Scale) => void;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
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';
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./media/tabstitlecontrol';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { shorten } from 'vs/base/common/labels';
|
||||
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner } from 'vs/workbench/common/editor';
|
||||
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
@@ -32,25 +32,22 @@ import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifie
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
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';
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import * as QueryConstants from 'sql/parts/query/common/constants';
|
||||
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
|
||||
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
|
||||
// {{SQL CARBON EDIT}} -- End
|
||||
|
||||
interface IEditorInputLabel {
|
||||
@@ -74,7 +71,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
private tabDisposeables: IDisposable[] = [];
|
||||
|
||||
private dimension: Dimension;
|
||||
private layoutScheduled: IDisposable;
|
||||
private layoutScheduled?: IDisposable;
|
||||
private blockRevealActiveTab: boolean;
|
||||
|
||||
constructor(
|
||||
@@ -95,11 +92,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IFileService fileService: IFileService,
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
|
||||
@ICommandService private commandService: ICommandService,
|
||||
@IConnectionManagementService private connectionService: IConnectionManagementService,
|
||||
@IQueryEditorService private queryEditorService: IQueryEditorService,
|
||||
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
|
||||
// {{SQL CARBON EDIT}} -- End
|
||||
) {
|
||||
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService);
|
||||
@@ -222,7 +215,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Return if transfer is unsupported
|
||||
if (!this.isSupportedDropTransfer(e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,9 +224,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
isLocalDragAndDrop = true;
|
||||
|
||||
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
|
||||
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';
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -241,7 +234,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// 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';
|
||||
e.dataTransfer!.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
this.updateDropFeedback(this.tabsContainer, true);
|
||||
@@ -317,7 +310,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
(this.tabsContainer.lastChild as HTMLElement).remove();
|
||||
|
||||
// Remove associated tab label and widget
|
||||
this.tabDisposeables.pop().dispose();
|
||||
this.tabDisposeables.pop()!.dispose();
|
||||
}
|
||||
|
||||
// A removal of a label requires to recompute all labels
|
||||
@@ -488,7 +481,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
// Open tabs editor
|
||||
this.group.openEditor(this.group.getEditor(index));
|
||||
const input = this.group.getEditor(index);
|
||||
if (input) {
|
||||
this.group.openEditor(input);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
@@ -496,7 +492,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
const showContextMenu = (e: Event) => {
|
||||
EventHelper.stop(e);
|
||||
|
||||
this.onContextMenu(this.group.getEditor(index), e, tab);
|
||||
const input = this.group.getEditor(index);
|
||||
if (input) {
|
||||
this.onContextMenu(input, e, tab);
|
||||
}
|
||||
};
|
||||
|
||||
// Open on Click / Touch
|
||||
@@ -543,7 +542,10 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Run action on Enter/Space
|
||||
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
|
||||
handled = true;
|
||||
this.group.openEditor(this.group.getEditor(index));
|
||||
const input = this.group.getEditor(index);
|
||||
if (input) {
|
||||
this.group.openEditor(input);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate in editors
|
||||
@@ -581,22 +583,29 @@ export class TabsTitleControl extends TitleControl {
|
||||
disposables.push(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
|
||||
EventHelper.stop(e);
|
||||
|
||||
this.group.pinEditor(this.group.getEditor(index));
|
||||
this.group.pinEditor(this.group.getEditor(index) || undefined);
|
||||
}));
|
||||
|
||||
// Context menu
|
||||
disposables.push(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
|
||||
EventHelper.stop(e, true);
|
||||
|
||||
this.onContextMenu(this.group.getEditor(index), e, tab);
|
||||
const input = this.group.getEditor(index);
|
||||
if (input) {
|
||||
this.onContextMenu(input, e, tab);
|
||||
}
|
||||
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
|
||||
|
||||
// Drag support
|
||||
disposables.push(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
|
||||
const editor = this.group.getEditor(index);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype);
|
||||
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
e.dataTransfer!.effectAllowed = 'copyMove';
|
||||
|
||||
// Apply some datatransfer types to allow for dragging the element outside of the application
|
||||
const resource = toResource(editor, { supportSideBySide: true });
|
||||
@@ -618,7 +627,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Return if transfer is unsupported
|
||||
if (!this.isSupportedDropTransfer(e)) {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -627,9 +636,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
isLocalDragAndDrop = true;
|
||||
|
||||
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
|
||||
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';
|
||||
e.dataTransfer!.dropEffect = 'none';
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -637,7 +646,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// 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';
|
||||
e.dataTransfer!.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
this.updateDropFeedback(tab, true, index);
|
||||
@@ -668,7 +677,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
private isSupportedDropTransfer(e: DragEvent): boolean {
|
||||
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0];
|
||||
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
|
||||
}
|
||||
@@ -680,7 +689,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
return true; // (local) editors can always be dropped
|
||||
}
|
||||
|
||||
if (e.dataTransfer.types.length > 0) {
|
||||
if (e.dataTransfer && e.dataTransfer.types.length > 0) {
|
||||
return true; // optimistically allow external data (// see https://github.com/Microsoft/vscode/issues/25789)
|
||||
}
|
||||
|
||||
@@ -689,7 +698,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void {
|
||||
const isTab = (typeof index === 'number');
|
||||
const isActiveTab = isTab && this.group.isActive(this.group.getEditor(index));
|
||||
const editor = typeof index === 'number' ? this.group.getEditor(index) : null;
|
||||
const isActiveTab = isTab && !!editor && this.group.isActive(editor);
|
||||
|
||||
// Background
|
||||
const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : null;
|
||||
@@ -728,9 +738,9 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Build labels and descriptions for each editor
|
||||
const labels = this.group.editors.map(editor => ({
|
||||
editor,
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription(verbosity),
|
||||
title: editor.getTitle(Verbosity.LONG)
|
||||
name: editor.getName()!,
|
||||
description: withNullAsUndefined(editor.getDescription(verbosity)),
|
||||
title: withNullAsUndefined(editor.getTitle(Verbosity.LONG))
|
||||
}));
|
||||
|
||||
// Shorten labels as needed
|
||||
@@ -782,7 +792,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
if (useLongDescriptions) {
|
||||
mapDescriptionToDuplicates.clear();
|
||||
duplicateTitles.forEach(label => {
|
||||
label.description = label.editor.getDescription(Verbosity.LONG);
|
||||
label.description = withNullAsUndefined(label.editor.getDescription(Verbosity.LONG));
|
||||
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
|
||||
});
|
||||
}
|
||||
@@ -793,7 +803,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Remove description if all descriptions are identical
|
||||
if (descriptions.length === 1) {
|
||||
for (const label of mapDescriptionToDuplicates.get(descriptions[0])) {
|
||||
for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) {
|
||||
label.description = '';
|
||||
}
|
||||
|
||||
@@ -803,14 +813,14 @@ export class TabsTitleControl extends TitleControl {
|
||||
// Shorten descriptions
|
||||
const shortenedDescriptions = shorten(descriptions);
|
||||
descriptions.forEach((description, i) => {
|
||||
for (const label of mapDescriptionToDuplicates.get(description)) {
|
||||
for (const label of mapDescriptionToDuplicates.get(description) || []) {
|
||||
label.description = shortenedDescriptions[i];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getLabelConfigFlags(value: string) {
|
||||
private getLabelConfigFlags(value: string | undefined) {
|
||||
switch (value) {
|
||||
case 'short':
|
||||
return { verbosity: Verbosity.SHORT, shortenDuplicates: false };
|
||||
@@ -891,7 +901,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
tabContainer.title = title;
|
||||
|
||||
// Label
|
||||
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
||||
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
|
||||
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
const isTabActive = this.group.isActive(editor);
|
||||
@@ -961,7 +971,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Highlight modified tabs with a border if configured
|
||||
if (this.accessor.partOptions.highlightModifiedTabs) {
|
||||
let modifiedBorderColor: string;
|
||||
let modifiedBorderColor: string | null;
|
||||
if (isGroupActive && isTabActive) {
|
||||
modifiedBorderColor = this.getColor(TAB_ACTIVE_MODIFIED_BORDER);
|
||||
} else if (isGroupActive && !isTabActive) {
|
||||
@@ -998,7 +1008,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
|
||||
const activeTab = this.getTab(this.group.activeEditor);
|
||||
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
|
||||
if (!activeTab || !this.dimension) {
|
||||
return;
|
||||
}
|
||||
@@ -1015,7 +1025,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
}
|
||||
|
||||
private doLayout(dimension: Dimension): void {
|
||||
const activeTab = this.getTab(this.group.activeEditor);
|
||||
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
|
||||
if (!activeTab) {
|
||||
return;
|
||||
}
|
||||
@@ -1050,25 +1060,25 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Reveal the active one
|
||||
const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft;
|
||||
const activeTabFits = activeTabWidth <= visibleContainerWidth;
|
||||
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) {
|
||||
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) {
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
|
||||
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) {
|
||||
else if (containerScrollPosX > activeTabPosX! || !activeTabFits) {
|
||||
this.tabsScrollbar.setScrollPosition({
|
||||
scrollLeft: activeTabPosX
|
||||
scrollLeft: activeTabPosX!
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getTab(editor: IEditorInput): HTMLElement {
|
||||
private getTab(editor: IEditorInput): HTMLElement | undefined {
|
||||
const editorIndex = this.group.getIndexOfEditor(editor);
|
||||
if (editorIndex >= 0) {
|
||||
return this.tabsContainer.children[editorIndex] as HTMLElement;
|
||||
@@ -1106,17 +1116,20 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Local Editor DND
|
||||
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
|
||||
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
|
||||
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
|
||||
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
|
||||
|
||||
// Move editor to target position and index
|
||||
if (this.isMoveOperation(e, draggedEditor.groupId)) {
|
||||
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
|
||||
}
|
||||
if (sourceGroup) {
|
||||
|
||||
// Copy editor to target position and index
|
||||
else {
|
||||
sourceGroup.copyEditor(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 });
|
||||
}
|
||||
}
|
||||
|
||||
this.group.focus();
|
||||
@@ -1125,15 +1138,17 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
// Local Editor Group DND
|
||||
else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
|
||||
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
|
||||
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1154,7 +1169,7 @@ export class TabsTitleControl extends TitleControl {
|
||||
// {{SQL CARBON EDIT}} -- Display the editor's tab color
|
||||
private setEditorTabColor(editor: IEditorInput, tabContainer: HTMLElement, isTabActive: boolean) {
|
||||
let sqlEditor = editor as any;
|
||||
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.workspaceConfigurationService, 'tabColorMode');
|
||||
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.configurationService, 'tabColorMode');
|
||||
if (tabColorMode === QueryConstants.tabColorModeOff || (tabColorMode !== QueryConstants.tabColorModeBorder && tabColorMode !== QueryConstants.tabColorModeFill)
|
||||
|| this.themeService.getTheme().type === HIGH_CONTRAST || !sqlEditor.tabColor) {
|
||||
tabContainer.style.borderTopColor = '';
|
||||
@@ -1176,7 +1191,8 @@ export class TabsTitleControl extends TitleControl {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this.layoutScheduled = dispose(this.layoutScheduled);
|
||||
dispose(this.layoutScheduled);
|
||||
this.layoutScheduled = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1206,6 +1222,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
`);
|
||||
}
|
||||
|
||||
// High Contrast Border Color for Editor Actions
|
||||
const contrastBorderColor = theme.getColor(contrastBorder);
|
||||
if (contrastBorder) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions {
|
||||
outline: 1px solid ${contrastBorderColor}
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Hover Background
|
||||
const tabHoverBackground = theme.getColor(TAB_HOVER_BACKGROUND);
|
||||
if (tabHoverBackground) {
|
||||
@@ -1251,12 +1277,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
|
||||
const editorDragAndDropBackground = theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
|
||||
|
||||
let adjustedTabBackground: Color;
|
||||
let adjustedTabBackground: Color | undefined;
|
||||
if (editorGroupHeaderTabsBackground && editorBackgroundColor) {
|
||||
adjustedTabBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorBackgroundColor, workbenchBackground);
|
||||
}
|
||||
|
||||
let adjustedTabDragBackground: Color;
|
||||
let adjustedTabDragBackground: Color | undefined;
|
||||
if (editorGroupHeaderTabsBackground && editorBackgroundColor && editorDragAndDropBackground && editorBackgroundColor) {
|
||||
adjustedTabDragBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorDragAndDropBackground, editorBackgroundColor, workbenchBackground);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
@@ -61,7 +61,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
getTitle(): string | null {
|
||||
if (this.input) {
|
||||
return this.input.getName();
|
||||
}
|
||||
@@ -120,6 +120,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
|
||||
// Readonly flag
|
||||
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
|
||||
return undefined;
|
||||
}, error => {
|
||||
|
||||
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
|
||||
@@ -262,7 +263,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support
|
||||
}
|
||||
|
||||
private saveTextDiffEditorViewState(input: EditorInput): void {
|
||||
private saveTextDiffEditorViewState(input: EditorInput | null): void {
|
||||
if (!(input instanceof DiffEditorInput)) {
|
||||
return; // only supported for diff editor inputs
|
||||
}
|
||||
@@ -288,11 +289,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
}
|
||||
}
|
||||
|
||||
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState {
|
||||
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState | null {
|
||||
return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support
|
||||
}
|
||||
|
||||
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
|
||||
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null {
|
||||
const control = this.getControl();
|
||||
const model = control.getModel();
|
||||
if (!model || !model.modified || !model.original) {
|
||||
@@ -311,9 +312,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
||||
return control.saveViewState();
|
||||
}
|
||||
|
||||
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
|
||||
let original: URI;
|
||||
let modified: URI;
|
||||
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | null {
|
||||
let original: URI | null;
|
||||
let modified: URI | null;
|
||||
|
||||
if (modelOrInput instanceof DiffEditorInput) {
|
||||
original = modelOrInput.originalInput.getResource();
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/service
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
|
||||
@@ -20,7 +20,7 @@ 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 { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
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';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
@@ -46,7 +46,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
getTitle(): string | null {
|
||||
if (this.input) {
|
||||
return this.input.getName();
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
|
||||
super.saveState();
|
||||
}
|
||||
|
||||
private saveTextResourceEditorViewState(input: EditorInput): void {
|
||||
private saveTextResourceEditorViewState(input: EditorInput | null): void {
|
||||
if (!(input instanceof UntitledEditorInput) && !(input instanceof ResourceEditorInput)) {
|
||||
return; // only enabled for untitled and resource inputs
|
||||
}
|
||||
|
||||
@@ -32,13 +32,14 @@ import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceData
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
|
||||
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
|
||||
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource } from 'vs/workbench/common/editor';
|
||||
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
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 { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
|
||||
export interface IToolbarActions {
|
||||
primary: IAction[];
|
||||
@@ -50,7 +51,7 @@ export abstract class TitleControl extends Themable {
|
||||
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
|
||||
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
|
||||
|
||||
protected breadcrumbsControl: BreadcrumbsControl;
|
||||
protected breadcrumbsControl?: BreadcrumbsControl;
|
||||
|
||||
private currentPrimaryEditorActionIds: string[] = [];
|
||||
private currentSecondaryEditorActionIds: string[] = [];
|
||||
@@ -155,18 +156,18 @@ export abstract class TitleControl extends Themable {
|
||||
}));
|
||||
}
|
||||
|
||||
private actionItemProvider(action: Action): IActionItem {
|
||||
private actionItemProvider(action: Action): IActionItem | null {
|
||||
const activeControl = this.group.activeControl;
|
||||
|
||||
// Check Active Editor
|
||||
let actionItem: IActionItem;
|
||||
let actionItem: IActionItem | null = null;
|
||||
if (activeControl instanceof BaseEditor) {
|
||||
actionItem = activeControl.getActionItem(action);
|
||||
}
|
||||
|
||||
// Check extensions
|
||||
if (!actionItem) {
|
||||
actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
|
||||
actionItem = withUndefinedAsNull(createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService));
|
||||
}
|
||||
|
||||
return actionItem;
|
||||
@@ -218,7 +219,7 @@ export abstract class TitleControl extends Themable {
|
||||
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
|
||||
|
||||
// Update the resource context
|
||||
this.resourceContext.set(toResource(this.group.activeEditor, { supportSideBySide: true }));
|
||||
this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null);
|
||||
|
||||
// Editor actions require the editor control to be there, so we retrieve it via service
|
||||
const activeControl = this.group.activeControl;
|
||||
@@ -254,23 +255,25 @@ 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';
|
||||
e.dataTransfer!.effectAllowed = 'copyMove';
|
||||
|
||||
// If tabs are disabled, treat dragging as if an editor tab was dragged
|
||||
if (!this.accessor.partOptions.showTabs) {
|
||||
const resource = toResource(this.group.activeEditor, { supportSideBySide: true });
|
||||
const resource = this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null;
|
||||
if (resource) {
|
||||
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
|
||||
}
|
||||
}
|
||||
|
||||
// Drag Image
|
||||
let label = this.group.activeEditor.getName();
|
||||
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
|
||||
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
|
||||
}
|
||||
if (this.group.activeEditor) {
|
||||
let label = this.group.activeEditor.getName();
|
||||
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
|
||||
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
|
||||
}
|
||||
|
||||
applyDragImage(e, label, 'monaco-editor-group-drag-image');
|
||||
applyDragImage(e, label, 'monaco-editor-group-drag-image');
|
||||
}
|
||||
}));
|
||||
|
||||
// Drag end
|
||||
@@ -305,7 +308,7 @@ export abstract class TitleControl extends Themable {
|
||||
onHide: () => {
|
||||
|
||||
// restore previous context
|
||||
this.resourceContext.set(currentContext);
|
||||
this.resourceContext.set(currentContext || null);
|
||||
|
||||
// restore focus to active group
|
||||
this.accessor.activeGroup.focus();
|
||||
@@ -313,14 +316,14 @@ export abstract class TitleControl extends Themable {
|
||||
});
|
||||
}
|
||||
|
||||
private getKeybinding(action: IAction): ResolvedKeybinding {
|
||||
private getKeybinding(action: IAction): ResolvedKeybinding | undefined {
|
||||
return this.keybindingService.lookupKeybinding(action.id);
|
||||
}
|
||||
|
||||
protected getKeybindingLabel(action: IAction): string {
|
||||
protected getKeybindingLabel(action: IAction): string | undefined {
|
||||
const keybinding = this.getKeybinding(action);
|
||||
|
||||
return keybinding ? keybinding.getLabel() : undefined;
|
||||
return keybinding ? keybinding.getLabel() || undefined : undefined;
|
||||
}
|
||||
|
||||
abstract openEditor(editor: IEditorInput): void;
|
||||
@@ -356,7 +359,8 @@ export abstract class TitleControl extends Themable {
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
|
||||
dispose(this.breadcrumbsControl);
|
||||
this.breadcrumbsControl = undefined;
|
||||
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
|
||||
|
||||
super.dispose();
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'vs/css!./media/notificationsActions';
|
||||
import { Themable, NOTIFICATIONS_BORDER, NOTIFICATIONS_CENTER_HEADER_FOREGROUND, NOTIFICATIONS_CENTER_HEADER_BACKGROUND, NOTIFICATIONS_CENTER_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType } from 'vs/workbench/common/notifications';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { NotificationsCenterVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
@@ -16,7 +16,7 @@ import { NotificationsList } from 'vs/workbench/browser/parts/notifications/noti
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { addClass, removeClass, isAncestor, Dimension } from 'vs/base/browser/dom';
|
||||
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
|
||||
@@ -43,7 +43,7 @@ export class NotificationsCenter extends Themable {
|
||||
private model: INotificationsModel,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService
|
||||
@@ -57,6 +57,7 @@ export class NotificationsCenter extends Themable {
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
}
|
||||
|
||||
get isVisible(): boolean {
|
||||
@@ -251,11 +252,11 @@ export class NotificationsCenter extends Themable {
|
||||
|
||||
// Make sure notifications are not exceeding available height
|
||||
availableHeight = this.workbenchDimensions.height - 35 /* header */;
|
||||
if (this.partService.isVisible(Parts.STATUSBAR_PART)) {
|
||||
if (this.layoutService.isVisible(Parts.STATUSBAR_PART)) {
|
||||
availableHeight -= 22; // adjust for status bar
|
||||
}
|
||||
|
||||
if (this.partService.isVisible(Parts.TITLEBAR_PART)) {
|
||||
if (this.layoutService.isVisible(Parts.TITLEBAR_PART)) {
|
||||
availableHeight -= 22; // adjust for title bar
|
||||
}
|
||||
|
||||
|
||||
@@ -32,14 +32,9 @@ export const TOGGLE_NOTIFICATION = 'notification.toggle';
|
||||
export const CLEAR_NOTIFICATION = 'notification.clear';
|
||||
export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll';
|
||||
|
||||
const notificationFocusedId = 'notificationFocus';
|
||||
export const NotificationFocusedContext = new RawContextKey<boolean>(notificationFocusedId, true);
|
||||
|
||||
const notificationsCenterVisibleId = 'notificationCenterVisible';
|
||||
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>(notificationsCenterVisibleId, false);
|
||||
|
||||
const notificationsToastsVisibleId = 'notificationToastsVisible';
|
||||
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>(notificationsToastsVisibleId, false);
|
||||
export const NotificationFocusedContext = new RawContextKey<boolean>('notificationFocus', true);
|
||||
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>('notificationCenterVisible', false);
|
||||
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>('notificationToastsVisible', false);
|
||||
|
||||
export interface INotificationsCenterController {
|
||||
readonly isVisible: boolean;
|
||||
|
||||
@@ -10,11 +10,11 @@ import { addClass, removeClass, isAncestor, addDisposableListener, EventType, Di
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { Themable, NOTIFICATIONS_TOAST_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -62,7 +62,7 @@ export class NotificationsToasts extends Themable {
|
||||
private container: HTMLElement,
|
||||
private model: INotificationsModel,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@@ -79,6 +79,9 @@ export class NotificationsToasts extends Themable {
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Layout
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
|
||||
// Delay some tasks until after we can show notifications
|
||||
this.onCanShowNotifications().then(() => {
|
||||
|
||||
@@ -466,11 +469,11 @@ export class NotificationsToasts extends Themable {
|
||||
|
||||
// Make sure notifications are not exceeding available height
|
||||
availableHeight = this.workbenchDimensions.height;
|
||||
if (this.partService.isVisible(Parts.STATUSBAR_PART)) {
|
||||
if (this.layoutService.isVisible(Parts.STATUSBAR_PART)) {
|
||||
availableHeight -= 22; // adjust for status bar
|
||||
}
|
||||
|
||||
if (this.partService.isVisible(Parts.TITLEBAR_PART)) {
|
||||
if (this.layoutService.isVisible(Parts.TITLEBAR_PART)) {
|
||||
availableHeight -= 22; // adjust for title bar
|
||||
}
|
||||
|
||||
|
||||
@@ -417,7 +417,7 @@ export class NotificationTemplateRenderer {
|
||||
actions.forEach(action => this.template.toolbar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) }));
|
||||
}
|
||||
|
||||
private renderSource(notification): void {
|
||||
private renderSource(notification: INotificationViewItem): void {
|
||||
if (notification.expanded && notification.source) {
|
||||
this.template.source.textContent = localize('notificationSource', "Source: {0}", notification.source);
|
||||
this.template.source.title = notification.source;
|
||||
|
||||
@@ -12,9 +12,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ActivityAction } from 'vs/workbench/browser/parts/compositeBarActions';
|
||||
import { IActivity } from 'vs/workbench/common/activity';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
||||
export class ClosePanelAction extends Action {
|
||||
|
||||
@@ -24,14 +25,14 @@ export class ClosePanelAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, name, 'hide-panel-action');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
this.partService.setPanelHidden(true);
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.setPanelHidden(true);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,14 +44,14 @@ export class TogglePanelAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, name, partService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel');
|
||||
super(id, name, layoutService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel');
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART));
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.setPanelHidden(this.layoutService.isVisible(Parts.PANEL_PART));
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ class FocusPanelAction extends Action {
|
||||
id: string,
|
||||
label: string,
|
||||
@IPanelService private readonly panelService: IPanelService,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -71,9 +72,9 @@ class FocusPanelAction extends Action {
|
||||
run(): Promise<any> {
|
||||
|
||||
// Show panel
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
this.partService.setPanelHidden(false);
|
||||
return Promise.resolve(null);
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
this.layoutService.setPanelHidden(false);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Focus into active panel
|
||||
@@ -82,7 +83,7 @@ class FocusPanelAction extends Action {
|
||||
panel.focus();
|
||||
}
|
||||
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,28 +100,29 @@ export class TogglePanelPositionAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IEditorGroupsService editorGroupsService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, partService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right');
|
||||
super(id, label, layoutService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right');
|
||||
|
||||
this.toDispose = [];
|
||||
|
||||
const setClassAndLabel = () => {
|
||||
const positionRight = this.partService.getPanelPosition() === Position.RIGHT;
|
||||
const positionRight = this.layoutService.getPanelPosition() === Position.RIGHT;
|
||||
this.class = positionRight ? 'move-panel-to-bottom' : 'move-panel-to-right';
|
||||
this.label = positionRight ? TogglePanelPositionAction.MOVE_TO_BOTTOM_LABEL : TogglePanelPositionAction.MOVE_TO_RIGHT_LABEL;
|
||||
};
|
||||
|
||||
this.toDispose.push(partService.onEditorLayout(() => setClassAndLabel()));
|
||||
this.toDispose.push(editorGroupsService.onDidLayout(() => setClassAndLabel()));
|
||||
|
||||
setClassAndLabel();
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const position = this.partService.getPanelPosition();
|
||||
const position = this.layoutService.getPanelPosition();
|
||||
|
||||
this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM);
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -143,26 +145,27 @@ export class ToggleMaximizedPanelAction extends Action {
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IEditorGroupsService editorGroupsService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, partService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
|
||||
super(id, label, layoutService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
|
||||
|
||||
this.toDispose = [];
|
||||
|
||||
this.toDispose.push(partService.onEditorLayout(() => {
|
||||
const maximized = this.partService.isPanelMaximized();
|
||||
this.toDispose.push(editorGroupsService.onDidLayout(() => {
|
||||
const maximized = this.layoutService.isPanelMaximized();
|
||||
this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action';
|
||||
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
|
||||
}));
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
this.partService.setPanelHidden(false);
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
this.layoutService.setPanelHidden(false);
|
||||
}
|
||||
|
||||
this.partService.toggleMaximizedPanel();
|
||||
return Promise.resolve(null);
|
||||
this.layoutService.toggleMaximizedPanel();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -184,7 +187,7 @@ export class PanelActivityAction extends ActivityAction {
|
||||
run(event: any): Promise<any> {
|
||||
this.panelService.openPanel(this.activity.id, true);
|
||||
this.activate();
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,17 +205,19 @@ export class SwitchPanelViewAction extends Action {
|
||||
const pinnedPanels = this.panelService.getPinnedPanels();
|
||||
const activePanel = this.panelService.getActivePanel();
|
||||
if (!activePanel) {
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
let targetPanelId: string;
|
||||
let targetPanelId: string | undefined;
|
||||
for (let i = 0; i < pinnedPanels.length; i++) {
|
||||
if (pinnedPanels[i].id === activePanel.getId()) {
|
||||
targetPanelId = pinnedPanels[(i + pinnedPanels.length + offset) % pinnedPanels.length].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.panelService.openPanel(targetPanelId, true);
|
||||
return Promise.resolve(null);
|
||||
if (typeof targetPanelId === 'string') {
|
||||
this.panelService.openPanel(targetPanelId, true);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +252,7 @@ export class NextPanelViewAction extends SwitchPanelViewAction {
|
||||
super(id, name, panelService);
|
||||
}
|
||||
|
||||
public run(): Promise<any> {
|
||||
run(): Promise<any> {
|
||||
return super.run(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
|
||||
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 } from 'vs/workbench/common/panel';
|
||||
import { IPanel, ActivePanelContext, PanelFocusContext } from 'vs/workbench/common/panel';
|
||||
import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart';
|
||||
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
|
||||
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, ToggleMaximizedPanelAction, TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
@@ -29,56 +29,58 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { Dimension, trackFocus } from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
|
||||
|
||||
export const ActivePanelContext = new RawContextKey<string>('activePanel', '');
|
||||
export const PanelFocusContext = new RawContextKey<boolean>('panelFocus', false);
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
interface ICachedPanel {
|
||||
id: string;
|
||||
pinned: boolean;
|
||||
order: number;
|
||||
order?: number;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export class PanelPart extends CompositePart<Panel> implements IPanelService, ISerializableView {
|
||||
export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid';
|
||||
|
||||
private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels';
|
||||
private static readonly MIN_COMPOSITE_BAR_WIDTH = 50;
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 300;
|
||||
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
readonly minimumHeight: number = 77;
|
||||
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
|
||||
readonly snapSize: number = 50;
|
||||
readonly priority: LayoutPriority = LayoutPriority.Low;
|
||||
|
||||
//#endregion
|
||||
|
||||
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); }
|
||||
get onDidPanelClose(): Event<IPanel> { return this.onDidCompositeClose.event; }
|
||||
|
||||
private activePanelContextKey: IContextKey<string>;
|
||||
private panelFocusContextKey: IContextKey<boolean>;
|
||||
private blockOpeningPanel: boolean;
|
||||
|
||||
private compositeBar: CompositeBar;
|
||||
private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
|
||||
|
||||
private blockOpeningPanel: boolean;
|
||||
private dimension: Dimension;
|
||||
|
||||
element: HTMLElement;
|
||||
minimumWidth: number = 300;
|
||||
maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
minimumHeight: number = 77;
|
||||
maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
snapSize: number = 50;
|
||||
priority: LayoutPriority = LayoutPriority.Low;
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -90,7 +92,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
storageService,
|
||||
telemetryService,
|
||||
contextMenuService,
|
||||
partService,
|
||||
layoutService,
|
||||
keybindingService,
|
||||
instantiationService,
|
||||
themeService,
|
||||
@@ -99,8 +101,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId(),
|
||||
'panel',
|
||||
'panel',
|
||||
null,
|
||||
id,
|
||||
undefined,
|
||||
Parts.PANEL_PART,
|
||||
{ hasTitle: true }
|
||||
);
|
||||
|
||||
@@ -116,10 +118,10 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))
|
||||
],
|
||||
getDefaultCompositeId: () => Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId(),
|
||||
hidePart: () => this.partService.setPanelHidden(true),
|
||||
hidePart: () => this.layoutService.setPanelHidden(true),
|
||||
compositeSize: 0,
|
||||
overflowActionSize: 44,
|
||||
colors: theme => ({
|
||||
colors: (theme: ITheme) => ({
|
||||
activeBackgroundColor: theme.getColor(PANEL_BACKGROUND), // Background color for overflow action
|
||||
inactiveBackgroundColor: theme.getColor(PANEL_BACKGROUND), // Background color for overflow action
|
||||
activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER),
|
||||
@@ -141,25 +143,13 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
this.element = parent;
|
||||
|
||||
super.create(parent);
|
||||
|
||||
const focusTracker = trackFocus(parent);
|
||||
|
||||
focusTracker.onDidFocus(() => {
|
||||
this.panelFocusContextKey.set(true);
|
||||
});
|
||||
focusTracker.onDidBlur(() => {
|
||||
this.panelFocusContextKey.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.onDidPanelOpen(({ panel }) => this._onDidPanelOpen(panel)));
|
||||
this._register(this.onDidPanelClose(this._onDidPanelClose, this));
|
||||
|
||||
// Panel open/close
|
||||
this._register(this.onDidPanelOpen(({ panel }) => this.onPanelOpen(panel)));
|
||||
this._register(this.onDidPanelClose(this.onPanelClose, this));
|
||||
|
||||
// Panel register/deregister
|
||||
this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor)));
|
||||
this._register(this.registry.onDidDeregister(panelDescriptor => {
|
||||
this.compositeBar.hideComposite(panelDescriptor.id);
|
||||
@@ -175,17 +165,18 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
// Deactivate panel action on close
|
||||
this._register(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId())));
|
||||
|
||||
// State
|
||||
this.lifecycleService.when(LifecyclePhase.Eventually).then(() => {
|
||||
this._register(this.compositeBar.onDidChange(() => this.saveCachedPanels()));
|
||||
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
|
||||
});
|
||||
}
|
||||
|
||||
private _onDidPanelOpen(panel: IPanel): void {
|
||||
private onPanelOpen(panel: IPanel): void {
|
||||
this.activePanelContextKey.set(panel.getId());
|
||||
}
|
||||
|
||||
private _onDidPanelClose(panel: IPanel): void {
|
||||
private onPanelClose(panel: IPanel): void {
|
||||
const id = panel.getId();
|
||||
|
||||
if (this.activePanelContextKey.get() === id) {
|
||||
@@ -193,12 +184,14 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
}
|
||||
}
|
||||
|
||||
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> {
|
||||
return Event.map(this._onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus }));
|
||||
}
|
||||
create(parent: HTMLElement): void {
|
||||
this.element = parent;
|
||||
|
||||
get onDidPanelClose(): Event<IPanel> {
|
||||
return this._onDidCompositeClose.event;
|
||||
super.create(parent);
|
||||
|
||||
const focusTracker = this._register(trackFocus(parent));
|
||||
this._register(focusTracker.onDidFocus(() => this.panelFocusContextKey.set(true)));
|
||||
this._register(focusTracker.onDidBlur(() => this.panelFocusContextKey.set(false)));
|
||||
}
|
||||
|
||||
updateStyles(): void {
|
||||
@@ -209,38 +202,40 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
|
||||
|
||||
const title = this.getTitleArea();
|
||||
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
|
||||
if (title) {
|
||||
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
|
||||
}
|
||||
}
|
||||
|
||||
openPanel(id: string, focus?: boolean): Panel {
|
||||
openPanel(id: string, focus?: boolean): Panel | null {
|
||||
if (this.blockOpeningPanel) {
|
||||
return null; // Workaround against a potential race condition
|
||||
}
|
||||
|
||||
// First check if panel is hidden and show if so
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
try {
|
||||
this.blockOpeningPanel = true;
|
||||
this.partService.setPanelHidden(false);
|
||||
this.layoutService.setPanelHidden(false);
|
||||
} finally {
|
||||
this.blockOpeningPanel = false;
|
||||
}
|
||||
}
|
||||
|
||||
return this.openComposite(id, focus);
|
||||
return this.openComposite(id, focus) || null;
|
||||
}
|
||||
|
||||
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
|
||||
return this.compositeBar.showActivity(panelId, badge, clazz);
|
||||
}
|
||||
|
||||
private getPanel(panelId: string): IPanelIdentifier {
|
||||
private getPanel(panelId: string): IPanelIdentifier | undefined {
|
||||
return this.getPanels().filter(p => p.id === panelId).pop();
|
||||
}
|
||||
|
||||
getPanels(): PanelDescriptor[] {
|
||||
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels()
|
||||
.sort((v1, v2) => v1.order - v2.order);
|
||||
.sort((v1, v2) => typeof v1.order === 'number' && typeof v2.order === 'number' ? v1.order - v2.order : NaN);
|
||||
}
|
||||
|
||||
getPinnedPanels(): PanelDescriptor[] {
|
||||
@@ -257,7 +252,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
];
|
||||
}
|
||||
|
||||
getActivePanel(): IPanel {
|
||||
getActivePanel(): IPanel | null {
|
||||
return this.getActiveComposite();
|
||||
}
|
||||
|
||||
@@ -286,41 +281,31 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
};
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
if (!this.partService.isVisible(Parts.PANEL_PART)) {
|
||||
if (dim1 instanceof Dimension) {
|
||||
return [dim1];
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { width, height } = dim1 instanceof Dimension ? dim1 : { width: dim1, height: dim2 };
|
||||
|
||||
if (this.partService.getPanelPosition() === Position.RIGHT) {
|
||||
// Take into account the 1px border when layouting
|
||||
this.dimension = new Dimension(width - 1, height);
|
||||
if (this.layoutService.getPanelPosition() === Position.RIGHT) {
|
||||
this.dimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting
|
||||
} else {
|
||||
this.dimension = new Dimension(width, height);
|
||||
this.dimension = new Dimension(width, height!);
|
||||
}
|
||||
|
||||
const sizes = super.layout(this.dimension.width, this.dimension.height);
|
||||
// Layout contents
|
||||
super.layout(this.dimension.width, this.dimension.height);
|
||||
|
||||
// Layout composite bar
|
||||
this.layoutCompositeBar();
|
||||
|
||||
if (dim1 instanceof Dimension) {
|
||||
return sizes;
|
||||
}
|
||||
}
|
||||
|
||||
private layoutCompositeBar(): void {
|
||||
if (this.dimension) {
|
||||
let availableWidth = this.dimension.width - 40; // take padding into account
|
||||
if (this.toolBar) {
|
||||
// adjust height for global actions showing
|
||||
availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth());
|
||||
availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing
|
||||
}
|
||||
|
||||
this.compositeBar.layout(new Dimension(availableWidth, this.dimension.height));
|
||||
}
|
||||
}
|
||||
@@ -334,6 +319,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
};
|
||||
this.compositeActions[compositeId] = compositeActions;
|
||||
}
|
||||
|
||||
return compositeActions;
|
||||
}
|
||||
|
||||
@@ -345,8 +331,10 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
compositeActions.pinnedAction.dispose();
|
||||
delete this.compositeActions[compositeId];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -355,6 +343,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
if (!activePanel) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.toolBar.getItemsWidth();
|
||||
}
|
||||
|
||||
@@ -393,30 +382,35 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
|
||||
|
||||
private saveCachedPanels(): void {
|
||||
const state: ICachedPanel[] = [];
|
||||
|
||||
const compositeItems = this.compositeBar.getCompositeBarItems();
|
||||
for (const compositeItem of compositeItems) {
|
||||
state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible });
|
||||
}
|
||||
|
||||
this.cachedPanelsValue = JSON.stringify(state);
|
||||
}
|
||||
|
||||
private getCachedPanels(): ICachedPanel[] {
|
||||
const storedStates = <Array<string | ICachedPanel>>JSON.parse(this.cachedPanelsValue);
|
||||
const registeredPanels = this.getPanels();
|
||||
|
||||
const storedStates = <Array<string | ICachedPanel>>JSON.parse(this.cachedPanelsValue);
|
||||
const cachedPanels = <ICachedPanel[]>storedStates.map(c => {
|
||||
const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? <ICachedPanel>{ id: c, pinned: true, order: undefined, visible: true } : c;
|
||||
const registered = registeredPanels.some(p => p.id === serialized.id);
|
||||
serialized.visible = registered ? isUndefinedOrNull(serialized.visible) ? true : serialized.visible : false;
|
||||
return serialized;
|
||||
});
|
||||
|
||||
return cachedPanels;
|
||||
}
|
||||
|
||||
private _cachedPanelsValue: string;
|
||||
private _cachedPanelsValue: string | null;
|
||||
private get cachedPanelsValue(): string {
|
||||
if (!this._cachedPanelsValue) {
|
||||
this._cachedPanelsValue = this.getStoredCachedPanelsValue();
|
||||
}
|
||||
|
||||
return this._cachedPanelsValue;
|
||||
}
|
||||
|
||||
@@ -509,3 +503,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
registerSingleton(IPanelService, PanelPart);
|
||||
@@ -6,9 +6,9 @@
|
||||
import 'vs/css!./quickInput';
|
||||
import { Component } from 'vs/workbench/common/component';
|
||||
import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
@@ -29,10 +29,10 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { inQuickOpenContext, InQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -40,10 +40,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
|
||||
import { AccessibilitySupport } from 'vs/base/common/platform';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -115,7 +115,7 @@ class QuickInput implements IQuickInput {
|
||||
this.onDidHideEmitter,
|
||||
];
|
||||
|
||||
private busyDelay: TimeoutTimer;
|
||||
private busyDelay: TimeoutTimer | null;
|
||||
|
||||
constructor(protected ui: QuickInputUI) {
|
||||
}
|
||||
@@ -252,21 +252,21 @@ class QuickInput implements IQuickInput {
|
||||
this.ui.leftActionBar.clear();
|
||||
const leftButtons = this.buttons.filter(button => button === backButton);
|
||||
this.ui.leftActionBar.push(leftButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
|
||||
this.onDidTriggerButtonEmitter.fire(button);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
action.tooltip = button.tooltip;
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
this.ui.rightActionBar.clear();
|
||||
const rightButtons = this.buttons.filter(button => button !== backButton);
|
||||
this.ui.rightActionBar.push(rightButtons.map((button, index) => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
|
||||
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
|
||||
this.onDidTriggerButtonEmitter.fire(button);
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
action.tooltip = button.tooltip;
|
||||
action.tooltip = button.tooltip || '';
|
||||
return action;
|
||||
}), { icon: true, label: false });
|
||||
}
|
||||
@@ -311,21 +311,26 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
private _value = '';
|
||||
private _placeholder;
|
||||
private onDidChangeValueEmitter = new Emitter<string>();
|
||||
private onDidAcceptEmitter = new Emitter<string>();
|
||||
private onDidAcceptEmitter = new Emitter<void>();
|
||||
private _items: Array<T | IQuickPickSeparator> = [];
|
||||
private itemsUpdated = false;
|
||||
private _canSelectMany = false;
|
||||
private _matchOnDescription = false;
|
||||
private _matchOnDetail = false;
|
||||
private _matchOnLabel = true;
|
||||
private _autoFocusOnList = true;
|
||||
private _activeItems: T[] = [];
|
||||
private activeItemsUpdated = false;
|
||||
private activeItemsToConfirm: T[] = [];
|
||||
private activeItemsToConfirm: T[] | null = [];
|
||||
private onDidChangeActiveEmitter = new Emitter<T[]>();
|
||||
private _selectedItems: T[] = [];
|
||||
private selectedItemsUpdated = false;
|
||||
private selectedItemsToConfirm: T[] = [];
|
||||
private selectedItemsToConfirm: T[] | null = [];
|
||||
private onDidChangeSelectionEmitter = new Emitter<T[]>();
|
||||
private onDidTriggerItemButtonEmitter = new Emitter<IQuickPickItemButtonEvent<T>>();
|
||||
private _valueSelection: Readonly<[number, number]>;
|
||||
private valueSelectionUpdated = true;
|
||||
private _validationMessage: string;
|
||||
|
||||
quickNavigate: IQuickNavigateConfiguration;
|
||||
|
||||
@@ -399,6 +404,24 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.update();
|
||||
}
|
||||
|
||||
get matchOnLabel() {
|
||||
return this._matchOnLabel;
|
||||
}
|
||||
|
||||
set matchOnLabel(matchOnLabel: boolean) {
|
||||
this._matchOnLabel = matchOnLabel;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get autoFocusOnList() {
|
||||
return this._autoFocusOnList;
|
||||
}
|
||||
|
||||
set autoFocusOnList(autoFocusOnList: boolean) {
|
||||
this._autoFocusOnList = autoFocusOnList;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get activeItems() {
|
||||
return this._activeItems;
|
||||
}
|
||||
@@ -425,10 +448,33 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
return this.ui.keyMods;
|
||||
}
|
||||
|
||||
set valueSelection(valueSelection: Readonly<[number, number]>) {
|
||||
this._valueSelection = valueSelection;
|
||||
this.valueSelectionUpdated = true;
|
||||
this.update();
|
||||
}
|
||||
|
||||
get validationMessage() {
|
||||
return this._validationMessage;
|
||||
}
|
||||
|
||||
set validationMessage(validationMessage: string) {
|
||||
this._validationMessage = validationMessage;
|
||||
this.update();
|
||||
}
|
||||
|
||||
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
|
||||
|
||||
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
|
||||
|
||||
private trySelectFirst() {
|
||||
if (this.autoFocusOnList) {
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
if (!this.visible) {
|
||||
this.visibleDisposables.push(
|
||||
@@ -438,11 +484,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
}
|
||||
this._value = value;
|
||||
this.ui.list.filter(this.ui.inputBox.value);
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
this.trySelectFirst();
|
||||
this.onDidChangeValueEmitter.fire(value);
|
||||
}),
|
||||
this.ui.inputBox.onMouseDown(event => {
|
||||
if (!this.autoFocusOnList) {
|
||||
this.ui.list.clearFocus();
|
||||
}
|
||||
}),
|
||||
this.ui.inputBox.onKeyDown(event => {
|
||||
switch (event.keyCode) {
|
||||
case KeyCode.DownArrow:
|
||||
@@ -529,6 +578,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent<T>)),
|
||||
this.registerQuickNavigation()
|
||||
);
|
||||
this.valueSelectionUpdated = true;
|
||||
}
|
||||
super.show(); // TODO: Why have show() bubble up while update() trickles down? (Could move setComboboxAccessibility() here.)
|
||||
}
|
||||
@@ -589,6 +639,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
if (this.ui.inputBox.value !== this.value) {
|
||||
this.ui.inputBox.value = this.value;
|
||||
}
|
||||
if (this.valueSelectionUpdated) {
|
||||
this.valueSelectionUpdated = false;
|
||||
this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] });
|
||||
}
|
||||
if (this.ui.inputBox.placeholder !== (this.placeholder || '')) {
|
||||
this.ui.inputBox.placeholder = (this.placeholder || '');
|
||||
}
|
||||
@@ -599,15 +653,13 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked();
|
||||
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
|
||||
this.ui.count.setCount(this.ui.list.getCheckedCount());
|
||||
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
|
||||
this.ui.list.focus('First');
|
||||
}
|
||||
this.trySelectFirst();
|
||||
}
|
||||
if (this.ui.container.classList.contains('show-checkboxes') !== !!this.canSelectMany) {
|
||||
if (this.canSelectMany) {
|
||||
this.ui.list.clearFocus();
|
||||
} else if (!this.ui.isScreenReaderOptimized()) {
|
||||
this.ui.list.focus('First');
|
||||
} else {
|
||||
this.trySelectFirst();
|
||||
}
|
||||
}
|
||||
if (this.activeItemsUpdated) {
|
||||
@@ -630,11 +682,19 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
||||
this.selectedItemsToConfirm = null;
|
||||
}
|
||||
}
|
||||
if (this.validationMessage) {
|
||||
this.ui.message.textContent = this.validationMessage;
|
||||
this.ui.inputBox.showDecoration(Severity.Error);
|
||||
} else {
|
||||
this.ui.message.textContent = null;
|
||||
this.ui.inputBox.showDecoration(Severity.Ignore);
|
||||
}
|
||||
this.ui.list.matchOnDescription = this.matchOnDescription;
|
||||
this.ui.list.matchOnDetail = this.matchOnDetail;
|
||||
this.ui.list.matchOnLabel = this.matchOnLabel;
|
||||
this.ui.setComboboxAccessibility(true);
|
||||
this.ui.inputBox.setAttribute('aria-label', QuickPick.INPUT_BOX_ARIA_LABEL);
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true });
|
||||
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true, message: !!this.validationMessage } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,7 +711,7 @@ class InputBox extends QuickInput implements IInputBox {
|
||||
private noValidationMessage = InputBox.noPromptMessage;
|
||||
private _validationMessage: string;
|
||||
private onDidValueChangeEmitter = new Emitter<string>();
|
||||
private onDidAcceptEmitter = new Emitter<string>();
|
||||
private onDidAcceptEmitter = new Emitter<void>();
|
||||
|
||||
constructor(ui: QuickInputUI) {
|
||||
super(ui);
|
||||
@@ -768,13 +828,12 @@ class InputBox extends QuickInput implements IInputBox {
|
||||
|
||||
export class QuickInputService extends Component implements IQuickInputService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
public _serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static readonly ID = 'workbench.component.quickinput';
|
||||
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
|
||||
|
||||
private idPrefix = 'quickInput_'; // Constant since there is still only one.
|
||||
private layoutDimensions: dom.Dimension;
|
||||
private titleBar: HTMLElement;
|
||||
private filterContainer: HTMLElement;
|
||||
private visibleCountContainer: HTMLElement;
|
||||
@@ -791,24 +850,26 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
private onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
|
||||
private keyMods: Writeable<IKeyMods> = { ctrlCmd: false, alt: false };
|
||||
|
||||
private controller: QuickInput;
|
||||
private controller: QuickInput | null = null;
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IQuickOpenService private readonly quickOpenService: IQuickOpenService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(QuickInputService.ID, themeService, storageService);
|
||||
this.inQuickOpenContext = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
|
||||
this.inQuickOpenContext = InQuickOpenContextKey.bindTo(contextKeyService);
|
||||
this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true)));
|
||||
this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false)));
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
this.registerKeyModsListeners();
|
||||
}
|
||||
|
||||
@@ -830,7 +891,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
|
||||
private setContextKey(id?: string) {
|
||||
let key: IContextKey<boolean>;
|
||||
let key: IContextKey<boolean> | undefined;
|
||||
if (id) {
|
||||
key = this.contexts[id];
|
||||
if (!key) {
|
||||
@@ -860,7 +921,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
|
||||
private registerKeyModsListeners() {
|
||||
const workbench = this.partService.getWorkbenchElement();
|
||||
const workbench = this.layoutService.getWorkbenchElement();
|
||||
this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
switch (event.keyCode) {
|
||||
@@ -892,7 +953,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
return;
|
||||
}
|
||||
|
||||
const workbench = this.partService.getWorkbenchElement();
|
||||
const workbench = this.layoutService.getWorkbenchElement();
|
||||
const container = dom.append(workbench, $('.quick-input-widget.show-file-icons'));
|
||||
container.tabIndex = -1;
|
||||
container.style.display = 'none';
|
||||
@@ -971,7 +1032,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}));
|
||||
this._register(list.onDidChangeFocus(() => {
|
||||
if (this.comboboxAccessibility) {
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant() || '');
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -1060,7 +1121,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
return;
|
||||
}
|
||||
const input = this.createQuickPick<T>();
|
||||
let activeItem: T;
|
||||
let activeItem: T | undefined;
|
||||
const disposables = [
|
||||
input,
|
||||
input.onDidAccept(() => {
|
||||
@@ -1114,15 +1175,17 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
resolve(undefined);
|
||||
}),
|
||||
];
|
||||
input.canSelectMany = options.canPickMany;
|
||||
input.canSelectMany = !!options.canPickMany;
|
||||
input.placeholder = options.placeHolder;
|
||||
input.ignoreFocusOut = options.ignoreFocusLost;
|
||||
input.matchOnDescription = options.matchOnDescription;
|
||||
input.matchOnDetail = options.matchOnDetail;
|
||||
input.ignoreFocusOut = !!options.ignoreFocusLost;
|
||||
input.matchOnDescription = !!options.matchOnDescription;
|
||||
input.matchOnDetail = !!options.matchOnDetail;
|
||||
input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true
|
||||
input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true
|
||||
input.quickNavigate = options.quickNavigate;
|
||||
input.contextKey = options.contextKey;
|
||||
input.busy = true;
|
||||
Promise.all([picks, options.activeItem])
|
||||
Promise.all<QuickPickInput<T>[], T | undefined>([picks, options.activeItem])
|
||||
.then(([items, _activeItem]) => {
|
||||
activeItem = _activeItem;
|
||||
input.busy = false;
|
||||
@@ -1162,7 +1225,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
validation.then(result => {
|
||||
if (value === validationValue) {
|
||||
input.validationMessage = result;
|
||||
input.validationMessage = result || undefined;
|
||||
}
|
||||
});
|
||||
}),
|
||||
@@ -1189,12 +1252,12 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
resolve(undefined);
|
||||
}),
|
||||
];
|
||||
input.value = options.value;
|
||||
input.value = options.value || '';
|
||||
input.valueSelection = options.valueSelection;
|
||||
input.prompt = options.prompt;
|
||||
input.placeholder = options.placeHolder;
|
||||
input.password = options.password;
|
||||
input.ignoreFocusOut = options.ignoreFocusLost;
|
||||
input.password = !!options.password;
|
||||
input.ignoreFocusOut = !!options.ignoreFocusLost;
|
||||
input.show();
|
||||
});
|
||||
}
|
||||
@@ -1236,6 +1299,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.ui.list.setElements([]);
|
||||
this.ui.list.matchOnDescription = false;
|
||||
this.ui.list.matchOnDetail = false;
|
||||
this.ui.list.matchOnLabel = true;
|
||||
this.ui.ignoreFocusOut = false;
|
||||
this.setComboboxAccessibility(false);
|
||||
this.ui.inputBox.removeAttribute('aria-label');
|
||||
@@ -1259,7 +1323,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.countContainer.style.display = visibilities.count ? '' : 'none';
|
||||
this.okContainer.style.display = visibilities.ok ? '' : 'none';
|
||||
this.ui.message.style.display = visibilities.message ? '' : 'none';
|
||||
this.ui.list.display(visibilities.list);
|
||||
this.ui.list.display(!!visibilities.list);
|
||||
this.ui.container.classList[visibilities.checkAll ? 'add' : 'remove']('show-checkboxes');
|
||||
this.updateLayout(); // TODO
|
||||
}
|
||||
@@ -1271,7 +1335,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
this.ui.inputBox.setAttribute('role', 'combobox');
|
||||
this.ui.inputBox.setAttribute('aria-haspopup', 'true');
|
||||
this.ui.inputBox.setAttribute('aria-autocomplete', 'list');
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
|
||||
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant() || '');
|
||||
} else {
|
||||
this.ui.inputBox.removeAttribute('role');
|
||||
this.ui.inputBox.removeAttribute('aria-haspopup');
|
||||
@@ -1282,7 +1346,7 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
|
||||
private isScreenReaderOptimized() {
|
||||
const detected = browser.getAccessibilitySupport() === AccessibilitySupport.Enabled;
|
||||
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
|
||||
const config = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
|
||||
return config === 'on' || (config === 'auto' && detected);
|
||||
}
|
||||
@@ -1354,17 +1418,16 @@ export class QuickInputService extends Component implements IQuickInputService {
|
||||
}
|
||||
|
||||
layout(dimension: dom.Dimension): void {
|
||||
this.layoutDimensions = dimension;
|
||||
this.updateLayout();
|
||||
}
|
||||
|
||||
private updateLayout() {
|
||||
if (this.layoutDimensions && this.ui) {
|
||||
const titlebarOffset = this.partService.getTitleBarOffset();
|
||||
if (this.ui) {
|
||||
const titlebarOffset = this.layoutService.getTitleBarOffset();
|
||||
this.ui.container.style.top = `${titlebarOffset}px`;
|
||||
|
||||
const style = this.ui.container.style;
|
||||
const width = Math.min(this.layoutDimensions.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH);
|
||||
const width = Math.min(this.layoutService.dimension.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH);
|
||||
style.width = width + 'px';
|
||||
style.marginLeft = '-' + (width / 2) + 'px';
|
||||
|
||||
@@ -1400,7 +1463,7 @@ export const QuickPickManyToggle: ICommandAndKeybindingRule = {
|
||||
id: 'workbench.action.quickPickManyToggle',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickOpenContext,
|
||||
primary: undefined,
|
||||
primary: 0,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
quickInputService.toggle();
|
||||
@@ -1418,6 +1481,8 @@ export class BackAction extends Action {
|
||||
|
||||
public run(): Promise<any> {
|
||||
this.quickInputService.back();
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IQuickInputService, QuickInputService, true);
|
||||
@@ -11,6 +11,7 @@ import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -34,6 +35,12 @@ export class QuickInputBox {
|
||||
});
|
||||
}
|
||||
|
||||
onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => {
|
||||
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => {
|
||||
handler(new StandardMouseEvent(e));
|
||||
});
|
||||
}
|
||||
|
||||
onDidChange = (handler: (event: string) => void): IDisposable => {
|
||||
return this.inputBox.onDidChange(handler);
|
||||
}
|
||||
|
||||
@@ -220,6 +220,7 @@ export class QuickInputList {
|
||||
private elementsToIndexes = new Map<IQuickPickItem, number>();
|
||||
matchOnDescription = false;
|
||||
matchOnDetail = false;
|
||||
matchOnLabel = true;
|
||||
private _onChangedAllVisibleChecked = new Emitter<boolean>();
|
||||
onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
|
||||
private _onChangedCheckedCount = new Emitter<number>();
|
||||
@@ -397,6 +398,9 @@ export class QuickInputList {
|
||||
this.list.setFocus(items
|
||||
.filter(item => this.elementsToIndexes.has(item))
|
||||
.map(item => this.elementsToIndexes.get(item)!));
|
||||
if (items.length > 0) {
|
||||
this.list.reveal(this.list.getFocus()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveDescendant() {
|
||||
@@ -468,6 +472,9 @@ export class QuickInputList {
|
||||
}
|
||||
|
||||
filter(query: string) {
|
||||
if (!(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
|
||||
return;
|
||||
}
|
||||
query = query.trim();
|
||||
|
||||
// Reset filtering
|
||||
@@ -485,7 +492,7 @@ export class QuickInputList {
|
||||
// Filter by value (since we support octicons, use octicon aware fuzzy matching)
|
||||
else {
|
||||
this.elements.forEach(element => {
|
||||
const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined;
|
||||
const labelHighlights = this.matchOnLabel ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined : undefined;
|
||||
const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || '')) || undefined : undefined;
|
||||
const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || '')) || undefined : undefined;
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
|
||||
const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: null } };
|
||||
const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: undefined } };
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: QUICKOPEN_ACTION_ID,
|
||||
@@ -96,6 +96,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
secondary: [globalQuickOpenKeybinding.secondary[0] | KeyMod.Shift],
|
||||
mac: {
|
||||
primary: globalQuickOpenKeybinding.mac.primary | KeyMod.Shift,
|
||||
secondary: null
|
||||
secondary: undefined
|
||||
}
|
||||
});
|
||||
@@ -25,12 +25,12 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { EditorInput, IWorkbenchEditorConfiguration, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { Component } from 'vs/workbench/common/component';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG, SEARCH_EDITOR_HISTORY, PRESERVE_INPUT_CONFIG } from 'vs/workbench/browser/quickopen';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IQuickOpenService, IShowOptions } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -44,12 +44,13 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Dimension, addClass } from 'vs/base/browser/dom';
|
||||
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
const HELP_PREFIX = '?';
|
||||
|
||||
@@ -60,7 +61,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
private static readonly MAX_SHORT_RESPONSE_TIME = 500;
|
||||
private static readonly ID = 'workbench.component.quickopen';
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private readonly _onShow: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onShow(): Event<void> { return this._onShow.event; }
|
||||
@@ -73,17 +74,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
private lastInputValue: string;
|
||||
private lastSubmittedInputValue: string;
|
||||
private quickOpenWidget: QuickOpenWidget;
|
||||
private dimension: Dimension;
|
||||
private mapResolvedHandlersToPrefix: { [prefix: string]: Promise<QuickOpenHandler>; } = Object.create(null);
|
||||
private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; } = Object.create(null);
|
||||
private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null);
|
||||
private promisesToCompleteOnHide: ValueCallback[] = [];
|
||||
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor;
|
||||
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor | null;
|
||||
private actionProvider = new ContributableActionProvider();
|
||||
private closeOnFocusLost: boolean;
|
||||
private searchInEditorHistory: boolean;
|
||||
private editorHistoryHandler: EditorHistoryHandler;
|
||||
private pendingGetResultsInvocation: CancellationTokenSource;
|
||||
private pendingGetResultsInvocation: CancellationTokenSource | null;
|
||||
|
||||
constructor(
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@@ -91,7 +91,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IPartService private readonly partService: IPartService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@@ -107,8 +107,9 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration()));
|
||||
this._register(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this._register(this.layoutService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
|
||||
this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget()));
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
}
|
||||
|
||||
private updateConfiguration(): void {
|
||||
@@ -165,7 +166,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Telemetry: log that quick open is shown and log the mode
|
||||
const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen);
|
||||
const handlerDescriptor = registry.getQuickOpenHandler(prefix) || registry.getDefaultQuickOpenHandler();
|
||||
const handlerDescriptor = (prefix ? registry.getQuickOpenHandler(prefix) : undefined) || registry.getDefaultQuickOpenHandler();
|
||||
|
||||
// Trigger onOpen
|
||||
this.resolveHandler(handlerDescriptor);
|
||||
@@ -173,7 +174,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
// Create upon first open
|
||||
if (!this.quickOpenWidget) {
|
||||
this.quickOpenWidget = this._register(new QuickOpenWidget(
|
||||
this.partService.getWorkbenchElement(),
|
||||
this.layoutService.getWorkbenchElement(),
|
||||
{
|
||||
onOk: () => this.onOk(),
|
||||
onCancel: () => { /* ignore */ },
|
||||
@@ -195,9 +196,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
// Layout
|
||||
if (this.dimension) {
|
||||
this.quickOpenWidget.layout(this.dimension);
|
||||
}
|
||||
this.quickOpenWidget.layout(this.layoutService.dimension);
|
||||
|
||||
// Show quick open with prefix or editor history
|
||||
if (!this.quickOpenWidget.isVisible() || quickNavigateConfiguration) {
|
||||
@@ -206,7 +205,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
} else {
|
||||
const editorHistory = this.getEditorHistoryWithGroupLabel();
|
||||
if (editorHistory.getEntries().length < 2) {
|
||||
quickNavigateConfiguration = null; // If no entries can be shown, default to normal quick open mode
|
||||
quickNavigateConfiguration = undefined; // If no entries can be shown, default to normal quick open mode
|
||||
}
|
||||
|
||||
// Compute auto focus
|
||||
@@ -239,7 +238,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
private positionQuickOpenWidget(): void {
|
||||
const titlebarOffset = this.partService.getTitleBarOffset();
|
||||
const titlebarOffset = this.layoutService.getTitleBarOffset();
|
||||
|
||||
if (this.quickOpenWidget) {
|
||||
this.quickOpenWidget.getElement().style.top = `${titlebarOffset}px`;
|
||||
@@ -270,7 +269,10 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// Complete promises that are waiting
|
||||
while (this.promisesToCompleteOnHide.length) {
|
||||
this.promisesToCompleteOnHide.pop()(true);
|
||||
const callback = this.promisesToCompleteOnHide.pop();
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (reason !== HideReason.FOCUS_LOST) {
|
||||
@@ -297,7 +299,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
private setQuickOpenContextKey(id?: string): void {
|
||||
let key: IContextKey<boolean>;
|
||||
let key: IContextKey<boolean> | undefined;
|
||||
if (id) {
|
||||
key = this.mapContextKeyToContext[id];
|
||||
if (!key) {
|
||||
@@ -479,13 +481,13 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
|
||||
// merge history and default handler results
|
||||
const handlerResults = (result && result.entries) || [];
|
||||
this.mergeResults(quickOpenModel, handlerResults, resolvedHandler.getGroupLabel());
|
||||
this.mergeResults(quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel()));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string): void {
|
||||
private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string | undefined): void {
|
||||
|
||||
// Remove results already showing by checking for a "resource" property
|
||||
const mapEntryToResource = this.mapEntriesToResource(quickOpenModel);
|
||||
@@ -526,7 +528,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() }), resolvedHandler.getAriaLabel());
|
||||
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
@@ -547,9 +549,9 @@ 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() }), resolvedHandler.getAriaLabel());
|
||||
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
} else {
|
||||
this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), resolvedHandler.getAriaLabel());
|
||||
this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -570,15 +572,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
private clearModel(): void {
|
||||
this.showModel(new QuickOpenModel(), null);
|
||||
this.showModel(new QuickOpenModel(), undefined);
|
||||
}
|
||||
|
||||
private mapEntriesToResource(model: QuickOpenModel): { [resource: string]: QuickOpenEntry; } {
|
||||
const entries = model.getEntries();
|
||||
const mapEntryToPath: { [path: string]: QuickOpenEntry; } = {};
|
||||
entries.forEach((entry: QuickOpenEntry) => {
|
||||
if (entry.getResource()) {
|
||||
mapEntryToPath[entry.getResource().toString()] = entry;
|
||||
const resource = entry.getResource();
|
||||
if (resource) {
|
||||
mapEntryToPath[resource.toString()] = entry;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -620,9 +623,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): void {
|
||||
this.dimension = dimension;
|
||||
if (this.quickOpenWidget) {
|
||||
this.quickOpenWidget.layout(this.dimension);
|
||||
this.quickOpenWidget.layout(dimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,7 +657,7 @@ class EditorHistoryHandler {
|
||||
getResults(searchValue?: string, token?: CancellationToken): QuickOpenEntry[] {
|
||||
|
||||
// Massage search for scoring
|
||||
const query = prepareQuery(searchValue);
|
||||
const query = prepareQuery(searchValue || '');
|
||||
|
||||
// Just return all if we are not searching
|
||||
const history = this.historyService.getHistory();
|
||||
@@ -670,7 +672,7 @@ class EditorHistoryHandler {
|
||||
|
||||
// For now, only support to match on inputs that provide resource information
|
||||
.filter(input => {
|
||||
let resource: URI;
|
||||
let resource: URI | undefined;
|
||||
if (input instanceof EditorInput) {
|
||||
resource = resourceForEditorHistory(input, this.fileService);
|
||||
} else {
|
||||
@@ -690,7 +692,7 @@ class EditorHistoryHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
|
||||
e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch);
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -707,8 +709,8 @@ class EditorHistoryItemAccessorClass extends QuickOpenItemAccessorClass {
|
||||
super();
|
||||
}
|
||||
|
||||
getItemDescription(entry: QuickOpenEntry): string {
|
||||
return this.allowMatchOnDescription ? entry.getDescription() : undefined;
|
||||
getItemDescription(entry: QuickOpenEntry): string | null {
|
||||
return this.allowMatchOnDescription ? entry.getDescription() : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,9 +723,9 @@ export class EditorHistoryEntryGroup extends QuickOpenEntryGroup {
|
||||
|
||||
export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
private input: IEditorInput | IResourceInput;
|
||||
private resource: URI;
|
||||
private label: string;
|
||||
private description: string;
|
||||
private resource: URI | undefined;
|
||||
private label: string | null;
|
||||
private description: string | null;
|
||||
private dirty: boolean;
|
||||
|
||||
constructor(
|
||||
@@ -762,7 +764,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
return this.dirty ? 'dirty' : '';
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
getLabel(): string | null {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
@@ -776,12 +778,12 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
return nls.localize('entryAriaLabel', "{0}, recently opened", this.getLabel());
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
getDescription(): string | null {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
getResource(): URI | null {
|
||||
return this.resource || null;
|
||||
}
|
||||
|
||||
getInput(): IEditorInput | IResourceInput {
|
||||
@@ -806,7 +808,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
}
|
||||
}
|
||||
|
||||
function resourceForEditorHistory(input: EditorInput, fileService: IFileService): URI {
|
||||
function resourceForEditorHistory(input: EditorInput, fileService: IFileService): URI | undefined {
|
||||
const resource = input ? input.getResource() : undefined;
|
||||
|
||||
// For the editor history we only prefer resources that are either untitled or
|
||||
@@ -846,7 +848,7 @@ export class RemoveFromEditorHistoryAction extends Action {
|
||||
|
||||
return <IHistoryPickEntry>{
|
||||
input: h,
|
||||
iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource()),
|
||||
iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource() || undefined),
|
||||
label: entry.getLabel(),
|
||||
description: entry.getDescription()
|
||||
};
|
||||
@@ -859,3 +861,5 @@ export class RemoveFromEditorHistoryAction extends Action {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IQuickOpenService, QuickOpenController, true);
|
||||
@@ -8,23 +8,37 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const inQuickOpenContext = ContextKeyExpr.has('inQuickOpen');
|
||||
const inQuickOpenKey = 'inQuickOpen';
|
||||
export const InQuickOpenContextKey = new RawContextKey<boolean>(inQuickOpenKey, false);
|
||||
export const inQuickOpenContext = ContextKeyExpr.has(inQuickOpenKey);
|
||||
export const defaultQuickOpenContextKey = 'inFilesPicker';
|
||||
export const defaultQuickOpenContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(defaultQuickOpenContextKey));
|
||||
|
||||
export const QUICKOPEN_ACTION_ID = 'workbench.action.quickOpen';
|
||||
export const QUICKOPEN_ACION_LABEL = nls.localize('quickOpen', "Go to File...");
|
||||
|
||||
CommandsRegistry.registerCommand(QUICKOPEN_ACTION_ID, function (accessor: ServicesAccessor, prefix: string | null = null) {
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
CommandsRegistry.registerCommand({
|
||||
id: QUICKOPEN_ACTION_ID,
|
||||
handler: function (accessor: ServicesAccessor, prefix: string | null = null) {
|
||||
const quickOpenService = accessor.get(IQuickOpenService);
|
||||
|
||||
return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => {
|
||||
return undefined;
|
||||
});
|
||||
return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => {
|
||||
return undefined;
|
||||
});
|
||||
},
|
||||
description: {
|
||||
description: `Quick open`,
|
||||
args: [{
|
||||
name: 'prefix',
|
||||
schema: {
|
||||
'type': 'string'
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
export const QUICKOPEN_FOCUS_SECONDARY_ACTION_ID = 'workbench.action.quickOpenPreviousEditor';
|
||||
|
||||
@@ -12,59 +12,64 @@ import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions, ViewletDescr
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IViewlet, SidebarFocusContext, ActiveViewletContext } from 'vs/workbench/common/viewlet';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { EventType, addDisposableListener, trackFocus, Dimension } from 'vs/base/browser/dom';
|
||||
import { EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export const SidebarFocusContext = new RawContextKey<boolean>('sideBarFocus', false);
|
||||
export const ActiveViewletContext = new RawContextKey<string>('activeViewlet', '');
|
||||
export class SidebarPart extends CompositePart<Viewlet> implements IViewletService {
|
||||
|
||||
export class SidebarPart extends CompositePart<Viewlet> implements ISerializableView, IViewletService {
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
|
||||
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 170;
|
||||
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
readonly minimumHeight: number = 0;
|
||||
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
|
||||
readonly snapSize: number = 50;
|
||||
readonly priority: LayoutPriority = LayoutPriority.Low;
|
||||
|
||||
//#endregion
|
||||
|
||||
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
|
||||
|
||||
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
|
||||
get onDidViewletDeregister(): Event<ViewletDescriptor> { return this._onDidViewletDeregister.event; }
|
||||
|
||||
get onDidViewletOpen(): Event<IViewlet> { return Event.map(this.onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite); }
|
||||
get onDidViewletClose(): Event<IViewlet> { return this.onDidCompositeClose.event as Event<IViewlet>; }
|
||||
|
||||
private viewletRegistry: ViewletRegistry;
|
||||
private sideBarFocusContextKey: IContextKey<boolean>;
|
||||
private activeViewletContextKey: IContextKey<string>;
|
||||
private blockOpeningViewlet: boolean;
|
||||
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
|
||||
|
||||
element: HTMLElement;
|
||||
minimumWidth: number = 170;
|
||||
maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
minimumHeight: number = 0;
|
||||
maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
snapSize: number = 50;
|
||||
priority: LayoutPriority = LayoutPriority.Low;
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -76,7 +81,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
storageService,
|
||||
telemetryService,
|
||||
contextMenuService,
|
||||
partService,
|
||||
layoutService,
|
||||
keybindingService,
|
||||
instantiationService,
|
||||
themeService,
|
||||
@@ -86,56 +91,51 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
'sideBar',
|
||||
'viewlet',
|
||||
SIDE_BAR_TITLE_FOREGROUND,
|
||||
id,
|
||||
Parts.SIDEBAR_PART,
|
||||
{ hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 }
|
||||
);
|
||||
|
||||
this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService);
|
||||
this.viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
|
||||
|
||||
this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService);
|
||||
this.activeViewletContextKey = ActiveViewletContext.bindTo(contextKeyService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
// Viewlet open
|
||||
this._register(this.onDidViewletOpen(viewlet => {
|
||||
this.activeViewletContextKey.set(viewlet.getId());
|
||||
}));
|
||||
|
||||
// Viewlet close
|
||||
this._register(this.onDidViewletClose(viewlet => {
|
||||
if (this.activeViewletContextKey.get() === viewlet.getId()) {
|
||||
this.activeViewletContextKey.reset();
|
||||
}
|
||||
}));
|
||||
|
||||
// Viewlet deregister
|
||||
this._register(this.registry.onDidDeregister(async (viewletDescriptor: ViewletDescriptor) => {
|
||||
if (this.getActiveViewlet().getId() === viewletDescriptor.id) {
|
||||
await this.openViewlet(this.getDefaultViewletId());
|
||||
}
|
||||
|
||||
this.removeComposite(viewletDescriptor.id);
|
||||
this._onDidViewletDeregister.fire(viewletDescriptor);
|
||||
}));
|
||||
}
|
||||
|
||||
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
|
||||
get onDidViewletDeregister(): Event<ViewletDescriptor> { return this._onDidViewletDeregister.event; }
|
||||
|
||||
get onDidViewletOpen(): Event<IViewlet> {
|
||||
return Event.map(this._onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite);
|
||||
}
|
||||
|
||||
get onDidViewletClose(): Event<IViewlet> {
|
||||
return this._onDidCompositeClose.event as Event<IViewlet>;
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
this.element = parent;
|
||||
|
||||
super.create(parent);
|
||||
|
||||
const focusTracker = trackFocus(parent);
|
||||
|
||||
focusTracker.onDidFocus(() => {
|
||||
this.sideBarFocusContextKey.set(true);
|
||||
});
|
||||
focusTracker.onDidBlur(() => {
|
||||
this.sideBarFocusContextKey.set(false);
|
||||
});
|
||||
const focusTracker = this._register(trackFocus(parent));
|
||||
this._register(focusTracker.onDidFocus(() => this.sideBarFocusContextKey.set(true)));
|
||||
this._register(focusTracker.onDidBlur(() => this.sideBarFocusContextKey.set(false)));
|
||||
}
|
||||
|
||||
createTitleArea(parent: HTMLElement): HTMLElement {
|
||||
@@ -158,7 +158,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
container.style.color = this.getColor(SIDE_BAR_FOREGROUND);
|
||||
|
||||
const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
|
||||
const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
|
||||
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;
|
||||
@@ -167,22 +167,12 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
container.style.borderLeftColor = !isPositionLeft ? borderColor : null;
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
if (dim1 instanceof Dimension) {
|
||||
return [dim1];
|
||||
}
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dim1 instanceof Dimension) {
|
||||
return super.layout(dim1);
|
||||
}
|
||||
|
||||
super.layout(dim1, dim2!);
|
||||
super.layout(width, height);
|
||||
}
|
||||
|
||||
// Viewlet service
|
||||
@@ -199,15 +189,17 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
this.hideActiveComposite();
|
||||
}
|
||||
|
||||
openViewlet(id: string, focus?: boolean): Promise<IViewlet | null> {
|
||||
if (this.getViewlet(id)) {
|
||||
openViewlet(id: string | undefined, focus?: boolean): Promise<IViewlet | null> {
|
||||
if (typeof id === 'string' && this.getViewlet(id)) {
|
||||
return Promise.resolve(this.doOpenViewlet(id, focus));
|
||||
}
|
||||
|
||||
return this.extensionService.whenInstalledExtensionsRegistered()
|
||||
.then(() => {
|
||||
if (this.getViewlet(id)) {
|
||||
if (typeof id === 'string' && this.getViewlet(id)) {
|
||||
return this.doOpenViewlet(id, focus);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -231,10 +223,10 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
}
|
||||
|
||||
// First check if sidebar is hidden and show if so
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
try {
|
||||
this.blockOpeningViewlet = true;
|
||||
this.partService.setSideBarHidden(false);
|
||||
this.layoutService.setSideBarHidden(false);
|
||||
} finally {
|
||||
this.blockOpeningViewlet = false;
|
||||
}
|
||||
@@ -244,7 +236,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
|
||||
}
|
||||
|
||||
protected getTitleAreaDropDownAnchorAlignment(): AnchorAlignment {
|
||||
return this.partService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT;
|
||||
return this.layoutService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT;
|
||||
}
|
||||
|
||||
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
|
||||
@@ -279,7 +271,7 @@ class FocusSideBarAction extends Action {
|
||||
id: string,
|
||||
label: string,
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
@@ -287,8 +279,8 @@ class FocusSideBarAction extends Action {
|
||||
run(): Promise<any> {
|
||||
|
||||
// Show side bar
|
||||
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return Promise.resolve(this.partService.setSideBarHidden(false));
|
||||
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
|
||||
return Promise.resolve(this.layoutService.setSideBarHidden(false));
|
||||
}
|
||||
|
||||
// Focus into active viewlet
|
||||
@@ -305,3 +297,5 @@ const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.Workbenc
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_0
|
||||
}), 'View: Focus into Side Bar', nls.localize('viewCategory', "View"));
|
||||
|
||||
registerSingleton(IViewletService, SidebarPart);
|
||||
@@ -33,25 +33,30 @@
|
||||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child {
|
||||
margin-right: 5px;
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child,
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child
|
||||
{
|
||||
margin-right: 3px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* adding padding to the most left status bar item */
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left {
|
||||
padding-left: 10px;
|
||||
padding-left: 7px;
|
||||
}
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.has-background-color.left {
|
||||
padding-right: 7px; /* expand padding if background color is configured for the status bar entry to make it look centered properly */
|
||||
}
|
||||
/* adding padding to the most right status bar item */
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.right:first-child {
|
||||
padding-right: 10px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.right:first-child {
|
||||
padding-left: 7px; /* expand padding if background color is configured for the status bar entry to make it look centered properly */
|
||||
}
|
||||
|
||||
.monaco-workbench .part.statusbar > .statusbar-item a {
|
||||
|
||||
@@ -6,60 +6,57 @@
|
||||
import 'vs/css!./media/statusbarpart';
|
||||
import * as nls from 'vs/nls';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { dispose, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { IStatusbarRegistry, Extensions, IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { isThemeColor } from 'vs/editor/common/editorCommon';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { addClass, EventHelper, createStyleSheet, addDisposableListener, Dimension } from 'vs/base/browser/dom';
|
||||
import { addClass, EventHelper, createStyleSheet, addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export class StatusbarPart extends Part implements IStatusbarService {
|
||||
|
||||
export class StatusbarPart extends Part implements IStatusbarService, ISerializableView {
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private static readonly PRIORITY_PROP = 'statusbar-entry-priority';
|
||||
private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment';
|
||||
|
||||
element: HTMLElement;
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 0;
|
||||
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
readonly minimumHeight: number = 22;
|
||||
readonly maximumHeight: number = 22;
|
||||
|
||||
//#endregion
|
||||
|
||||
private statusMsgDispose: IDisposable;
|
||||
|
||||
|
||||
minimumWidth: number = 0;
|
||||
maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
minimumHeight: number = 22;
|
||||
maximumHeight: number = 22;
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private styleElement: HTMLStyleElement;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
@@ -152,7 +149,7 @@ export class StatusbarPart extends Part implements IStatusbarService, ISerializa
|
||||
return this.element;
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
const container = this.getContainer();
|
||||
@@ -233,14 +230,8 @@ export class StatusbarPart extends Part implements IStatusbarService, ISerializa
|
||||
return dispose;
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
if (dim1 instanceof Dimension) {
|
||||
return super.layout(dim1);
|
||||
} else {
|
||||
super.layout(new Dimension(dim1, dim2!));
|
||||
}
|
||||
layout(width: number, height: number): void {
|
||||
super.layoutContents(width, height);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
@@ -292,18 +283,13 @@ class StatusBarEntryItem implements IStatusbarItem {
|
||||
textContainer.title = this.entry.tooltip;
|
||||
}
|
||||
|
||||
// Color
|
||||
let color = this.entry.color;
|
||||
if (color) {
|
||||
if (isThemeColor(color)) {
|
||||
let colorId = color.id;
|
||||
color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
|
||||
toDispose.push(this.themeService.onThemeChange(theme => {
|
||||
let colorValue = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
|
||||
textContainer.style.color = colorValue;
|
||||
}));
|
||||
}
|
||||
textContainer.style.color = color;
|
||||
// Color (only applies to text container)
|
||||
toDispose.push(this.applyColor(textContainer, this.entry.color));
|
||||
|
||||
// Background Color (applies to parent element to fully fill container)
|
||||
if (this.entry.backgroundColor) {
|
||||
toDispose.push(this.applyColor(el, this.entry.backgroundColor, true));
|
||||
addClass(el, 'has-background-color');
|
||||
}
|
||||
|
||||
// Context Menu
|
||||
@@ -328,6 +314,24 @@ class StatusBarEntryItem implements IStatusbarItem {
|
||||
};
|
||||
}
|
||||
|
||||
private applyColor(container: HTMLElement, color: string | ThemeColor | undefined, isBackground?: boolean): IDisposable {
|
||||
const disposable: IDisposable[] = [];
|
||||
|
||||
if (color) {
|
||||
if (isThemeColor(color)) {
|
||||
const colorId = color.id;
|
||||
color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
|
||||
disposable.push(this.themeService.onThemeChange(theme => {
|
||||
const colorValue = (theme.getColor(colorId) || Color.transparent).toString();
|
||||
isBackground ? container.style.backgroundColor = colorValue : container.style.color = colorValue;
|
||||
}));
|
||||
}
|
||||
isBackground ? container.style.backgroundColor = color : container.style.color = color;
|
||||
}
|
||||
|
||||
return combinedDisposable(disposable);
|
||||
}
|
||||
|
||||
private executeCommand(id: string, args?: any[]) {
|
||||
args = args || [];
|
||||
|
||||
@@ -382,3 +386,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`);
|
||||
}
|
||||
});
|
||||
|
||||
registerSingleton(IStatusbarService, StatusbarPart);
|
||||
@@ -42,8 +42,8 @@
|
||||
|
||||
/* Windows/Linux: Rules for custom title (icon, window controls) */
|
||||
|
||||
.windows > .monaco-workbench .part.titlebar,
|
||||
.linux > .monaco-workbench .part.titlebar {
|
||||
.monaco-workbench.windows .part.titlebar,
|
||||
.monaco-workbench.linux .part.titlebar {
|
||||
padding: 0;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
@@ -51,17 +51,17 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.windows > .monaco-workbench .part.titlebar > .window-title,
|
||||
.linux > .monaco-workbench .part.titlebar > .window-title {
|
||||
.monaco-workbench.windows .part.titlebar > .window-title,
|
||||
.monaco-workbench.linux .part.titlebar > .window-title {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.linux > .monaco-workbench .part.titlebar > .window-title {
|
||||
.monaco-workbench.linux .part.titlebar > .window-title {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.windows > .monaco-workbench .part.titlebar > .resizer,
|
||||
.linux > .monaco-workbench .part.titlebar > .resizer {
|
||||
.monaco-workbench.windows .part.titlebar > .resizer,
|
||||
.monaco-workbench.linux .part.titlebar > .resizer {
|
||||
-webkit-app-region: no-drag;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -69,17 +69,16 @@
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
.windows > .monaco-workbench.fullscreen .part.titlebar > .resizer,
|
||||
.linux > .monaco-workbench.fullscreen .part.titlebar > .resizer {
|
||||
.monaco-workbench.windows.fullscreen .part.titlebar > .resizer,
|
||||
.monaco-workbench.linux.fullscreen .part.titlebar > .resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.monaco-workbench .part.titlebar > .window-appicon {
|
||||
width: 35px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
z-index: 3000;
|
||||
background-image: url('code-icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
@@ -97,7 +96,7 @@
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
z-index: 3000;
|
||||
-webkit-app-region: no-drag;
|
||||
height: 100%;
|
||||
width: 138px;
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar';
|
||||
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, URIType } 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';
|
||||
@@ -17,8 +17,7 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IRecentlyOpened } from 'vs/platform/history/common/history';
|
||||
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IRecentlyOpened, isRecentFolder, IRecent, isRecentWorkspace } from 'vs/platform/history/common/history';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_BORDER, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -32,6 +31,9 @@ import { MenuBar } from 'vs/base/browser/ui/menu/menubar';
|
||||
import { SubmenuAction } from 'vs/base/browser/ui/menu/menu';
|
||||
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export class MenubarControl extends Disposable {
|
||||
|
||||
@@ -57,7 +59,7 @@ export class MenubarControl extends Disposable {
|
||||
// 'Terminal': IMenu;
|
||||
'Window'?: IMenu;
|
||||
'Help': IMenu;
|
||||
// [index: string]: IMenu;
|
||||
// [index: string]: IMenu | undefined;
|
||||
};
|
||||
|
||||
// {{SQL CARBON EDIT}} - Disable unused menus
|
||||
@@ -76,9 +78,10 @@ export class MenubarControl extends Disposable {
|
||||
private menuUpdater: RunOnceScheduler;
|
||||
private container: HTMLElement;
|
||||
private recentlyOpened: IRecentlyOpened;
|
||||
private alwaysOnMnemonics: boolean;
|
||||
|
||||
private _onVisibilityChange: Emitter<boolean>;
|
||||
private _onFocusStateChange: Emitter<boolean>;
|
||||
private readonly _onVisibilityChange: Emitter<boolean>;
|
||||
private readonly _onFocusStateChange: Emitter<boolean>;
|
||||
|
||||
private static MAX_MENU_RECENT_ENTRIES = 10;
|
||||
|
||||
@@ -96,7 +99,8 @@ export class MenubarControl extends Disposable {
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IPreferencesService private readonly preferencesService: IPreferencesService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
|
||||
) {
|
||||
|
||||
super();
|
||||
@@ -120,8 +124,11 @@ export class MenubarControl extends Disposable {
|
||||
this.menuUpdater = this._register(new RunOnceScheduler(() => this.doUpdateMenubar(false), 200));
|
||||
|
||||
if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') {
|
||||
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
|
||||
this._register(this.topLevelMenus[topLevelMenuName].onDidChange(() => this.updateMenubar()));
|
||||
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
|
||||
const menu = this.topLevelMenus[topLevelMenuName];
|
||||
if (menu) {
|
||||
this._register(menu.onDidChange(() => this.updateMenubar()));
|
||||
}
|
||||
}
|
||||
|
||||
this.doUpdateMenubar(true);
|
||||
@@ -134,7 +141,9 @@ export class MenubarControl extends Disposable {
|
||||
this.recentlyOpened = recentlyOpened;
|
||||
});
|
||||
|
||||
this.detectAndRecommendCustomTitlebar();
|
||||
this.notifyExistingLinuxUser();
|
||||
|
||||
this.notifyUserOfCustomMenubarAccessibility();
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
@@ -194,8 +203,8 @@ export class MenubarControl extends Disposable {
|
||||
this.updateMenubar();
|
||||
}
|
||||
|
||||
if (event.affectsConfiguration('window.menuBarVisibility')) {
|
||||
this.detectAndRecommendCustomTitlebar();
|
||||
if (event.affectsConfiguration('editor.accessibilitySupport')) {
|
||||
this.notifyUserOfCustomMenubarAccessibility();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,41 +215,63 @@ export class MenubarControl extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private detectAndRecommendCustomTitlebar(): void {
|
||||
// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation
|
||||
// if (!isLinux) {
|
||||
// return;
|
||||
// }
|
||||
// TODO@sbatten remove after feb19
|
||||
private notifyExistingLinuxUser(): void {
|
||||
/*// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation
|
||||
if (!isLinux) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) {
|
||||
// if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') {
|
||||
// // Issue will not arise for user, abort notification
|
||||
// return;
|
||||
// }
|
||||
const isNewUser = !this.storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL);
|
||||
const hasBeenNotified = this.storageService.getBoolean('menubar/linuxTitlebarRevertNotified', StorageScope.GLOBAL, false);
|
||||
const titleBarConfiguration = this.configurationService.inspect('window.titleBarStyle');
|
||||
const customShown = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
|
||||
|
||||
// const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar.");
|
||||
// this.notificationService.prompt(Severity.Info, message, [
|
||||
// {
|
||||
// label: nls.localize('goToSetting', "Open Settings"),
|
||||
// run: () => {
|
||||
// return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: nls.localize('moreInfo', "More Info"),
|
||||
// run: () => {
|
||||
// window.open('https://go.microsoft.com/fwlink/?linkid=2038566');
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: nls.localize('neverShowAgain', "Don't Show Again"),
|
||||
// run: () => {
|
||||
// this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL);
|
||||
// }
|
||||
// }
|
||||
// ]);
|
||||
// }
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
if (!hasBeenNotified) {
|
||||
this.storageService.store('menubar/linuxTitlebarRevertNotified', true, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
if (isNewUser || hasBeenNotified || (titleBarConfiguration && titleBarConfiguration.user) || customShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = nls.localize('menubar.linuxTitlebarRevertNotification', "We have updated the default title bar on Linux to use the native setting. If you prefer, you can go back to the custom setting. More information is available in our [online documentation](https://go.microsoft.com/fwlink/?linkid=2074137).");
|
||||
this.notificationService.prompt(Severity.Info, message, [
|
||||
{
|
||||
label: nls.localize('goToSetting', "Open Settings"),
|
||||
run: () => {
|
||||
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
|
||||
}
|
||||
}
|
||||
]);
|
||||
*/
|
||||
}
|
||||
|
||||
private notifyUserOfCustomMenubarAccessibility(): void {
|
||||
if (isMacintosh) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false);
|
||||
const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
|
||||
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
|
||||
const config = this.configurationService.getValue('editor.accessibilitySupport');
|
||||
|
||||
if (hasBeenNotified || usingCustomMenubar || !(config === 'on' || (config === 'auto' && detected))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = nls.localize('menubar.customTitlebarAccessibilityNotification', "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style.");
|
||||
this.notificationService.prompt(Severity.Info, message, [
|
||||
{
|
||||
label: nls.localize('goToSetting', "Open Settings"),
|
||||
run: () => {
|
||||
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
@@ -322,26 +353,34 @@ export class MenubarControl extends Disposable {
|
||||
return label;
|
||||
}
|
||||
|
||||
private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): IAction & { uri: URI } {
|
||||
private createOpenRecentMenuAction(recent: IRecent, isFile: boolean): IAction & { uri: URI } {
|
||||
|
||||
let label: string;
|
||||
let uri: URI;
|
||||
let commandId: string;
|
||||
let typeHint: URIType | undefined;
|
||||
|
||||
if (isSingleFolderWorkspaceIdentifier(workspace) && !isFile) {
|
||||
label = this.labelService.getWorkspaceLabel(workspace, { verbose: true });
|
||||
uri = workspace;
|
||||
} else if (isWorkspaceIdentifier(workspace)) {
|
||||
label = this.labelService.getWorkspaceLabel(workspace, { verbose: true });
|
||||
uri = URI.file(workspace.configPath);
|
||||
if (isRecentFolder(recent)) {
|
||||
uri = recent.folderUri;
|
||||
label = recent.label || this.labelService.getWorkspaceLabel(uri, { verbose: true });
|
||||
commandId = 'openRecentFolder';
|
||||
typeHint = 'folder';
|
||||
} else if (isRecentWorkspace(recent)) {
|
||||
uri = recent.workspace.configPath;
|
||||
label = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: true });
|
||||
commandId = 'openRecentWorkspace';
|
||||
typeHint = 'file';
|
||||
} else {
|
||||
uri = workspace;
|
||||
label = this.labelService.getUriLabel(uri);
|
||||
uri = recent.fileUri;
|
||||
label = recent.label || this.labelService.getUriLabel(uri);
|
||||
commandId = 'openRecentFile';
|
||||
typeHint = 'file';
|
||||
}
|
||||
|
||||
const ret: IAction = new Action(commandId, label, undefined, undefined, (event) => {
|
||||
const ret: IAction = new Action(commandId, unmnemonicLabel(label), undefined, undefined, (event) => {
|
||||
const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
|
||||
|
||||
return this.windowService.openWindow([uri], {
|
||||
return this.windowService.openWindow([{ uri, typeHint }], {
|
||||
forceNewWindow: openInNewWindow,
|
||||
forceOpenWorkspaceAsFile: isFile
|
||||
});
|
||||
@@ -362,7 +401,7 @@ export class MenubarControl extends Disposable {
|
||||
|
||||
if (workspaces.length > 0) {
|
||||
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) {
|
||||
result.push(this.createOpenRecentMenuAction(workspaces[i], 'openRecentWorkspace', false));
|
||||
result.push(this.createOpenRecentMenuAction(workspaces[i], false));
|
||||
}
|
||||
|
||||
result.push(new Separator());
|
||||
@@ -370,7 +409,7 @@ export class MenubarControl extends Disposable {
|
||||
|
||||
if (files.length > 0) {
|
||||
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
|
||||
result.push(this.createOpenRecentMenuAction(files[i], 'openRecentFile', false));
|
||||
result.push(this.createOpenRecentMenuAction(files[i], true));
|
||||
}
|
||||
|
||||
result.push(new Separator());
|
||||
@@ -408,7 +447,7 @@ export class MenubarControl extends Disposable {
|
||||
return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false);
|
||||
|
||||
case StateType.AvailableForDownload:
|
||||
return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), null, true, () =>
|
||||
return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), undefined, true, () =>
|
||||
this.updateService.downloadUpdate());
|
||||
|
||||
case StateType.Downloading:
|
||||
@@ -437,6 +476,7 @@ export class MenubarControl extends Disposable {
|
||||
if (!isMacintosh) {
|
||||
const updateAction = this.getUpdateAction();
|
||||
if (updateAction) {
|
||||
updateAction.label = mnemonicMenuLabel(updateAction.label);
|
||||
target.push(updateAction);
|
||||
target.push(new Separator());
|
||||
}
|
||||
@@ -459,12 +499,17 @@ export class MenubarControl extends Disposable {
|
||||
}
|
||||
));
|
||||
|
||||
this.accessibilityService.alwaysUnderlineAccessKeys().then(val => {
|
||||
this.alwaysOnMnemonics = val;
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
|
||||
});
|
||||
|
||||
this._register(this.menubar.onFocusStateChange(e => this._onFocusStateChange.fire(e)));
|
||||
this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e)));
|
||||
|
||||
this._register(attachMenuStyler(this.menubar, this.themeService));
|
||||
} else {
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id) });
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
|
||||
}
|
||||
|
||||
// Update the menu actions
|
||||
@@ -480,10 +525,10 @@ export class MenubarControl extends Disposable {
|
||||
const submenu = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
|
||||
const submenuActions: SubmenuAction[] = [];
|
||||
updateActions(submenu, submenuActions);
|
||||
target.push(new SubmenuAction(action.label, submenuActions));
|
||||
target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions));
|
||||
submenu.dispose();
|
||||
} else {
|
||||
action.label = this.calculateActionLabel(action);
|
||||
action.label = mnemonicMenuLabel(this.calculateActionLabel(action));
|
||||
target.push(action);
|
||||
}
|
||||
}
|
||||
@@ -494,28 +539,30 @@ export class MenubarControl extends Disposable {
|
||||
target.pop();
|
||||
};
|
||||
|
||||
for (let title of Object.keys(this.topLevelMenus)) {
|
||||
for (const title of Object.keys(this.topLevelMenus)) {
|
||||
const menu = this.topLevelMenus[title];
|
||||
if (firstTime) {
|
||||
if (firstTime && menu) {
|
||||
this._register(menu.onDidChange(() => {
|
||||
const actions = [];
|
||||
const actions: IAction[] = [];
|
||||
updateActions(menu, actions);
|
||||
this.menubar.updateMenu({ actions: actions, label: this.topLevelTitles[title] });
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
}));
|
||||
}
|
||||
|
||||
const actions = [];
|
||||
updateActions(menu, actions);
|
||||
const actions: IAction[] = [];
|
||||
if (menu) {
|
||||
updateActions(menu, actions);
|
||||
}
|
||||
|
||||
if (!firstTime) {
|
||||
this.menubar.updateMenu({ actions: actions, label: this.topLevelTitles[title] });
|
||||
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
} else {
|
||||
this.menubar.push({ actions: actions, label: this.topLevelTitles[title] });
|
||||
this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getMenubarKeybinding(id: string): IMenubarKeybinding {
|
||||
private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined {
|
||||
const binding = this.keybindingService.lookupKeybinding(id);
|
||||
if (!binding) {
|
||||
return undefined;
|
||||
@@ -524,19 +571,19 @@ export class MenubarControl extends Disposable {
|
||||
// first try to resolve a native accelerator
|
||||
const electronAccelerator = binding.getElectronAccelerator();
|
||||
if (electronAccelerator) {
|
||||
return { label: electronAccelerator, userSettingsLabel: binding.getUserSettingsLabel() };
|
||||
return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
|
||||
}
|
||||
|
||||
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
|
||||
const acceleratorLabel = binding.getLabel();
|
||||
if (acceleratorLabel) {
|
||||
return { label: acceleratorLabel, isNative: false, userSettingsLabel: binding.getUserSettingsLabel() };
|
||||
return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding }) {
|
||||
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) {
|
||||
let groups = menu.getActions();
|
||||
for (let group of groups) {
|
||||
const [, actions] = group;
|
||||
@@ -604,15 +651,17 @@ export class MenubarControl extends Disposable {
|
||||
}
|
||||
|
||||
menubarData.keybindings = this.getAdditionalKeybindings();
|
||||
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
|
||||
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
|
||||
const menu = this.topLevelMenus[topLevelMenuName];
|
||||
let menubarMenu: IMenubarMenu = { items: [] };
|
||||
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
|
||||
if (menubarMenu.items.length === 0) {
|
||||
// Menus are incomplete
|
||||
return false;
|
||||
if (menu) {
|
||||
const menubarMenu: IMenubarMenu = { items: [] };
|
||||
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
|
||||
if (menubarMenu.items.length === 0) {
|
||||
// Menus are incomplete
|
||||
return false;
|
||||
}
|
||||
menubarData.menus[topLevelMenuName] = menubarMenu;
|
||||
}
|
||||
menubarData.menus[topLevelMenuName] = menubarMenu;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -632,7 +681,7 @@ export class MenubarControl extends Disposable {
|
||||
}
|
||||
|
||||
if (this.menubar) {
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id) });
|
||||
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/titlebarpart';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { dirname, posix } from 'vs/base/common/path';
|
||||
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';
|
||||
@@ -28,17 +28,16 @@ import { Color } from 'vs/base/common/color';
|
||||
import { trim } from 'vs/base/common/strings';
|
||||
import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
|
||||
import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { template, getBaseLabel } from 'vs/base/common/labels';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
|
||||
import { Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export class TitlebarPart extends Part implements ITitleService, ISerializableView {
|
||||
|
||||
_serviceBrand: any;
|
||||
export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
private static readonly NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
|
||||
private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[Superuser]");
|
||||
@@ -46,7 +45,20 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
private static readonly TITLE_DIRTY = '\u25cf ';
|
||||
private static readonly TITLE_SEPARATOR = isMacintosh ? ' — ' : ' - '; // macOS uses special - separator
|
||||
|
||||
element: HTMLElement;
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 0;
|
||||
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
|
||||
get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
|
||||
|
||||
//#endregion
|
||||
|
||||
private _onMenubarVisibilityChange = this._register(new Emitter<boolean>());
|
||||
get onMenubarVisibilityChange(): Event<boolean> { return this._onMenubarVisibilityChange.event; }
|
||||
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private title: HTMLElement;
|
||||
private dragRegion: HTMLElement;
|
||||
private windowControls: HTMLElement;
|
||||
@@ -55,6 +67,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
private menubarPart: MenubarControl;
|
||||
private menubar: HTMLElement;
|
||||
private resizer: HTMLElement;
|
||||
private lastLayoutDimensions: Dimension;
|
||||
|
||||
private pendingTitle: string;
|
||||
private representedFileName: string;
|
||||
@@ -64,16 +77,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
private properties: ITitleProperties;
|
||||
private activeEditorListeners: IDisposable[];
|
||||
|
||||
minimumWidth: number = 0;
|
||||
maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
|
||||
get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
|
||||
|
||||
private _onDidChange = new Emitter<{ width: number; height: number; }>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
private titleUpdater: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.doUpdateTitle(), 0));
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@@ -84,9 +90,10 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IStorageService storageService: IStorageService
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, { hasTitle: false }, themeService, storageService);
|
||||
super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
this.properties = { isPure: true, isAdmin: false };
|
||||
this.activeEditorListeners = [];
|
||||
@@ -98,10 +105,10 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
this._register(this.windowService.onDidChangeFocus(focused => focused ? this.onFocus() : this.onBlur()));
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.doUpdateTitle()));
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.doUpdateTitle()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceName(() => this.doUpdateTitle()));
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.doUpdateTitle()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.titleUpdater.schedule()));
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.titleUpdater.schedule()));
|
||||
this._register(this.contextService.onDidChangeWorkspaceName(() => this.titleUpdater.schedule()));
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.titleUpdater.schedule()));
|
||||
}
|
||||
|
||||
private onBlur(): void {
|
||||
@@ -116,7 +123,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
|
||||
private onConfigurationChanged(event: IConfigurationChangeEvent): void {
|
||||
if (event.affectsConfiguration('window.title')) {
|
||||
this.doUpdateTitle();
|
||||
this.titleUpdater.schedule();
|
||||
}
|
||||
|
||||
if (event.affectsConfiguration('window.doubleClickIconToClose')) {
|
||||
@@ -136,6 +143,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
}
|
||||
|
||||
this.adjustTitleMarginToCenter();
|
||||
|
||||
this._onMenubarVisibilityChange.fire(visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,10 +158,6 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
}
|
||||
}
|
||||
|
||||
onMenubarVisibilityChange(): Event<boolean> {
|
||||
return this.menubarPart.onVisibilityChange;
|
||||
}
|
||||
|
||||
private onActiveEditorChange(): void {
|
||||
|
||||
// Dispose old listeners
|
||||
@@ -160,13 +165,13 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
this.activeEditorListeners = [];
|
||||
|
||||
// Calculate New Window Title
|
||||
this.doUpdateTitle();
|
||||
this.titleUpdater.schedule();
|
||||
|
||||
// Apply listener for dirty and label changes
|
||||
const activeEditor = this.editorService.activeEditor;
|
||||
if (activeEditor instanceof EditorInput) {
|
||||
this.activeEditorListeners.push(activeEditor.onDidChangeDirty(() => this.doUpdateTitle()));
|
||||
this.activeEditorListeners.push(activeEditor.onDidChangeLabel(() => this.doUpdateTitle()));
|
||||
this.activeEditorListeners.push(activeEditor.onDidChangeDirty(() => this.titleUpdater.schedule()));
|
||||
this.activeEditorListeners.push(activeEditor.onDidChangeLabel(() => this.titleUpdater.schedule()));
|
||||
}
|
||||
|
||||
// Represented File Name
|
||||
@@ -174,7 +179,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
}
|
||||
|
||||
private updateRepresentedFilename(): void {
|
||||
const file = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: 'file' });
|
||||
const file = toResource(this.editorService.activeEditor || null, { supportSideBySide: true, filter: 'file' });
|
||||
const path = file ? file.fsPath : '';
|
||||
|
||||
// Apply to window
|
||||
@@ -202,7 +207,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
}
|
||||
|
||||
if ((isWindows || isLinux) && this.title) {
|
||||
this.adjustTitleMarginToCenter();
|
||||
if (this.lastLayoutDimensions) {
|
||||
this.updateLayout(this.lastLayoutDimensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +239,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
this.properties.isAdmin = isAdmin;
|
||||
this.properties.isPure = isPure;
|
||||
|
||||
this.doUpdateTitle();
|
||||
this.titleUpdater.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +265,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
|
||||
// Compute root
|
||||
let root: URI;
|
||||
let root: URI | undefined;
|
||||
if (workspace.configuration) {
|
||||
root = workspace.configuration;
|
||||
} else if (workspace.folders.length) {
|
||||
@@ -275,7 +282,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
// 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: true }));
|
||||
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor || null, { supportSideBySide: true })!);
|
||||
|
||||
// Variables
|
||||
const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
|
||||
@@ -343,7 +350,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
if (this.pendingTitle) {
|
||||
this.title.innerText = this.pendingTitle;
|
||||
} else {
|
||||
this.doUpdateTitle();
|
||||
this.titleUpdater.schedule();
|
||||
}
|
||||
|
||||
// Maximize/Restore on doubleclick
|
||||
@@ -452,7 +459,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
this.adjustTitleMarginToCenter();
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
updateStyles(): void {
|
||||
super.updateStyles();
|
||||
|
||||
// Part container
|
||||
@@ -465,7 +472,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
|
||||
const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND);
|
||||
this.element.style.backgroundColor = titleBackground;
|
||||
if (Color.fromHex(titleBackground).isLighter()) {
|
||||
if (titleBackground && Color.fromHex(titleBackground).isLighter()) {
|
||||
addClass(this.element, 'light');
|
||||
} else {
|
||||
removeClass(this.element, 'light');
|
||||
@@ -487,8 +494,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
const setting = this.configurationService.getValue('window.doubleClickIconToClose');
|
||||
if (setting) {
|
||||
this.appIcon.style['-webkit-app-region'] = 'no-drag';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.appIcon.style['-webkit-app-region'] = 'drag';
|
||||
}
|
||||
}
|
||||
@@ -514,7 +520,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
const actions: IAction[] = [];
|
||||
|
||||
if (this.representedFileName) {
|
||||
const segments = this.representedFileName.split(paths.sep);
|
||||
const segments = this.representedFileName.split(posix.sep);
|
||||
for (let i = segments.length; i > 0; i--) {
|
||||
const isFile = (i === segments.length);
|
||||
|
||||
@@ -523,16 +529,16 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
pathOffset++; // for segments which are not the file name we want to open the folder
|
||||
}
|
||||
|
||||
const path = segments.slice(0, pathOffset).join(paths.sep);
|
||||
const path = segments.slice(0, pathOffset).join(posix.sep);
|
||||
|
||||
let label: string;
|
||||
if (!isFile) {
|
||||
label = getBaseLabel(paths.dirname(path));
|
||||
label = getBaseLabel(dirname(path));
|
||||
} else {
|
||||
label = getBaseLabel(path);
|
||||
}
|
||||
|
||||
actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService));
|
||||
actions.push(new ShowItemInFolderAction(path, label || posix.sep, this.windowsService));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,6 +560,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
}
|
||||
|
||||
updateLayout(dimension: Dimension): void {
|
||||
this.lastLayoutDimensions = dimension;
|
||||
|
||||
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
|
||||
// Only prevent zooming behavior on macOS or when the menubar is not visible
|
||||
if (isMacintosh || this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden') {
|
||||
@@ -573,25 +581,16 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
|
||||
runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
|
||||
|
||||
if (this.menubarPart) {
|
||||
const menubarDimension = new Dimension(undefined, dimension.height);
|
||||
const menubarDimension = new Dimension(0, dimension.height);
|
||||
this.menubarPart.layout(menubarDimension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(dimension: Dimension): Dimension[];
|
||||
layout(width: number, height: number): void;
|
||||
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
|
||||
if (dim1 instanceof Dimension) {
|
||||
this.updateLayout(dim1);
|
||||
layout(width: number, height: number): void {
|
||||
this.updateLayout(new Dimension(width, height));
|
||||
|
||||
return super.layout(dim1);
|
||||
}
|
||||
|
||||
const dimensions = new Dimension(dim1, dim2);
|
||||
this.updateLayout(dimensions);
|
||||
|
||||
super.layout(dimensions);
|
||||
super.layoutContents(width, height);
|
||||
}
|
||||
|
||||
toJSON(): object {
|
||||
@@ -608,7 +607,7 @@ class ShowItemInFolderAction extends Action {
|
||||
}
|
||||
|
||||
run(): Promise<void> {
|
||||
return this.windowsService.showItemInFolder(this.path);
|
||||
return this.windowsService.showItemInFolder(URI.file(this.path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,3 +630,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
registerSingleton(ITitleService, TitlebarPart);
|
||||
@@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views';
|
||||
import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views';
|
||||
import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -26,7 +26,7 @@ import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/t
|
||||
import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels';
|
||||
import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename } from 'vs/base/common/paths';
|
||||
import { dirname, basename } from 'vs/base/common/resources';
|
||||
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
|
||||
@@ -43,7 +43,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
export class CustomTreeViewPanel extends ViewletPanel {
|
||||
|
||||
@@ -58,7 +58,7 @@ export class CustomTreeViewPanel extends ViewletPanel {
|
||||
@IViewsService viewsService: IViewsService,
|
||||
) {
|
||||
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
|
||||
const { treeView } = (<ITreeViewDescriptor>ViewsRegistry.getView(options.id));
|
||||
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
|
||||
this.treeView = treeView;
|
||||
this.treeView.onDidChangeActions(() => this.updateActions(), this, this.disposables);
|
||||
this.disposables.push(toDisposable(() => this.treeView.setVisibility(false)));
|
||||
@@ -87,8 +87,8 @@ export class CustomTreeViewPanel extends ViewletPanel {
|
||||
return [...this.treeView.getSecondaryActions()];
|
||||
}
|
||||
|
||||
getActionItem(action: IAction): IActionItem {
|
||||
return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined;
|
||||
getActionItem(action: IAction): IActionItem | null {
|
||||
return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : null;
|
||||
}
|
||||
|
||||
getOptimalWidth(): number {
|
||||
@@ -164,9 +164,9 @@ class TitleMenus implements IDisposable {
|
||||
class Root implements ITreeItem {
|
||||
label = { label: 'root' };
|
||||
handle = '0';
|
||||
parentHandle = null;
|
||||
parentHandle: string | undefined = undefined;
|
||||
collapsibleState = TreeItemCollapsibleState.Expanded;
|
||||
children = undefined;
|
||||
children: ITreeItem[] | undefined = undefined;
|
||||
}
|
||||
|
||||
const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data.");
|
||||
@@ -191,21 +191,21 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
private menus: TitleMenus;
|
||||
|
||||
private markdownRenderer: MarkdownRenderer;
|
||||
private markdownResult: IMarkdownRenderResult;
|
||||
private markdownResult: IMarkdownRenderResult | null;
|
||||
|
||||
private _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
private readonly _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
readonly onDidExpandItem: Event<ITreeItem> = this._onDidExpandItem.event;
|
||||
|
||||
private _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
private readonly _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
readonly onDidCollapseItem: Event<ITreeItem> = this._onDidCollapseItem.event;
|
||||
|
||||
private _onDidChangeSelection: Emitter<ITreeItem[]> = this._register(new Emitter<ITreeItem[]>());
|
||||
readonly onDidChangeSelection: Event<ITreeItem[]> = this._onDidChangeSelection.event;
|
||||
|
||||
private _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
private readonly _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility: Event<boolean> = this._onDidChangeVisibility.event;
|
||||
|
||||
private _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
|
||||
private readonly _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeActions: Event<void> = this._onDidChangeActions.event;
|
||||
|
||||
constructor(
|
||||
@@ -235,7 +235,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
this.markdownResult.dispose();
|
||||
}
|
||||
}));
|
||||
this._register(ViewsRegistry.onDidChangeContainer(({ views, from, to }) => {
|
||||
this._register(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onDidChangeContainer(({ views, from, to }) => {
|
||||
if (from === this.viewContainer && views.some(v => v.id === this.id)) {
|
||||
this.viewContainer = to;
|
||||
}
|
||||
@@ -243,15 +243,15 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
this.create();
|
||||
}
|
||||
|
||||
private _dataProvider: ITreeViewDataProvider;
|
||||
get dataProvider(): ITreeViewDataProvider {
|
||||
private _dataProvider: ITreeViewDataProvider | null;
|
||||
get dataProvider(): ITreeViewDataProvider | null {
|
||||
return this._dataProvider;
|
||||
}
|
||||
|
||||
set dataProvider(dataProvider: ITreeViewDataProvider) {
|
||||
set dataProvider(dataProvider: ITreeViewDataProvider | null) {
|
||||
if (dataProvider) {
|
||||
this._dataProvider = new class implements ITreeViewDataProvider {
|
||||
getChildren(node?: ITreeItem): Promise<ITreeItem[]> {
|
||||
getChildren(node: ITreeItem): Promise<ITreeItem[]> {
|
||||
if (node && node.children) {
|
||||
return Promise.resolve(node.children);
|
||||
}
|
||||
@@ -305,7 +305,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
|
||||
getPrimaryActions(): IAction[] {
|
||||
if (this.showCollapseAllAction) {
|
||||
const collapseAllAction = new Action('vs.tree.collapse', localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve());
|
||||
const collapseAllAction = new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve());
|
||||
return [...this.menus.getTitleActions(), collapseAllAction];
|
||||
} else {
|
||||
return this.menus.getTitleActions();
|
||||
@@ -378,7 +378,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
}
|
||||
|
||||
private createTree() {
|
||||
const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined;
|
||||
const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : null;
|
||||
const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id));
|
||||
this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this));
|
||||
const dataSource = this.instantiationService.createInstance(TreeDataSource, this, <T>(task: Promise<T>) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task));
|
||||
@@ -462,7 +462,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
this.elementsToRefresh = [];
|
||||
}
|
||||
for (const element of elements) {
|
||||
element.children = null; // reset children
|
||||
element.children = undefined; // reset children
|
||||
}
|
||||
if (this.isVisible) {
|
||||
return this.doRefresh(elements);
|
||||
@@ -508,7 +508,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
|
||||
if (this.tree) {
|
||||
return this.tree.reveal(item);
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
private activate() {
|
||||
@@ -583,7 +583,7 @@ class TreeDataSource implements IDataSource {
|
||||
}
|
||||
|
||||
hasChildren(tree: ITree, node: ITreeItem): boolean {
|
||||
return this.treeView.dataProvider && node.collapsibleState !== TreeItemCollapsibleState.None;
|
||||
return !!this.treeView.dataProvider && node.collapsibleState !== TreeItemCollapsibleState.None;
|
||||
}
|
||||
|
||||
getChildren(tree: ITree, node: ITreeItem): Promise<any[]> {
|
||||
@@ -677,7 +677,7 @@ class TreeRenderer implements IRenderer {
|
||||
|
||||
renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void {
|
||||
const resource = node.resourceUri ? URI.revive(node.resourceUri) : null;
|
||||
const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource.path) } : undefined;
|
||||
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 ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : undefined;
|
||||
@@ -758,7 +758,7 @@ class Aligner extends Disposable {
|
||||
if (this.hasIcon(parent)) {
|
||||
return false;
|
||||
}
|
||||
return parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
|
||||
return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
|
||||
}
|
||||
|
||||
private hasIcon(node: ITreeItem): boolean {
|
||||
@@ -873,7 +873,7 @@ class TreeMenus extends Disposable implements IDisposable {
|
||||
return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary;
|
||||
}
|
||||
|
||||
private getActions(menuId: MenuId, context: { key: string, value: string }): { primary: IAction[]; secondary: IAction[]; } {
|
||||
private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } {
|
||||
const contextKeyService = this.contextKeyService.createScoped();
|
||||
contextKeyService.createKey('view', this.id);
|
||||
contextKeyService.createKey(context.key, context.value);
|
||||
|
||||
@@ -24,10 +24,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { IView } from 'vs/workbench/common/views';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export interface IPanelColors extends IColorMapping {
|
||||
dropBackground?: ColorIdentifier;
|
||||
@@ -127,7 +128,7 @@ export abstract class ViewletPanel extends Panel implements IView {
|
||||
orientation: ActionsOrientation.HORIZONTAL,
|
||||
actionItemProvider: action => this.getActionItem(action),
|
||||
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
|
||||
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) || undefined,
|
||||
getKeyBinding: action => withNullAsUndefined(this.keybindingService.lookupKeybinding(action.id)),
|
||||
actionRunner: this.actionRunner
|
||||
});
|
||||
|
||||
@@ -221,13 +222,13 @@ export class PanelViewlet extends Viewlet {
|
||||
id: string,
|
||||
private options: IViewsViewletOptions,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IContextMenuService protected contextMenuService: IContextMenuService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(id, configurationService, partService, telemetryService, themeService, storageService);
|
||||
super(id, configurationService, layoutService, telemetryService, themeService, storageService);
|
||||
}
|
||||
|
||||
create(parent: HTMLElement): void {
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
|
||||
import 'vs/css!./media/views';
|
||||
import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView, IViewDescriptorCollection } from 'vs/workbench/common/views';
|
||||
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';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { sortedDiff, firstIndex, move, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
@@ -21,6 +20,8 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { toggleClass, addClass } from 'vs/base/browser/dom';
|
||||
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event<IViewDescriptor[]> {
|
||||
return Event.chain(event)
|
||||
@@ -96,10 +97,11 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
const onRelevantViewsRegistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsRegistered);
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
|
||||
const onRelevantViewsRegistered = filterViewRegisterEvent(container, viewsRegistry.onViewsRegistered);
|
||||
this._register(onRelevantViewsRegistered(this.onViewsRegistered, this));
|
||||
|
||||
const onRelevantViewsMoved = filterViewMoveEvent(container, ViewsRegistry.onDidChangeContainer);
|
||||
const onRelevantViewsMoved = filterViewMoveEvent(container, viewsRegistry.onDidChangeContainer);
|
||||
this._register(onRelevantViewsMoved(({ added, removed }) => {
|
||||
if (isNonEmptyArray(added)) {
|
||||
this.onViewsRegistered(added);
|
||||
@@ -109,16 +111,16 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
}
|
||||
}));
|
||||
|
||||
const onRelevantViewsDeregistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsDeregistered);
|
||||
const onRelevantViewsDeregistered = filterViewRegisterEvent(container, viewsRegistry.onViewsDeregistered);
|
||||
this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this));
|
||||
|
||||
const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
|
||||
this._register(onRelevantContextChange(this.onContextChanged, this));
|
||||
|
||||
this.onViewsRegistered(ViewsRegistry.getViews(container));
|
||||
this.onViewsRegistered(viewsRegistry.getViews(container));
|
||||
}
|
||||
|
||||
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): any {
|
||||
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void {
|
||||
const added: IViewDescriptor[] = [];
|
||||
|
||||
for (const viewDescriptor of viewDescriptors) {
|
||||
@@ -145,7 +147,7 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
}
|
||||
}
|
||||
|
||||
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): any {
|
||||
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): void {
|
||||
const removed: IViewDescriptor[] = [];
|
||||
|
||||
for (const viewDescriptor of viewDescriptors) {
|
||||
@@ -174,7 +176,7 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
}
|
||||
}
|
||||
|
||||
private onContextChanged(event: IContextKeyChangeEvent): any {
|
||||
private onContextChanged(event: IContextKeyChangeEvent): void {
|
||||
const removed: IViewDescriptor[] = [];
|
||||
const added: IViewDescriptor[] = [];
|
||||
|
||||
@@ -203,7 +205,8 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
|
||||
}
|
||||
|
||||
export interface IViewState {
|
||||
visible: boolean;
|
||||
visibleGlobal: boolean;
|
||||
visibleWorkspace: boolean;
|
||||
collapsed: boolean;
|
||||
order?: number;
|
||||
size?: number;
|
||||
@@ -223,7 +226,7 @@ export class ContributableViewsModel extends Disposable {
|
||||
|
||||
readonly viewDescriptors: IViewDescriptor[] = [];
|
||||
get visibleViewDescriptors(): IViewDescriptor[] {
|
||||
return this.viewDescriptors.filter(v => this.viewStates.get(v.id)!.visible);
|
||||
return this.viewDescriptors.filter(v => this.isViewDescriptorVisible(v));
|
||||
}
|
||||
|
||||
private _onDidAdd = this._register(new Emitter<IAddedViewDescriptorRef[]>());
|
||||
@@ -250,27 +253,35 @@ export class ContributableViewsModel extends Disposable {
|
||||
}
|
||||
|
||||
isVisible(id: string): boolean {
|
||||
const state = this.viewStates.get(id);
|
||||
const viewDescriptor = this.viewDescriptors.filter(v => v.id === id)[0];
|
||||
|
||||
if (!state) {
|
||||
if (!viewDescriptor) {
|
||||
throw new Error(`Unknown view ${id}`);
|
||||
}
|
||||
|
||||
return state.visible;
|
||||
return this.isViewDescriptorVisible(viewDescriptor);
|
||||
}
|
||||
|
||||
setVisible(id: string, visible: boolean): void {
|
||||
setVisible(id: string, visible: boolean, size?: number): void {
|
||||
const { visibleIndex, viewDescriptor, state } = this.find(id);
|
||||
|
||||
if (!viewDescriptor.canToggleVisibility) {
|
||||
throw new Error(`Can't toggle this view's visibility`);
|
||||
}
|
||||
|
||||
if (state.visible === visible) {
|
||||
if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.visible = visible;
|
||||
if (viewDescriptor.workspace) {
|
||||
state.visibleWorkspace = visible;
|
||||
} else {
|
||||
state.visibleGlobal = visible;
|
||||
}
|
||||
|
||||
if (typeof size === 'number') {
|
||||
state.size = size;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: state.collapsed }]);
|
||||
@@ -329,6 +340,14 @@ export class ContributableViewsModel extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private isViewDescriptorVisible(viewDescriptor: IViewDescriptor): boolean {
|
||||
const viewState = this.viewStates.get(viewDescriptor.id);
|
||||
if (!viewState) {
|
||||
throw new Error(`Unknown view ${viewDescriptor.id}`);
|
||||
}
|
||||
return viewDescriptor.workspace ? viewState.visibleWorkspace : viewState.visibleGlobal;
|
||||
}
|
||||
|
||||
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];
|
||||
@@ -341,7 +360,7 @@ export class ContributableViewsModel extends Disposable {
|
||||
return { index: i, visibleIndex, viewDescriptor, state };
|
||||
}
|
||||
|
||||
if (state.visible) {
|
||||
if (viewDescriptor.workspace ? state.visibleWorkspace : state.visibleGlobal) {
|
||||
visibleIndex++;
|
||||
}
|
||||
}
|
||||
@@ -376,11 +395,16 @@ export class ContributableViewsModel extends Disposable {
|
||||
const viewState = this.viewStates.get(viewDescriptor.id);
|
||||
if (viewState) {
|
||||
// set defaults if not set
|
||||
viewState.visible = isUndefinedOrNull(viewState.visible) ? !viewDescriptor.hideByDefault : viewState.visible;
|
||||
if (viewDescriptor.workspace) {
|
||||
viewState.visibleWorkspace = isUndefinedOrNull(viewState.visibleWorkspace) ? !viewDescriptor.hideByDefault : viewState.visibleWorkspace;
|
||||
} else {
|
||||
viewState.visibleGlobal = isUndefinedOrNull(viewState.visibleGlobal) ? !viewDescriptor.hideByDefault : viewState.visibleGlobal;
|
||||
}
|
||||
viewState.collapsed = isUndefinedOrNull(viewState.collapsed) ? !!viewDescriptor.collapsed : viewState.collapsed;
|
||||
} else {
|
||||
this.viewStates.set(viewDescriptor.id, {
|
||||
visible: !viewDescriptor.hideByDefault,
|
||||
visibleGlobal: !viewDescriptor.hideByDefault,
|
||||
visibleWorkspace: !viewDescriptor.hideByDefault,
|
||||
collapsed: !!viewDescriptor.collapsed
|
||||
});
|
||||
}
|
||||
@@ -401,9 +425,8 @@ export class ContributableViewsModel extends Disposable {
|
||||
|
||||
for (let i = 0; i < splice.deleteCount; i++) {
|
||||
const viewDescriptor = this.viewDescriptors[splice.start + i];
|
||||
const { state } = this.find(viewDescriptor.id);
|
||||
|
||||
if (state.visible) {
|
||||
if (this.isViewDescriptorVisible(viewDescriptor)) {
|
||||
toRemove.push({ index: startIndex++, viewDescriptor });
|
||||
}
|
||||
}
|
||||
@@ -411,7 +434,7 @@ export class ContributableViewsModel extends Disposable {
|
||||
for (const viewDescriptor of splice.toInsert) {
|
||||
const state = this.viewStates.get(viewDescriptor.id)!;
|
||||
|
||||
if (state.visible) {
|
||||
if (this.isViewDescriptorVisible(viewDescriptor)) {
|
||||
toAdd.push({ index: startIndex++, viewDescriptor, size: state.size, collapsed: state.collapsed });
|
||||
}
|
||||
}
|
||||
@@ -435,24 +458,21 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
private readonly hiddenViewsStorageId: string;
|
||||
|
||||
private storageService: IStorageService;
|
||||
private contextService: IWorkspaceContextService;
|
||||
|
||||
constructor(
|
||||
container: ViewContainer,
|
||||
viewletStateStorageId: string,
|
||||
@IViewsService viewsService: IViewsService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||
) {
|
||||
const hiddenViewsStorageId = `${viewletStateStorageId}.hidden`;
|
||||
const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, hiddenViewsStorageId, storageService, contextService);
|
||||
const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, hiddenViewsStorageId, storageService);
|
||||
|
||||
super(container, viewsService, viewStates);
|
||||
|
||||
this.viewletStateStorageId = viewletStateStorageId;
|
||||
this.hiddenViewsStorageId = hiddenViewsStorageId;
|
||||
this.storageService = storageService;
|
||||
this.contextService = contextService;
|
||||
|
||||
this._register(this.onDidAdd(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
|
||||
this._register(this.onDidRemove(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
|
||||
@@ -479,27 +499,49 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
}
|
||||
|
||||
private saveVisibilityStates(viewDescriptors: IViewDescriptor[]): void {
|
||||
const storedViewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(this.hiddenViewsStorageId, this.storageService, this.contextService);
|
||||
const globalViews: IViewDescriptor[] = viewDescriptors.filter(v => !v.workspace);
|
||||
const workspaceViews: IViewDescriptor[] = viewDescriptors.filter(v => v.workspace);
|
||||
if (globalViews.length) {
|
||||
this.saveVisibilityStatesInScope(globalViews, StorageScope.GLOBAL);
|
||||
}
|
||||
if (workspaceViews.length) {
|
||||
this.saveVisibilityStatesInScope(workspaceViews, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
private saveVisibilityStatesInScope(viewDescriptors: IViewDescriptor[], scope: StorageScope): void {
|
||||
const storedViewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(this.hiddenViewsStorageId, this.storageService, scope);
|
||||
for (const viewDescriptor of viewDescriptors) {
|
||||
if (viewDescriptor.canToggleVisibility) {
|
||||
const viewState = this.viewStates.get(viewDescriptor.id);
|
||||
storedViewsVisibilityStates.set(viewDescriptor.id, { id: viewDescriptor.id, isHidden: viewState ? !viewState.visible : false });
|
||||
storedViewsVisibilityStates.set(viewDescriptor.id, { id: viewDescriptor.id, isHidden: viewState ? (scope === StorageScope.GLOBAL ? !viewState.visibleGlobal : !viewState.visibleWorkspace) : false });
|
||||
}
|
||||
}
|
||||
this.storageService.store(this.hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
|
||||
this.storageService.store(this.hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), scope);
|
||||
}
|
||||
|
||||
private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, IViewState> {
|
||||
private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService): Map<string, IViewState> {
|
||||
const viewStates = new Map<string, IViewState>();
|
||||
const storedViewsStates = JSON.parse(storageService.get(viewletStateStorageId, StorageScope.WORKSPACE, '{}'));
|
||||
const viewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(hiddenViewsStorageId, storageService, contextService);
|
||||
for (const { id, isHidden } of values(viewsVisibilityStates)) {
|
||||
const globalVisibilityStates = this.loadViewsVisibilityState(hiddenViewsStorageId, storageService, StorageScope.GLOBAL);
|
||||
const workspaceVisibilityStates = this.loadViewsVisibilityState(hiddenViewsStorageId, storageService, StorageScope.WORKSPACE);
|
||||
|
||||
for (const { id, isHidden } of values(globalVisibilityStates)) {
|
||||
const viewState = storedViewsStates[id];
|
||||
if (viewState) {
|
||||
viewStates.set(id, <IViewState>{ ...viewState, ...{ visible: !isHidden } });
|
||||
viewStates.set(id, <IViewState>{ ...viewState, ...{ visibleGlobal: !isHidden } });
|
||||
} else {
|
||||
// New workspace
|
||||
viewStates.set(id, <IViewState>{ ...{ visible: !isHidden } });
|
||||
viewStates.set(id, <IViewState>{ ...{ visibleGlobal: !isHidden } });
|
||||
}
|
||||
}
|
||||
for (const { id, isHidden } of values(workspaceVisibilityStates)) {
|
||||
const viewState = storedViewsStates[id];
|
||||
if (viewState) {
|
||||
viewStates.set(id, <IViewState>{ ...viewState, ...{ visibleWorkspace: !isHidden } });
|
||||
} else {
|
||||
// New workspace
|
||||
viewStates.set(id, <IViewState>{ ...{ visibleWorkspace: !isHidden } });
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(storedViewsStates)) {
|
||||
@@ -510,8 +552,8 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
return viewStates;
|
||||
}
|
||||
|
||||
private static loadViewsVisibilityState(hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, { id: string, isHidden: boolean }> {
|
||||
const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, StorageScope.GLOBAL, '[]'));
|
||||
private static loadViewsVisibilityState(hiddenViewsStorageId: string, storageService: IStorageService, scope: StorageScope): Map<string, { id: string, isHidden: boolean }> {
|
||||
const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, scope, '[]'));
|
||||
let hasDuplicates = false;
|
||||
const storedViewsVisibilityStates = storedVisibilityStates.reduce((result, storedState) => {
|
||||
if (typeof storedState === 'string' /* migration */) {
|
||||
@@ -525,7 +567,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
}, new Map<string, { id: string, isHidden: boolean }>());
|
||||
|
||||
if (hasDuplicates) {
|
||||
storageService.store(hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
|
||||
storageService.store(hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), scope);
|
||||
}
|
||||
|
||||
return storedViewsVisibilityStates;
|
||||
@@ -534,7 +576,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
|
||||
|
||||
export class ViewsService extends Disposable implements IViewsService {
|
||||
|
||||
_serviceBrand: any;
|
||||
_serviceBrand: ServiceIdentifier<any>;
|
||||
|
||||
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>;
|
||||
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
|
||||
@@ -550,14 +592,15 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
|
||||
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
|
||||
|
||||
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
|
||||
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
|
||||
viewContainersRegistry.all.forEach(viewContainer => {
|
||||
this.onDidRegisterViews(viewContainer, ViewsRegistry.getViews(viewContainer));
|
||||
this.onDidRegisterViews(viewContainer, viewsRegistry.getViews(viewContainer));
|
||||
this.onDidRegisterViewContainer(viewContainer);
|
||||
});
|
||||
this._register(ViewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views)));
|
||||
this._register(ViewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views)));
|
||||
this._register(ViewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); }));
|
||||
this._register(viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views)));
|
||||
this._register(viewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views)));
|
||||
this._register(viewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); }));
|
||||
this._register(toDisposable(() => {
|
||||
this.viewDisposable.forEach(disposable => disposable.dispose());
|
||||
this.viewDisposable.clear();
|
||||
@@ -576,7 +619,7 @@ export class ViewsService extends Disposable implements IViewsService {
|
||||
}
|
||||
|
||||
openView(id: string, focus: boolean): Promise<IView | null> {
|
||||
const viewContainer = ViewsRegistry.getViewContainer(id);
|
||||
const viewContainer = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).getViewContainer(id);
|
||||
if (viewContainer) {
|
||||
const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id);
|
||||
if (viewletDescriptor) {
|
||||
@@ -682,4 +725,6 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement,
|
||||
|
||||
onDidChangeFileIconTheme(themeService.getFileIconTheme());
|
||||
return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IViewsService, ViewsService);
|
||||
@@ -25,7 +25,7 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
|
||||
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IPartService } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -43,7 +43,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
|
||||
private readonly visibleViewsCountFromCache: number;
|
||||
private readonly visibleViewsStorageId: string;
|
||||
private readonly viewsModel: PersistentContributableViewsModel;
|
||||
protected readonly viewsModel: PersistentContributableViewsModel;
|
||||
private viewDisposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -51,7 +51,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
viewletStateStorageId: string,
|
||||
showHeaderInTitleWhenSingleView: boolean,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IPartService partService: IPartService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IStorageService protected storageService: IStorageService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@@ -60,14 +60,14 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
@IExtensionService protected extensionService: IExtensionService,
|
||||
@IWorkspaceContextService protected contextService: IWorkspaceContextService
|
||||
) {
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, partService, contextMenuService, telemetryService, themeService, storageService);
|
||||
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService);
|
||||
|
||||
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).get(id);
|
||||
this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId));
|
||||
this.viewletState = this.getMemento(StorageScope.WORKSPACE);
|
||||
|
||||
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
|
||||
this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1);
|
||||
this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1);
|
||||
this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables)));
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
}
|
||||
|
||||
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel {
|
||||
return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel;
|
||||
return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPanel;
|
||||
}
|
||||
|
||||
protected getView(id: string): ViewletPanel {
|
||||
@@ -197,7 +197,6 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
|
||||
viewletState: this.viewletState
|
||||
});
|
||||
panel.render();
|
||||
panel.setVisible(true);
|
||||
const contextMenuDisposable = DOM.addDisposableListener(panel.draggableElement, 'contextmenu', e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
@@ -50,14 +50,14 @@ export class QuickOpenHandler {
|
||||
/**
|
||||
* The ARIA label to apply when this quick open handler is active in quick open.
|
||||
*/
|
||||
getAriaLabel() {
|
||||
getAriaLabel(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra CSS class name to add to the quick open widget to do custom styling of entries.
|
||||
*/
|
||||
getClass(): string {
|
||||
getClass(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export class QuickOpenHandler {
|
||||
/**
|
||||
* Allows to return a label that will be placed to the side of the results from this handler or null if none.
|
||||
*/
|
||||
getGroupLabel(): string {
|
||||
getGroupLabel(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -129,16 +129,16 @@ export interface QuickOpenHandlerHelpEntry {
|
||||
export class QuickOpenHandlerDescriptor {
|
||||
prefix: string;
|
||||
description: string;
|
||||
contextKey: string;
|
||||
contextKey?: string;
|
||||
helpEntries: QuickOpenHandlerHelpEntry[];
|
||||
instantProgress: boolean;
|
||||
|
||||
private id: string;
|
||||
private ctor: IConstructorSignature0<QuickOpenHandler>;
|
||||
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string, description: string, instantProgress?: boolean);
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean);
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string, param: any, instantProgress: boolean = false) {
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean);
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean);
|
||||
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, param: any, instantProgress: boolean = false) {
|
||||
this.ctor = ctor;
|
||||
this.id = id;
|
||||
this.prefix = prefix;
|
||||
@@ -185,7 +185,7 @@ export interface IQuickOpenRegistry {
|
||||
/**
|
||||
* Get a specific quick open handler for a given prefix.
|
||||
*/
|
||||
getQuickOpenHandler(prefix: string): QuickOpenHandlerDescriptor;
|
||||
getQuickOpenHandler(prefix: string): QuickOpenHandlerDescriptor | null;
|
||||
|
||||
/**
|
||||
* Returns the default quick open handler.
|
||||
@@ -213,8 +213,8 @@ class QuickOpenRegistry implements IQuickOpenRegistry {
|
||||
return this.handlers.slice(0);
|
||||
}
|
||||
|
||||
getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor {
|
||||
return text ? arrays.first(this.handlers, h => strings.startsWith(text, h.prefix), null) : null;
|
||||
getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor | null {
|
||||
return text ? arrays.first<QuickOpenHandlerDescriptor>(this.handlers, h => strings.startsWith(text, h.prefix), null) : null;
|
||||
}
|
||||
|
||||
getDefaultQuickOpenHandler(): QuickOpenHandlerDescriptor {
|
||||
@@ -229,12 +229,12 @@ export interface IEditorQuickOpenEntry {
|
||||
/**
|
||||
* The editor input used for this entry when opening.
|
||||
*/
|
||||
getInput(): IResourceInput | IEditorInput;
|
||||
getInput(): IResourceInput | IEditorInput | null;
|
||||
|
||||
/**
|
||||
* The editor options used for this entry when opening.
|
||||
*/
|
||||
getOptions(): IEditorOptions;
|
||||
getOptions(): IEditorOptions | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,11 +250,11 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
|
||||
return this._editorService;
|
||||
}
|
||||
|
||||
getInput(): IResourceInput | IEditorInput {
|
||||
getInput(): IResourceInput | IEditorInput | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
getOptions(): IEditorOptions {
|
||||
getOptions(): IEditorOptions | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
|
||||
if (mode === Mode.OPEN || mode === Mode.OPEN_IN_BACKGROUND) {
|
||||
const sideBySide = context.keymods.ctrlCmd;
|
||||
|
||||
let openOptions: IEditorOptions;
|
||||
let openOptions: IEditorOptions | undefined;
|
||||
if (mode === Mode.OPEN_IN_BACKGROUND) {
|
||||
openOptions = { pinned: true, preserveFocus: true };
|
||||
} else if (context.keymods.alt) {
|
||||
@@ -280,7 +280,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
|
||||
opts = EditorOptions.create(openOptions);
|
||||
}
|
||||
|
||||
this.editorService.openEditor(input, opts, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
this.editorService.openEditor(input, opts || undefined, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
} else {
|
||||
const resourceInput = <IResourceInput>input;
|
||||
|
||||
@@ -301,11 +301,11 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick
|
||||
*/
|
||||
export class EditorQuickOpenEntryGroup extends QuickOpenEntryGroup implements IEditorQuickOpenEntry {
|
||||
|
||||
getInput(): IEditorInput | IResourceInput {
|
||||
getInput(): IEditorInput | IResourceInput | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
getOptions(): IEditorOptions {
|
||||
getOptions(): IEditorOptions | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
137
src/vs/workbench/browser/style.ts
Normal file
137
src/vs/workbench/browser/style.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/style';
|
||||
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
|
||||
import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
|
||||
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
|
||||
|
||||
// Foreground
|
||||
const windowForeground = theme.getColor(foreground);
|
||||
if (windowForeground) {
|
||||
collector.addRule(`.monaco-workbench { color: ${windowForeground}; }`);
|
||||
}
|
||||
|
||||
// Selection
|
||||
const windowSelectionBackground = theme.getColor(selectionBackground);
|
||||
if (windowSelectionBackground) {
|
||||
collector.addRule(`.monaco-workbench ::selection { background-color: ${windowSelectionBackground}; }`);
|
||||
}
|
||||
|
||||
// Input placeholder
|
||||
const placeholderForeground = theme.getColor(inputPlaceholderForeground);
|
||||
if (placeholderForeground) {
|
||||
collector.addRule(`.monaco-workbench input::-webkit-input-placeholder { color: ${placeholderForeground}; }`);
|
||||
collector.addRule(`.monaco-workbench textarea::-webkit-input-placeholder { color: ${placeholderForeground}; }`);
|
||||
}
|
||||
|
||||
// List highlight
|
||||
const listHighlightForegroundColor = theme.getColor(listHighlightForeground);
|
||||
if (listHighlightForegroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .monaco-tree .monaco-tree-row .monaco-highlighted-label .highlight,
|
||||
.monaco-workbench .monaco-list .monaco-list-row .monaco-highlighted-label .highlight {
|
||||
color: ${listHighlightForegroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// We need to set the workbench background color so that on Windows we get subpixel-antialiasing.
|
||||
const workbenchBackground = WORKBENCH_BACKGROUND(theme);
|
||||
collector.addRule(`.monaco-workbench { background-color: ${workbenchBackground}; }`);
|
||||
|
||||
// Scrollbars
|
||||
const scrollbarShadowColor = theme.getColor(scrollbarShadow);
|
||||
if (scrollbarShadowColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .monaco-scrollable-element > .shadow.top {
|
||||
box-shadow: ${scrollbarShadowColor} 0 6px 6px -6px inset;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-scrollable-element > .shadow.left {
|
||||
box-shadow: ${scrollbarShadowColor} 6px 0 6px -6px inset;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-scrollable-element > .shadow.top.left {
|
||||
box-shadow: ${scrollbarShadowColor} 6px 6px 6px -6px inset;
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const scrollbarSliderBackgroundColor = theme.getColor(scrollbarSliderBackground);
|
||||
if (scrollbarSliderBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .monaco-scrollable-element > .scrollbar > .slider {
|
||||
background: ${scrollbarSliderBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const scrollbarSliderHoverBackgroundColor = theme.getColor(scrollbarSliderHoverBackground);
|
||||
if (scrollbarSliderHoverBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .monaco-scrollable-element > .scrollbar > .slider:hover {
|
||||
background: ${scrollbarSliderHoverBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const scrollbarSliderActiveBackgroundColor = theme.getColor(scrollbarSliderActiveBackground);
|
||||
if (scrollbarSliderActiveBackgroundColor) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .monaco-scrollable-element > .scrollbar > .slider.active {
|
||||
background: ${scrollbarSliderActiveBackgroundColor};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Focus outline
|
||||
const focusOutline = theme.getColor(focusBorder);
|
||||
if (focusOutline) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench [tabindex="0"]:focus,
|
||||
.monaco-workbench .synthetic-focus,
|
||||
.monaco-workbench select:focus,
|
||||
.monaco-workbench .monaco-tree.focused.no-focused-item:focus:before,
|
||||
.monaco-workbench .monaco-list:not(.element-focused):focus:before,
|
||||
.monaco-workbench input[type="button"]:focus,
|
||||
.monaco-workbench input[type="text"]:focus,
|
||||
.monaco-workbench button:focus,
|
||||
.monaco-workbench textarea:focus,
|
||||
.monaco-workbench input[type="search"]:focus,
|
||||
.monaco-workbench input[type="checkbox"]:focus {
|
||||
outline-color: ${focusOutline};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// High Contrast theme overwrites for outline
|
||||
if (theme.type === HIGH_CONTRAST) {
|
||||
collector.addRule(`
|
||||
.hc-black [tabindex="0"]:focus,
|
||||
.hc-black .synthetic-focus,
|
||||
.hc-black select:focus,
|
||||
.hc-black input[type="button"]:focus,
|
||||
.hc-black input[type="text"]:focus,
|
||||
.hc-black textarea:focus,
|
||||
.hc-black input[type="checkbox"]:focus {
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
}
|
||||
|
||||
.hc-black .monaco-tree.focused.no-focused-item:focus:before {
|
||||
outline-width: 1px;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.hc-black .synthetic-focus input {
|
||||
background: transparent; /* Search input focus fix when in high contrast */
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
@@ -7,26 +7,26 @@ import * as nls from 'vs/nls';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Action, IAction } from 'vs/base/common/actions';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { AbstractTree } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
|
||||
export abstract class Viewlet extends Composite implements IViewlet {
|
||||
|
||||
constructor(id: string,
|
||||
protected configurationService: IConfigurationService,
|
||||
private partService: IPartService,
|
||||
private layoutService: IWorkbenchLayoutService,
|
||||
telemetryService: ITelemetryService,
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService
|
||||
@@ -39,13 +39,13 @@ export abstract class Viewlet extends Composite implements IViewlet {
|
||||
}
|
||||
|
||||
getContextMenuActions(): IAction[] {
|
||||
const toggleSidebarPositionAction = new ToggleSidebarPositionAction(ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.partService), this.partService, this.configurationService);
|
||||
const toggleSidebarPositionAction = new ToggleSidebarPositionAction(ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService), this.layoutService, this.configurationService);
|
||||
return [toggleSidebarPositionAction,
|
||||
<IAction>{
|
||||
id: ToggleSidebarVisibilityAction.ID,
|
||||
label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"),
|
||||
enabled: true,
|
||||
run: () => this.partService.setSideBarHidden(true)
|
||||
run: () => this.layoutService.setSideBarHidden(true)
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -130,19 +130,17 @@ Registry.add(Extensions.Viewlets, new ViewletRegistry());
|
||||
* A reusable action to show a viewlet with a specific id.
|
||||
*/
|
||||
export class ShowViewletAction extends Action {
|
||||
private viewletId: string;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
name: string,
|
||||
viewletId: string,
|
||||
private readonly viewletId: string,
|
||||
@IViewletService protected viewletService: IViewletService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
@IPartService private readonly partService: IPartService
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, name);
|
||||
|
||||
this.viewletId = viewletId;
|
||||
this.enabled = !!this.viewletService && !!this.editorGroupService;
|
||||
}
|
||||
|
||||
@@ -169,35 +167,15 @@ export class ShowViewletAction extends Action {
|
||||
const activeViewlet = this.viewletService.getActiveViewlet();
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
return !!(activeViewlet && activeElement && DOM.isAncestor(activeElement, this.partService.getContainer(Parts.SIDEBAR_PART)));
|
||||
return !!(activeViewlet && activeElement && DOM.isAncestor(activeElement, this.layoutService.getContainer(Parts.SIDEBAR_PART)));
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse All action
|
||||
export class CollapseAction extends Action {
|
||||
|
||||
constructor(viewer: ITree, enabled: boolean, clazz: string) {
|
||||
super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, (context: any) => {
|
||||
if (viewer.getHighlight()) {
|
||||
return Promise.resolve(null); // Global action disabled if user is in edit mode from another action
|
||||
}
|
||||
|
||||
viewer.collapseAll();
|
||||
viewer.clearSelection();
|
||||
viewer.clearFocus();
|
||||
viewer.domFocus();
|
||||
viewer.focusFirst();
|
||||
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse All action for the new tree
|
||||
export class CollapseAction2 extends Action {
|
||||
constructor(tree: AsyncDataTree<any, any, any>, enabled: boolean, clazz: string) {
|
||||
constructor(tree: AsyncDataTree<any, any, any> | AbstractTree<any, any, any>, enabled: boolean, clazz?: string) {
|
||||
super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, () => {
|
||||
tree.collapseAll();
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,280 +8,316 @@ import * as nls from 'vs/nls';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
// Configuration
|
||||
(function registerConfiguration(): void {
|
||||
const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
|
||||
// Configuration: Workbench
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'workbench',
|
||||
'order': 7,
|
||||
'title': nls.localize('workbenchConfigurationTitle', "Workbench"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'workbench.editor.showTabs': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.highlightModifiedTabs': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.labelFormat': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'short', 'medium', 'long'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."),
|
||||
nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."),
|
||||
nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."),
|
||||
nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.")
|
||||
],
|
||||
'default': 'default',
|
||||
'description': nls.localize({
|
||||
comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'],
|
||||
key: 'tabDescription'
|
||||
}, "Controls the format of the label for an editor."),
|
||||
},
|
||||
'workbench.editor.tabCloseButton': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right', 'off'],
|
||||
'default': 'right',
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'.")
|
||||
},
|
||||
'workbench.editor.tabSizing': {
|
||||
'type': 'string',
|
||||
'enum': ['fit', 'shrink'],
|
||||
'default': 'fit',
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."),
|
||||
nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.")
|
||||
],
|
||||
'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.focusRecentEditorAfterClose': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.showIcons': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.enablePreview': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.enablePreviewFromQuickOpen': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.closeOnFileDelete': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.openPositioning': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right', 'first', 'last'],
|
||||
'default': 'right',
|
||||
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.")
|
||||
},
|
||||
'workbench.editor.openSideBySideDirection': {
|
||||
'type': 'string',
|
||||
'enum': ['right', 'down'],
|
||||
'default': 'right',
|
||||
'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
|
||||
},
|
||||
'workbench.editor.closeEmptyGroups': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.revealIfOpen': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.swipeToNavigate': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally."),
|
||||
'default': false,
|
||||
'included': isMacintosh
|
||||
},
|
||||
'workbench.editor.restoreViewState': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening files after they have been closed."),
|
||||
'default': true,
|
||||
},
|
||||
'workbench.editor.centeredLayoutAutoResize': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.")
|
||||
},
|
||||
'workbench.commandPalette.history': {
|
||||
'type': 'number',
|
||||
'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."),
|
||||
'default': 50
|
||||
},
|
||||
'workbench.commandPalette.preserveInput': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.quickOpen.closeOnFocusLost': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.quickOpen.preserveInput': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.openDefaultSettings': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.useSplitJSON': {
|
||||
'type': 'boolean',
|
||||
'markdownDescription': nls.localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.openDefaultKeybindings': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.sideBar.location': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right'],
|
||||
'default': 'left',
|
||||
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.")
|
||||
},
|
||||
'workbench.panel.defaultLocation': {
|
||||
'type': 'string',
|
||||
'enum': ['bottom', 'right'],
|
||||
'default': 'bottom',
|
||||
'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom or on the right of the workbench.")
|
||||
},
|
||||
'workbench.statusBar.visible': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.")
|
||||
},
|
||||
'workbench.activityBar.visible': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.")
|
||||
},
|
||||
'workbench.view.alwaysShowHeaderActions': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.")
|
||||
},
|
||||
'workbench.fontAliasing': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'antialiased', 'none', 'auto'],
|
||||
'default': 'default',
|
||||
'description':
|
||||
nls.localize('fontAliasing', "Controls font aliasing method in the workbench."),
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."),
|
||||
nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."),
|
||||
nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."),
|
||||
nls.localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.")
|
||||
],
|
||||
'included': isMacintosh
|
||||
},
|
||||
'workbench.settings.enableNaturalLanguageSearch': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."),
|
||||
'default': true,
|
||||
'scope': ConfigurationScope.WINDOW,
|
||||
'tags': ['usesOnlineServices']
|
||||
},
|
||||
'workbench.settings.settingsSearchTocBehavior': {
|
||||
'type': 'string',
|
||||
'enum': ['hide', 'filter'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."),
|
||||
nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."),
|
||||
],
|
||||
'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."),
|
||||
'default': 'filter',
|
||||
'scope': ConfigurationScope.WINDOW
|
||||
},
|
||||
'workbench.settings.editor': {
|
||||
'type': 'string',
|
||||
'enum': ['ui', 'json'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('settings.editor.ui', "Use the settings UI editor."),
|
||||
nls.localize('settings.editor.json', "Use the JSON file editor."),
|
||||
],
|
||||
'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."),
|
||||
'default': 'ui',
|
||||
'scope': ConfigurationScope.WINDOW
|
||||
},
|
||||
'workbench.enableExperiments': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."),
|
||||
'default': true,
|
||||
'tags': ['usesOnlineServices']
|
||||
},
|
||||
'workbench.useExperimentalGridLayout': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."),
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION
|
||||
// Workbench
|
||||
registry.registerConfiguration({
|
||||
'id': 'workbench',
|
||||
'order': 7,
|
||||
'title': nls.localize('workbenchConfigurationTitle', "Workbench"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'workbench.editor.showTabs': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.highlightModifiedTabs': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.labelFormat': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'short', 'medium', 'long'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.labelFormat.default', "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active."),
|
||||
nls.localize('workbench.editor.labelFormat.short', "Show the name of the file followed by its directory name."),
|
||||
nls.localize('workbench.editor.labelFormat.medium', "Show the name of the file followed by its path relative to the workspace folder."),
|
||||
nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by its absolute path.")
|
||||
],
|
||||
'default': 'default',
|
||||
'description': nls.localize({
|
||||
comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'],
|
||||
key: 'tabDescription'
|
||||
}, "Controls the format of the label for an editor."),
|
||||
},
|
||||
'workbench.editor.tabCloseButton': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right', 'off'],
|
||||
'default': 'right',
|
||||
'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'.")
|
||||
},
|
||||
'workbench.editor.tabSizing': {
|
||||
'type': 'string',
|
||||
'enum': ['fit', 'shrink'],
|
||||
'default': 'fit',
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."),
|
||||
nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.")
|
||||
],
|
||||
'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.focusRecentEditorAfterClose': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.showIcons': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.enablePreview': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing) and show up with an italic font style."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.enablePreviewFromQuickOpen': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are pinned (e.g. via double click or editing)."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.closeOnFileDelete': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.openPositioning': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right', 'first', 'last'],
|
||||
'default': 'right',
|
||||
'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.")
|
||||
},
|
||||
'workbench.editor.openSideBySideDirection': {
|
||||
'type': 'string',
|
||||
'enum': ['right', 'down'],
|
||||
'default': 'right',
|
||||
'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.")
|
||||
},
|
||||
'workbench.editor.closeEmptyGroups': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeEmptyGroups', "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.editor.revealIfOpen': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.editor.swipeToNavigate': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('swipeToNavigate', "Navigate between open files using three-finger swipe horizontally."),
|
||||
'default': false,
|
||||
'included': isMacintosh
|
||||
},
|
||||
'workbench.editor.restoreViewState': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('restoreViewState', "Restores the last view state (e.g. scroll position) when re-opening files after they have been closed."),
|
||||
'default': true,
|
||||
},
|
||||
'workbench.editor.centeredLayoutAutoResize': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('centeredLayoutAutoResize', "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.")
|
||||
},
|
||||
'workbench.commandPalette.history': {
|
||||
'type': 'number',
|
||||
'description': nls.localize('commandHistory', "Controls the number of recently used commands to keep in history for the command palette. Set to 0 to disable command history."),
|
||||
'default': 50
|
||||
},
|
||||
'workbench.commandPalette.preserveInput': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.quickOpen.closeOnFocusLost': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."),
|
||||
'default': true
|
||||
},
|
||||
'workbench.quickOpen.preserveInput': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.quickOpen.preserveInput', "Controls whether the last typed input to Quick Open should be restored when opening it the next time."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.openDefaultSettings': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.useSplitJSON': {
|
||||
'type': 'boolean',
|
||||
'markdownDescription': nls.localize('useSplitJSON', "Controls whether to use the split JSON editor when editing settings as JSON."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.settings.openDefaultKeybindings': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."),
|
||||
'default': false
|
||||
},
|
||||
'workbench.sideBar.location': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right'],
|
||||
'default': 'left',
|
||||
'description': nls.localize('sideBarLocation', "Controls the location of the sidebar. It can either show on the left or right of the workbench.")
|
||||
},
|
||||
'workbench.panel.defaultLocation': {
|
||||
'type': 'string',
|
||||
'enum': ['bottom', 'right'],
|
||||
'default': 'bottom',
|
||||
'description': nls.localize('panelDefaultLocation', "Controls the default location of the panel (terminal, debug console, output, problems). It can either show at the bottom or on the right of the workbench.")
|
||||
},
|
||||
'workbench.statusBar.visible': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('statusBarVisibility', "Controls the visibility of the status bar at the bottom of the workbench.")
|
||||
},
|
||||
'workbench.activityBar.visible': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('activityBarVisibility', "Controls the visibility of the activity bar in the workbench.")
|
||||
},
|
||||
'workbench.view.alwaysShowHeaderActions': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': nls.localize('viewVisibility', "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.")
|
||||
},
|
||||
'workbench.fontAliasing': {
|
||||
'type': 'string',
|
||||
'enum': ['default', 'antialiased', 'none', 'auto'],
|
||||
'default': 'default',
|
||||
'description':
|
||||
nls.localize('fontAliasing', "Controls font aliasing method in the workbench."),
|
||||
'enumDescriptions': [
|
||||
nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."),
|
||||
nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."),
|
||||
nls.localize('workbench.fontAliasing.none', "Disables font smoothing. Text will show with jagged sharp edges."),
|
||||
nls.localize('workbench.fontAliasing.auto', "Applies `default` or `antialiased` automatically based on the DPI of displays.")
|
||||
],
|
||||
'included': isMacintosh
|
||||
},
|
||||
'workbench.settings.enableNaturalLanguageSearch': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service."),
|
||||
'default': true,
|
||||
'scope': ConfigurationScope.WINDOW,
|
||||
'tags': ['usesOnlineServices']
|
||||
},
|
||||
'workbench.settings.settingsSearchTocBehavior': {
|
||||
'type': 'string',
|
||||
'enum': ['hide', 'filter'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('settingsSearchTocBehavior.hide', "Hide the Table of Contents while searching."),
|
||||
nls.localize('settingsSearchTocBehavior.filter', "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category."),
|
||||
],
|
||||
'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."),
|
||||
'default': 'filter',
|
||||
'scope': ConfigurationScope.WINDOW
|
||||
},
|
||||
'workbench.settings.editor': {
|
||||
'type': 'string',
|
||||
'enum': ['ui', 'json'],
|
||||
'enumDescriptions': [
|
||||
nls.localize('settings.editor.ui', "Use the settings UI editor."),
|
||||
nls.localize('settings.editor.json', "Use the JSON file editor."),
|
||||
],
|
||||
'description': nls.localize('settings.editor.desc', "Determines which settings editor to use by default."),
|
||||
'default': 'ui',
|
||||
'scope': ConfigurationScope.WINDOW
|
||||
},
|
||||
'workbench.enableExperiments': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."),
|
||||
'default': true,
|
||||
'tags': ['usesOnlineServices']
|
||||
},
|
||||
'workbench.useExperimentalGridLayout': {
|
||||
'type': 'boolean',
|
||||
'description': nls.localize('workbench.useExperimentalGridLayout', "Enables the grid layout for the workbench. This setting may enable additional layout options for workbench components."),
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Configuration: Zen Mode
|
||||
configurationRegistry.registerConfiguration({
|
||||
'id': 'zenMode',
|
||||
'order': 9,
|
||||
'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'zenMode.fullScreen': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.")
|
||||
},
|
||||
'zenMode.centerLayout': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.")
|
||||
},
|
||||
'zenMode.hideTabs': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.")
|
||||
},
|
||||
'zenMode.hideStatusBar': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.")
|
||||
},
|
||||
'zenMode.hideActivityBar': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar at the left of the workbench.")
|
||||
},
|
||||
'zenMode.hideLineNumbers': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.")
|
||||
},
|
||||
'zenMode.restore': {
|
||||
'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.")
|
||||
// Window
|
||||
|
||||
let windowTitleDescription = nls.localize('windowTitle', "Controls the window title based on the active editor. Variables are substituted based on the context:");
|
||||
windowTitleDescription += [
|
||||
nls.localize('activeEditorShort', "`\${activeEditorShort}`: the file name (e.g. myFile.txt)."),
|
||||
nls.localize('activeEditorMedium', "`\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)."),
|
||||
nls.localize('activeEditorLong', "`\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt)."),
|
||||
nls.localize('activeFolderShort', "`\${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder)."),
|
||||
nls.localize('activeFolderMedium', "`\${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder)."),
|
||||
nls.localize('activeFolderLong', "`\${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder)."),
|
||||
nls.localize('folderName', "`\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder)."),
|
||||
nls.localize('folderPath', "`\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)."),
|
||||
nls.localize('rootName', "`\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace)."),
|
||||
nls.localize('rootPath', "`\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace)."),
|
||||
nls.localize('appName', "`\${appName}`: e.g. VS Code."),
|
||||
nls.localize('dirty', "`\${dirty}`: a dirty indicator if the active editor is dirty."),
|
||||
nls.localize('separator', "`\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.")
|
||||
].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations
|
||||
|
||||
registry.registerConfiguration({
|
||||
'id': 'window',
|
||||
'order': 8,
|
||||
'title': nls.localize('windowConfigurationTitle', "Window"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'window.title': {
|
||||
'type': 'string',
|
||||
'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}',
|
||||
'markdownDescription': windowTitleDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Zen Mode
|
||||
registry.registerConfiguration({
|
||||
'id': 'zenMode',
|
||||
'order': 9,
|
||||
'title': nls.localize('zenModeConfigurationTitle', "Zen Mode"),
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'zenMode.fullScreen': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.")
|
||||
},
|
||||
'zenMode.centerLayout': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.")
|
||||
},
|
||||
'zenMode.hideTabs': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.")
|
||||
},
|
||||
'zenMode.hideStatusBar': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.")
|
||||
},
|
||||
'zenMode.hideActivityBar': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar at the left of the workbench.")
|
||||
},
|
||||
'zenMode.hideLineNumbers': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': nls.localize('zenMode.hideLineNumbers', "Controls whether turning on Zen Mode also hides the editor line numbers.")
|
||||
},
|
||||
'zenMode.restore': {
|
||||
'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.")
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
564
src/vs/workbench/browser/workbench.ts
Normal file
564
src/vs/workbench/browser/workbench.ts
Normal file
@@ -0,0 +1,564 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/workbench/browser/style';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { setFileNameComparer } from 'vs/base/common/comparers';
|
||||
import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event';
|
||||
import { addClasses, addClass, removeClasses } from 'vs/base/browser/dom';
|
||||
import { runWhenIdle, IdleValue } from 'vs/base/common/async';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
|
||||
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
|
||||
import { getServices } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { LifecyclePhase, ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { NotificationService } from 'vs/workbench/services/notification/common/notificationService';
|
||||
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
|
||||
import { NotificationsAlerts } from 'vs/workbench/browser/parts/notifications/notificationsAlerts';
|
||||
import { NotificationsStatus } from 'vs/workbench/browser/parts/notifications/notificationsStatus';
|
||||
import { registerNotificationCommands } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
|
||||
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
|
||||
import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { setARIAContainer } from 'vs/base/browser/ui/aria/aria';
|
||||
import { restoreFontInfo, readFontInfo, saveFontInfo } from 'vs/editor/browser/config/configuration';
|
||||
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { Layout } from 'vs/workbench/browser/layout';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
|
||||
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
|
||||
import { ConnectionDialogService } from 'sql/workbench/services/connection/browser/connectionDialogService';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { ErrorMessageService } from 'sql/workbench/services/errorMessage/browser/errorMessageService';
|
||||
import { ServerGroupController } from 'sql/workbench/services/serverGroup/browser/serverGroupController';
|
||||
import { IServerGroupController } from 'sql/platform/serverGroup/common/serverGroupController';
|
||||
import { IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
|
||||
import { AngularEventingService } from 'sql/platform/angularEventing/node/angularEventingService';
|
||||
import { ICapabilitiesService, CapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ICredentialsService, CredentialsService } from 'sql/platform/credentials/common/credentialsService';
|
||||
import { ISerializationService, SerializationService } from 'sql/platform/serialization/common/serializationService';
|
||||
import { IMetadataService, MetadataService } from 'sql/platform/metadata/common/metadataService';
|
||||
import { IObjectExplorerService, ObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
|
||||
import { ITaskService, TaskService } from 'sql/platform/taskHistory/common/taskService';
|
||||
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
|
||||
import { QueryModelService } from 'sql/platform/query/common/queryModelService';
|
||||
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
|
||||
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
|
||||
import { IQueryManagementService, QueryManagementService } from 'sql/platform/query/common/queryManagement';
|
||||
import { IEditorDescriptorService, EditorDescriptorService } from 'sql/workbench/services/queryEditor/common/editorDescriptorService';
|
||||
import { IScriptingService, ScriptingService } from 'sql/platform/scripting/common/scriptingService';
|
||||
import { IAdminService, AdminService } from 'sql/workbench/services/admin/common/adminService';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { JobManagementService } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { IDacFxService, DacFxService } from 'sql/platform/dacfx/common/dacFxService';
|
||||
import { IBackupService } from 'sql/platform/backup/common/backupService';
|
||||
import { BackupService } from 'sql/platform/backup/common/backupServiceImp';
|
||||
import { IBackupUiService } from 'sql/workbench/services/backup/common/backupUiService';
|
||||
import { BackupUiService } from 'sql/workbench/services/backup/browser/backupUiService';
|
||||
import { IRestoreDialogController, IRestoreService } from 'sql/platform/restore/common/restoreService';
|
||||
import { RestoreService, RestoreDialogController } from 'sql/platform/restore/common/restoreServiceImpl';
|
||||
import { INewDashboardTabDialogService } from 'sql/workbench/services/dashboard/common/newDashboardTabDialog';
|
||||
import { NewDashboardTabDialogService } from 'sql/workbench/services/dashboard/browser/newDashboardTabDialogService';
|
||||
import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
|
||||
import { FileBrowserService } from 'sql/platform/fileBrowser/common/fileBrowserService';
|
||||
import { IFileBrowserDialogController } from 'sql/workbench/services/fileBrowser/common/fileBrowserDialogController';
|
||||
import { FileBrowserDialogController } from 'sql/workbench/services/fileBrowser/browser/fileBrowserDialogController';
|
||||
import { IInsightsDialogService } from 'sql/workbench/services/insights/common/insightsDialogService';
|
||||
import { InsightsDialogService } from 'sql/workbench/services/insights/browser/insightsDialogService';
|
||||
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
|
||||
import { AccountManagementService } from 'sql/workbench/services/accountManagement/browser/accountManagementService';
|
||||
import { IProfilerService } from 'sql/workbench/services/profiler/common/interfaces';
|
||||
import { ProfilerService } from 'sql/workbench/services/profiler/common/profilerService';
|
||||
import { ISqlOAuthService } from 'sql/platform/oAuth/common/sqlOAuthService';
|
||||
import { SqlOAuthService } from 'sql/platform/oAuth/electron-browser/sqlOAuthServiceImpl';
|
||||
import { IClipboardService as sqlIClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { ClipboardService as sqlClipboardService } from 'sql/platform/clipboard/electron-browser/clipboardService';
|
||||
import { AccountPickerService } from 'sql/platform/accountManagement/browser/accountPickerService';
|
||||
import { IAccountPickerService } from 'sql/platform/accountManagement/common/accountPicker';
|
||||
import { IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
|
||||
import { ResourceProviderService } from 'sql/workbench/services/resourceProvider/browser/resourceProviderService';
|
||||
import { IDashboardViewService } from 'sql/platform/dashboard/common/dashboardViewService';
|
||||
import { DashboardViewService } from 'sql/platform/dashboard/common/dashboardViewServiceImpl';
|
||||
import { IModelViewService } from 'sql/platform/modelComponents/common/modelViewService';
|
||||
import { ModelViewService } from 'sql/platform/modelComponents/common/modelViewServiceImpl';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { DashboardService } from 'sql/platform/dashboard/browser/dashboardServiceImpl';
|
||||
import { NotebookService } from 'sql/workbench/services/notebook/common/notebookServiceImpl';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { ICommandLineProcessing } from 'sql/workbench/services/commandLine/common/commandLine';
|
||||
import { CommandLineService } from 'sql/workbench/services/commandLine/common/commandLineService';
|
||||
import { OEShimService, IOEShimService } from 'sql/parts/objectExplorer/common/objectExplorerViewTreeShim';
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
export class Workbench extends Layout {
|
||||
|
||||
private readonly _onShutdown = this._register(new Emitter<void>());
|
||||
get onShutdown(): Event<void> { return this._onShutdown.event; }
|
||||
|
||||
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
|
||||
get onWillShutdown(): Event<WillShutdownEvent> { return this._onWillShutdown.event; }
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
private readonly serviceCollection: ServiceCollection,
|
||||
logService: ILogService
|
||||
) {
|
||||
super(parent);
|
||||
|
||||
this.registerErrorHandler(logService);
|
||||
}
|
||||
|
||||
private registerErrorHandler(logService: ILogService): void {
|
||||
|
||||
// Listen on unhandled rejection events
|
||||
window.addEventListener('unhandledrejection', (event: PromiseRejectionEvent) => {
|
||||
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
|
||||
onUnexpectedError(event.reason);
|
||||
|
||||
// Prevent the printing of this event to the console
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Install handler for unexpected errors
|
||||
setUnexpectedErrorHandler(error => this.handleUnexpectedError(error, logService));
|
||||
|
||||
// Inform user about loading issues from the loader
|
||||
(<any>window).require.config({
|
||||
onError: err => {
|
||||
if (err.errorCode === 'load') {
|
||||
onUnexpectedError(new Error(localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err))));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private previousUnexpectedError: { message: string | undefined, time: number } = { message: undefined, time: 0 };
|
||||
private handleUnexpectedError(error: any, logService: ILogService): void {
|
||||
const message = toErrorMessage(error, true);
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (message === this.previousUnexpectedError.message && now - this.previousUnexpectedError.time <= 1000) {
|
||||
return; // Return if error message identical to previous and shorter than 1 second
|
||||
}
|
||||
|
||||
this.previousUnexpectedError.time = now;
|
||||
this.previousUnexpectedError.message = message;
|
||||
|
||||
// Log it
|
||||
logService.error(message);
|
||||
}
|
||||
|
||||
startup(): IInstantiationService {
|
||||
try {
|
||||
|
||||
// Configure emitter leak warning threshold
|
||||
setGlobalLeakWarningThreshold(175);
|
||||
|
||||
// Setup Intl for comparers
|
||||
setFileNameComparer(new IdleValue(() => {
|
||||
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
|
||||
return {
|
||||
collator: collator,
|
||||
collatorIsNumeric: collator.resolvedOptions().numeric
|
||||
};
|
||||
}));
|
||||
|
||||
// ARIA
|
||||
setARIAContainer(document.body);
|
||||
|
||||
// Services
|
||||
const instantiationService = this.initServices(this.serviceCollection);
|
||||
|
||||
instantiationService.invokeFunction(accessor => {
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
const storageService = accessor.get(IStorageService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
// Layout
|
||||
this.initLayout(accessor);
|
||||
|
||||
// Registries
|
||||
this.startRegistries(accessor);
|
||||
|
||||
// Context Keys
|
||||
this._register(instantiationService.createInstance(WorkbenchContextKeysHandler));
|
||||
|
||||
// Register Listeners
|
||||
this.registerListeners(lifecycleService, storageService, configurationService);
|
||||
|
||||
// Render Workbench
|
||||
this.renderWorkbench(instantiationService, accessor.get(INotificationService) as NotificationService, storageService, configurationService);
|
||||
|
||||
// Workbench Layout
|
||||
this.createWorkbenchLayout(instantiationService);
|
||||
|
||||
// Layout
|
||||
this.layout();
|
||||
|
||||
// Restore
|
||||
this.restoreWorkbench(accessor.get(IEditorService), accessor.get(IEditorGroupsService), accessor.get(IViewletService), accessor.get(IPanelService), accessor.get(ILogService), lifecycleService).then(undefined, error => onUnexpectedError(error));
|
||||
});
|
||||
|
||||
return instantiationService;
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
|
||||
throw error; // rethrow because this is a critical issue we cannot handle properly here
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
private sendUsageEvents(telemetryService: ITelemetryService): void {
|
||||
const dailyLastUseDate = Date.parse(this.storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
const weeklyLastUseDate = Date.parse(this.storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
const monthlyLastUseDate = Date.parse(this.storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
|
||||
|
||||
let today = new Date().toUTCString();
|
||||
|
||||
// daily user event
|
||||
if (this.diffInDays(Date.parse(today), dailyLastUseDate) >= 1) {
|
||||
// daily first use
|
||||
telemetryService.publicLog('telemetry.dailyFirstUse', { dailyFirstUse: true });
|
||||
this.storageService.store('telemetry.dailyLastUseDate', today, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
// weekly user event
|
||||
if (this.diffInDays(Date.parse(today), weeklyLastUseDate) >= 7) {
|
||||
// weekly first use
|
||||
telemetryService.publicLog('telemetry.weeklyFirstUse', { weeklyFirstUse: true });
|
||||
this.storageService.store('telemetry.weeklyLastUseDate', today, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
// monthly user events
|
||||
if (this.diffInDays(Date.parse(today), monthlyLastUseDate) >= 30) {
|
||||
telemetryService.publicLog('telemetry.monthlyUse', { monthlyFirstUse: true });
|
||||
this.storageService.store('telemetry.monthlyLastUseDate', today, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
private diffInDays(nowDate: number, lastUseDate: number): number {
|
||||
return (nowDate - lastUseDate) / (24 * 3600 * 1000);
|
||||
}
|
||||
*/
|
||||
|
||||
private initServices(serviceCollection: ServiceCollection): IInstantiationService {
|
||||
|
||||
// Layout Service
|
||||
serviceCollection.set(IWorkbenchLayoutService, this);
|
||||
|
||||
//
|
||||
// NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE.
|
||||
// INSTEAD, CONTRIBUTE IT VIA WORKBENCH.MAIN.TS
|
||||
//
|
||||
|
||||
// All Contributed Services
|
||||
const contributedServices = getServices();
|
||||
for (let contributedService of contributedServices) {
|
||||
serviceCollection.set(contributedService.id, contributedService.descriptor);
|
||||
}
|
||||
|
||||
const instantiationService = new InstantiationService(serviceCollection, true);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// SQL Tools services
|
||||
serviceCollection.set(IDashboardService, instantiationService.createInstance(DashboardService));
|
||||
serviceCollection.set(IDashboardViewService, instantiationService.createInstance(DashboardViewService));
|
||||
serviceCollection.set(IModelViewService, instantiationService.createInstance(ModelViewService));
|
||||
serviceCollection.set(IAngularEventingService, instantiationService.createInstance(AngularEventingService));
|
||||
serviceCollection.set(INewDashboardTabDialogService, instantiationService.createInstance(NewDashboardTabDialogService));
|
||||
serviceCollection.set(ISqlOAuthService, instantiationService.createInstance(SqlOAuthService));
|
||||
serviceCollection.set(sqlIClipboardService, instantiationService.createInstance(sqlClipboardService));
|
||||
serviceCollection.set(ICapabilitiesService, instantiationService.createInstance(CapabilitiesService));
|
||||
serviceCollection.set(IErrorMessageService, instantiationService.createInstance(ErrorMessageService));
|
||||
serviceCollection.set(IConnectionDialogService, instantiationService.createInstance(ConnectionDialogService));
|
||||
serviceCollection.set(IServerGroupController, instantiationService.createInstance(ServerGroupController));
|
||||
serviceCollection.set(ICredentialsService, instantiationService.createInstance(CredentialsService));
|
||||
serviceCollection.set(IResourceProviderService, instantiationService.createInstance(ResourceProviderService));
|
||||
serviceCollection.set(IAccountManagementService, instantiationService.createInstance(AccountManagementService, undefined));
|
||||
serviceCollection.set(IConnectionManagementService, instantiationService.createInstance(ConnectionManagementService, undefined, undefined));
|
||||
serviceCollection.set(ISerializationService, instantiationService.createInstance(SerializationService));
|
||||
serviceCollection.set(IQueryManagementService, instantiationService.createInstance(QueryManagementService));
|
||||
serviceCollection.set(IQueryModelService, instantiationService.createInstance(QueryModelService));
|
||||
serviceCollection.set(IQueryEditorService, instantiationService.createInstance(QueryEditorService));
|
||||
serviceCollection.set(IEditorDescriptorService, instantiationService.createInstance(EditorDescriptorService));
|
||||
serviceCollection.set(ITaskService, instantiationService.createInstance(TaskService));
|
||||
serviceCollection.set(IMetadataService, instantiationService.createInstance(MetadataService));
|
||||
serviceCollection.set(IObjectExplorerService, instantiationService.createInstance(ObjectExplorerService));
|
||||
serviceCollection.set(IOEShimService, instantiationService.createInstance(OEShimService));
|
||||
serviceCollection.set(IScriptingService, instantiationService.createInstance(ScriptingService));
|
||||
serviceCollection.set(IAdminService, instantiationService.createInstance(AdminService));
|
||||
serviceCollection.set(IJobManagementService, instantiationService.createInstance(JobManagementService));
|
||||
serviceCollection.set(IBackupService, instantiationService.createInstance(BackupService));
|
||||
serviceCollection.set(IBackupUiService, instantiationService.createInstance(BackupUiService));
|
||||
serviceCollection.set(IRestoreService, instantiationService.createInstance(RestoreService));
|
||||
serviceCollection.set(IRestoreDialogController, instantiationService.createInstance(RestoreDialogController));
|
||||
serviceCollection.set(IFileBrowserService, instantiationService.createInstance(FileBrowserService));
|
||||
serviceCollection.set(IFileBrowserDialogController, instantiationService.createInstance(FileBrowserDialogController));
|
||||
serviceCollection.set(IInsightsDialogService, instantiationService.createInstance(InsightsDialogService));
|
||||
serviceCollection.set(INotebookService, instantiationService.createInstance(NotebookService));
|
||||
serviceCollection.set(IAccountPickerService, instantiationService.createInstance(AccountPickerService));
|
||||
serviceCollection.set(IProfilerService, instantiationService.createInstance(ProfilerService));
|
||||
serviceCollection.set(ICommandLineProcessing, instantiationService.createInstance(CommandLineService));
|
||||
serviceCollection.set(IDacFxService, instantiationService.createInstance(DacFxService));
|
||||
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
// Wrap up
|
||||
instantiationService.invokeFunction(accessor => {
|
||||
const lifecycleService = accessor.get(ILifecycleService);
|
||||
|
||||
// TODO@Ben TODO@Sandeep debt around cyclic dependencies
|
||||
const fileService = accessor.get(IFileService);
|
||||
const configurationService = accessor.get(IConfigurationService) as any;
|
||||
|
||||
if (typeof configurationService.acquireFileService === 'function') {
|
||||
configurationService.acquireFileService(fileService);
|
||||
}
|
||||
|
||||
if (typeof configurationService.acquireInstantiationService === 'function') {
|
||||
configurationService.acquireInstantiationService(instantiationService);
|
||||
}
|
||||
|
||||
// Signal to lifecycle that services are set
|
||||
lifecycleService.phase = LifecyclePhase.Ready;
|
||||
});
|
||||
|
||||
return instantiationService;
|
||||
}
|
||||
|
||||
private startRegistries(accessor: ServicesAccessor): void {
|
||||
Registry.as<IActionBarRegistry>(ActionBarExtensions.Actionbar).start(accessor);
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).start(accessor);
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).start(accessor);
|
||||
}
|
||||
|
||||
private registerListeners(
|
||||
lifecycleService: ILifecycleService,
|
||||
storageService: IStorageService,
|
||||
configurationService: IConfigurationService
|
||||
): void {
|
||||
|
||||
// Lifecycle
|
||||
this._register(lifecycleService.onWillShutdown(event => this._onWillShutdown.fire(event)));
|
||||
this._register(lifecycleService.onShutdown(() => {
|
||||
this._onShutdown.fire();
|
||||
this.dispose();
|
||||
}));
|
||||
|
||||
// Storage
|
||||
this._register(storageService.onWillSaveState(() => saveFontInfo(storageService)));
|
||||
|
||||
// Configuration changes
|
||||
this._register(configurationService.onDidChangeConfiguration(() => this.setFontAliasing(configurationService)));
|
||||
}
|
||||
|
||||
private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto';
|
||||
private setFontAliasing(configurationService: IConfigurationService) {
|
||||
const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing');
|
||||
if (this.fontAliasing === aliasing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fontAliasing = aliasing;
|
||||
|
||||
// Remove all
|
||||
const fontAliasingValues: (typeof aliasing)[] = ['antialiased', 'none', 'auto'];
|
||||
removeClasses(this.container, ...fontAliasingValues.map(value => `monaco-font-aliasing-${value}`));
|
||||
|
||||
// Add specific
|
||||
if (fontAliasingValues.some(option => option === aliasing)) {
|
||||
addClass(this.container, `monaco-font-aliasing-${aliasing}`);
|
||||
}
|
||||
}
|
||||
|
||||
private renderWorkbench(instantiationService: IInstantiationService, notificationService: NotificationService, storageService: IStorageService, configurationService: IConfigurationService): void {
|
||||
|
||||
// State specific classes
|
||||
const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac';
|
||||
const workbenchClasses = coalesce([
|
||||
'monaco-workbench',
|
||||
platformClass,
|
||||
this.state.sideBar.hidden ? 'nosidebar' : undefined,
|
||||
this.state.panel.hidden ? 'nopanel' : undefined,
|
||||
this.state.statusBar.hidden ? 'nostatusbar' : undefined,
|
||||
this.state.fullscreen ? 'fullscreen' : undefined
|
||||
]);
|
||||
|
||||
addClasses(this.container, ...workbenchClasses);
|
||||
addClasses(document.body, platformClass); // used by our fonts
|
||||
|
||||
// Apply font aliasing
|
||||
this.setFontAliasing(configurationService);
|
||||
|
||||
// Warm up font cache information before building up too many dom elements
|
||||
restoreFontInfo(storageService);
|
||||
readFontInfo(BareFontInfo.createFromRawSettings(configurationService.getValue('editor'), getZoomLevel()));
|
||||
|
||||
// Create Parts
|
||||
[
|
||||
{ id: Parts.TITLEBAR_PART, role: 'contentinfo', classes: ['titlebar'] },
|
||||
{ id: Parts.ACTIVITYBAR_PART, role: 'navigation', classes: ['activitybar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] },
|
||||
{ id: Parts.SIDEBAR_PART, role: 'complementary', classes: ['sidebar', this.state.sideBar.position === Position.LEFT ? 'left' : 'right'] },
|
||||
{ id: Parts.EDITOR_PART, role: 'main', classes: ['editor'], options: { restorePreviousState: this.state.editor.restoreEditors } },
|
||||
{ id: Parts.PANEL_PART, role: 'complementary', classes: ['panel', this.state.panel.position === Position.BOTTOM ? 'bottom' : 'right'] },
|
||||
{ id: Parts.STATUSBAR_PART, role: 'contentinfo', classes: ['statusbar'] }
|
||||
].forEach(({ id, role, classes, options }) => {
|
||||
const partContainer = this.createPart(id, role, classes);
|
||||
|
||||
if (!configurationService.getValue('workbench.useExperimentalGridLayout')) {
|
||||
// TODO@Ben cleanup once moved to grid
|
||||
// Insert all workbench parts at the beginning. Issue #52531
|
||||
// This is primarily for the title bar to allow overriding -webkit-app-region
|
||||
this.container.insertBefore(partContainer, this.container.lastChild);
|
||||
}
|
||||
|
||||
this.getPart(id).create(partContainer, options);
|
||||
});
|
||||
|
||||
// Notification Handlers
|
||||
this.createNotificationsHandlers(instantiationService, notificationService);
|
||||
|
||||
// Add Workbench to DOM
|
||||
this.parent.appendChild(this.container);
|
||||
}
|
||||
|
||||
private createPart(id: string, role: string, classes: string[]): HTMLElement {
|
||||
const part = document.createElement('div');
|
||||
addClasses(part, 'part', ...classes);
|
||||
part.id = id;
|
||||
part.setAttribute('role', role);
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
private createNotificationsHandlers(instantiationService: IInstantiationService, notificationService: NotificationService): void {
|
||||
|
||||
// Instantiate Notification components
|
||||
const notificationsCenter = this._register(instantiationService.createInstance(NotificationsCenter, this.container, notificationService.model));
|
||||
const notificationsToasts = this._register(instantiationService.createInstance(NotificationsToasts, this.container, notificationService.model));
|
||||
this._register(instantiationService.createInstance(NotificationsAlerts, notificationService.model));
|
||||
const notificationsStatus = instantiationService.createInstance(NotificationsStatus, notificationService.model);
|
||||
|
||||
// Visibility
|
||||
this._register(notificationsCenter.onDidChangeVisibility(() => {
|
||||
notificationsStatus.update(notificationsCenter.isVisible);
|
||||
notificationsToasts.update(notificationsCenter.isVisible);
|
||||
}));
|
||||
|
||||
// Register Commands
|
||||
registerNotificationCommands(notificationsCenter, notificationsToasts);
|
||||
}
|
||||
|
||||
private restoreWorkbench(
|
||||
editorService: IEditorService,
|
||||
editorGroupService: IEditorGroupsService,
|
||||
viewletService: IViewletService,
|
||||
panelService: IPanelService,
|
||||
logService: ILogService,
|
||||
lifecycleService: ILifecycleService
|
||||
): Promise<void> {
|
||||
const restorePromises: Promise<void>[] = [];
|
||||
|
||||
// Restore editors
|
||||
mark('willRestoreEditors');
|
||||
restorePromises.push(editorGroupService.whenRestored.then(() => {
|
||||
|
||||
function openEditors(editors: IResourceEditor[], editorService: IEditorService) {
|
||||
if (editors.length) {
|
||||
return editorService.openEditors(editors);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
if (Array.isArray(this.state.editor.editorsToOpen)) {
|
||||
return openEditors(this.state.editor.editorsToOpen, editorService);
|
||||
}
|
||||
|
||||
return this.state.editor.editorsToOpen.then(editors => openEditors(editors, editorService));
|
||||
}).then(() => mark('didRestoreEditors')));
|
||||
|
||||
// Restore Sidebar
|
||||
if (this.state.sideBar.viewletToRestore) {
|
||||
mark('willRestoreViewlet');
|
||||
restorePromises.push(viewletService.openViewlet(this.state.sideBar.viewletToRestore)
|
||||
.then(viewlet => {
|
||||
if (!viewlet) {
|
||||
return viewletService.openViewlet(viewletService.getDefaultViewletId()); // fallback to default viewlet as needed
|
||||
}
|
||||
|
||||
return viewlet;
|
||||
})
|
||||
.then(() => mark('didRestoreViewlet')));
|
||||
}
|
||||
|
||||
// Restore Panel
|
||||
if (this.state.panel.panelToRestore) {
|
||||
mark('willRestorePanel');
|
||||
panelService.openPanel(this.state.panel.panelToRestore);
|
||||
mark('didRestorePanel');
|
||||
}
|
||||
|
||||
// Restore Zen Mode
|
||||
if (this.state.zenMode.restore) {
|
||||
this.toggleZenMode(true, true);
|
||||
}
|
||||
|
||||
// Restore Editor Center Mode
|
||||
if (this.state.editor.restoreCentered) {
|
||||
this.centerEditorLayout(true);
|
||||
}
|
||||
|
||||
// Emit a warning after 10s if restore does not complete
|
||||
const restoreTimeoutHandle = setTimeout(() => logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'), 10000);
|
||||
|
||||
return Promise.all(restorePromises)
|
||||
.then(() => clearTimeout(restoreTimeoutHandle))
|
||||
.catch(error => onUnexpectedError(error))
|
||||
.finally(() => {
|
||||
|
||||
// Set lifecycle phase to `Restored`
|
||||
lifecycleService.phase = LifecyclePhase.Restored;
|
||||
|
||||
// Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec)
|
||||
setTimeout(() => {
|
||||
this._register(runWhenIdle(() => {
|
||||
lifecycleService.phase = LifecyclePhase.Eventually;
|
||||
}, 2500));
|
||||
}, 2500);
|
||||
|
||||
// Telemetry: startup metrics
|
||||
mark('didStartWorkbench');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user