diff --git a/src/vs/platform/contextkey/common/contextkeys.ts b/src/vs/platform/contextkey/common/contextkeys.ts index 130a736dc1512a95f07b9b92f63c304ce6c50579..86eeb57780e163d656f5555240c5db6357299185 100644 --- a/src/vs/platform/contextkey/common/contextkeys.ts +++ b/src/vs/platform/contextkey/common/contextkeys.ts @@ -4,18 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; export const InputFocusedContextKey = 'inputFocus'; export const InputFocusedContext = new RawContextKey(InputFocusedContextKey, false); - -export const IsMacContext = new RawContextKey('isMac', isMacintosh); -export const IsLinuxContext = new RawContextKey('isLinux', isLinux); -export const IsWindowsContext = new RawContextKey('isWindows', isWindows); - -export const HasMacNativeTabsContext = new RawContextKey('hasMacNativeTabs', false); - -export const SupportsWorkspacesContext = new RawContextKey('supportsWorkspaces', true); -export const SupportsOpenFileFolderContext = new RawContextKey('supportsOpenFileFolder', isMacintosh); - -export const IsDevelopmentContext = new RawContextKey('isDevelopment', false); diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index be6ecd99e8065b65fbef4cd55ff59987d1994d7a..982192ef859232430b19e4ea7791e9c65bcf7fde 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -19,7 +19,7 @@ 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/contextkey/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'; diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts new file mode 100644 index 0000000000000000000000000000000000000000..1cbe41a4f309b12e221d9ea874628bb487cf7ae6 --- /dev/null +++ b/src/vs/workbench/browser/contextkeys.ts @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. 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 } 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 { EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; + +export class WorkbenchContextKeysHandler extends Disposable { + private inputFocusedContext: IContextKey; + private activeEditorContext: IContextKey; + private editorsVisibleContext: IContextKey; + private textCompareEditorVisibleContext: IContextKey; + private textCompareEditorActiveContext: IContextKey; + private activeEditorGroupEmpty: IContextKey; + private multipleEditorGroupsContext: IContextKey; + private workbenchStateContext: IContextKey; + private workspaceFolderCountContext: IContextKey; + private splitEditorsVerticallyContext: IContextKey; + + 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: EditorGroupsServiceImpl + ) { + 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(); + } + })); + } + + private initContextKeys(): void { + + // Platform + IsMacContext.bindTo(this.contextKeyService); + IsLinuxContext.bindTo(this.contextKeyService); + IsWindowsContext.bindTo(this.contextKeyService); + + // macOS Native Tabs + const windowConfig = this.configurationService.getValue(); + 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(); + } + + 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'; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 016b50dc100fcaad67a84633d5e07de3935adef8..f5b4eb854e4e90c4a6a3517198d69410ba4958a3 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -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(notificationFocusedId, true); - -const notificationsCenterVisibleId = 'notificationCenterVisible'; -export const NotificationsCenterVisibleContext = new RawContextKey(notificationsCenterVisibleId, false); - -const notificationsToastsVisibleId = 'notificationToastsVisible'; -export const NotificationsToastsVisibleContext = new RawContextKey(notificationsToastsVisibleId, false); +export const NotificationFocusedContext = new RawContextKey('notificationFocus', true); +export const NotificationsCenterVisibleContext = new RawContextKey('notificationCenterVisible', false); +export const NotificationsToastsVisibleContext = new RawContextKey('notificationToastsVisible', false); export interface INotificationsCenterController { readonly isVisible: boolean; diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 5c522c29ddc03060bc53149e42fb5b999cbc6082..ceaca6180a98df5c84bec60243427d7e4b744345 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -32,7 +32,7 @@ import Severity from 'vs/base/common/severity'; 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'; @@ -836,7 +836,7 @@ export class QuickInputService extends Component implements IQuickInputService { @IAccessibilityService private readonly accessibilityService: IAccessibilityService ) { super(QuickInputService.ID, themeService, storageService); - this.inQuickOpenContext = new RawContextKey('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.registerKeyModsListeners(); diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.ts b/src/vs/workbench/browser/parts/quickopen/quickopen.ts index 2a176b5726fa9b30ddb30d5dda597343a9ea8e50..808a6e8bb4e9d8e7d440d3108a772d9c7943ca2d 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickopen.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickopen.ts @@ -8,11 +8,13 @@ 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(inQuickOpenKey, false); +export const inQuickOpenContext = ContextKeyExpr.has(inQuickOpenKey); export const defaultQuickOpenContextKey = 'inFilesPicker'; export const defaultQuickOpenContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(defaultQuickOpenContextKey)); diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 83e373830a07a9c9dccd3863dc460375f5ba1187..653dde866acabc542d868ee7cb8f3cafa6083cb5 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -33,6 +33,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; +export const SidebarVisibleContext = new RawContextKey('sidebarVisible', false); export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0921a04c80dda030d5b1f3c318b7199961b7879 --- /dev/null +++ b/src/vs/workbench/common/contextkeys.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; + +export const IsMacContext = new RawContextKey('isMac', isMacintosh); +export const IsLinuxContext = new RawContextKey('isLinux', isLinux); +export const IsWindowsContext = new RawContextKey('isWindows', isWindows); + +export const HasMacNativeTabsContext = new RawContextKey('hasMacNativeTabs', false); + +export const SupportsWorkspacesContext = new RawContextKey('supportsWorkspaces', true); +export const SupportsOpenFileFolderContext = new RawContextKey('supportsOpenFileFolder', isMacintosh); + +export const IsDevelopmentContext = new RawContextKey('isDevelopment', false); + +export const WorkbenchStateContext = new RawContextKey('workbenchState', undefined); + +export const WorkspaceFolderCountContext = new RawContextKey('workspaceFolderCount', 0); \ No newline at end of file diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index d1bea6d2d45a0ad5d720b39e30dd00c30f58a419..f83842f985ed3f7c850ddc69cc16da618a4a5d95 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -23,7 +23,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { SupportsWorkspacesContext } from 'vs/platform/contextkey/common/contextkeys'; +import { SupportsWorkspacesContext } from 'vs/workbench/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; // Contribute Global Actions diff --git a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts index e00f7328347a50eb868bb384bde279bfc2f1dfd0..6d2d5ab3bc17e824d42f4597389ed25f41172e89 100644 --- a/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/electron-browser/preferences.contribution.ts @@ -12,7 +12,8 @@ import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; import * as nls from 'vs/nls'; import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -423,7 +424,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon dark: URI.parse(require.toUrl(`vs/workbench/contrib/preferences/electron-browser/media/preferences-editor-inverse.svg`)) } }, - when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource.toString()), new RawContextKey('workbenchState', '').isEqualTo('workspace')), + when: ContextKeyExpr.and(ResourceContextKey.Resource.isEqualTo(this.preferencesService.workspaceSettingsResource.toString()), WorkbenchStateContext.isEqualTo('workspace')), group: 'navigation', order: 1 }); @@ -478,7 +479,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { title: { value: `${category}: ${OpenFolderSettingsAction.LABEL}`, original: 'Preferences: Open Folder Settings' }, category: nls.localize('preferencesCategory', "Prefernces") }, - when: new RawContextKey('workbenchState', '').isEqualTo('workspace') + when: WorkbenchStateContext.isEqualTo('workspace') }); CommandsRegistry.registerCommand(OpenWorkspaceSettingsAction.ID, serviceAccessor => { @@ -490,7 +491,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { title: { value: `${category}: ${OpenWorkspaceSettingsAction.LABEL}`, original: 'Preferences: Open Workspace Settings' }, category: nls.localize('preferencesCategory', "Prefernces") }, - when: new RawContextKey('workbenchState', '').notEqualsTo('empty') + when: WorkbenchStateContext.notEqualsTo('empty') }); KeybindingsRegistry.registerCommandAndKeybindingRule({ diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 2b20a241748bff1551032bccc90d8b8547a6db0f..0d9d443dc16d80fa7ddcb5b5c6fcb1a40fa119bd 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -15,13 +15,13 @@ import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductor import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ReloadWindowAction, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; +import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; @@ -128,7 +128,7 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; id: OpenWorkspaceConfigFileAction.ID, title: { value: `${workspacesCategory}: ${OpenWorkspaceConfigFileAction.LABEL}`, original: 'Workspaces: Open Workspace Configuration File' }, }, - when: new RawContextKey('workbenchState', '').isEqualTo('workspace') + when: WorkbenchStateContext.isEqualTo('workspace') }); })(); @@ -308,10 +308,10 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; command: { id: CloseWorkspaceAction.ID, title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), - precondition: new RawContextKey('workspaceFolderCount', 0).notEqualsTo('0') + precondition: WorkspaceFolderCountContext.notEqualsTo('0') }, order: 3, - when: new RawContextKey('workbenchState', '').notEqualsTo('workspace') + when: WorkbenchStateContext.notEqualsTo('workspace') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -321,7 +321,7 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") }, order: 3, - when: new RawContextKey('workbenchState', '').isEqualTo('workspace') + when: WorkbenchStateContext.isEqualTo('workspace') }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 3b0cbb1fa2d22739bdc83e759716a5a7366f3606..22848a1783ec58220d736562fb5a26c876650a8c 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -5,14 +5,12 @@ import * as fs from 'fs'; import { createHash } from 'crypto'; -import * as nls from 'vs/nls'; import * as perf from 'vs/base/common/performance'; import { Workbench } from 'vs/workbench/electron-browser/workbench'; import { ElectronWindow } from 'vs/workbench/electron-browser/window'; import * as browser from 'vs/base/browser/browser'; -import { domContentLoaded } from 'vs/base/browser/dom'; +import { domContentLoaded, addDisposableListener, EventType, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { onUnexpectedError } from 'vs/base/common/errors'; -import * as comparer from 'vs/base/common/comparers'; import * as platform from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; @@ -45,8 +43,6 @@ import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { Schemas } from 'vs/base/common/network'; import { sanitizeFilePath } from 'vs/base/node/extfs'; import { basename } from 'vs/base/common/path'; -import { IdleValue } from 'vs/base/common/async'; -import { setGlobalLeakWarningThreshold } from 'vs/base/common/event'; import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -57,6 +53,8 @@ import { registerWindowDriver } from 'vs/platform/driver/electron-browser/driver export class CodeWindow extends Disposable { + private workbench: Workbench; + constructor(private readonly configuration: IWindowConfiguration) { super(); @@ -74,9 +72,6 @@ export class CodeWindow extends Disposable { // Setup perf perf.importEntries(this.configuration.perfEntries); - // Configure emitter leak warning threshold - setGlobalLeakWarningThreshold(175); - // Browser config browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151) @@ -84,24 +79,6 @@ export class CodeWindow extends Disposable { // Keyboard support KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); - - // Setup Intl for comparers - comparer.setFileNameComparer(new IdleValue(() => { - const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); - return { - collator: collator, - collatorIsNumeric: collator.resolvedOptions().numeric - }; - })); - - // Inform user about loading issues from the loader - (self).require.config({ - onError: err => { - if (err.errorCode === 'load') { - onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); - } - } - }); } private reviveUris() { @@ -135,19 +112,22 @@ export class CodeWindow extends Disposable { const instantiationService = new InstantiationService(services, true); // Create Workbench - const workbench: Workbench = instantiationService.createInstance( + this.workbench = instantiationService.createInstance( Workbench, document.body, this.configuration, services ); + // Layout + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); + // Workbench Lifecycle - this._register(workbench.onShutdown(() => this.dispose())); - this._register(workbench.onWillShutdown(event => event.join((services.get(IStorageService) as StorageService).close()))); + this._register(this.workbench.onShutdown(() => this.dispose())); + this._register(this.workbench.onWillShutdown(event => event.join((services.get(IStorageService) as StorageService).close()))); // Startup - workbench.startup(); + this.workbench.startup(); // Window this._register(instantiationService.createInstance(ElectronWindow)); @@ -160,6 +140,24 @@ export class CodeWindow extends Disposable { }); } + private onWindowResize(e: any, retry: boolean): void { + if (e.target === window) { + if (window.document && window.document.body && window.document.body.clientWidth === 0) { + // TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled + // where for some reason the window clientWidth is reported as 0 when switching + // between simple fullscreen and normal screen. In that case we schedule the layout + // call at the next animation frame once, in the hope that the dimensions are + // proper then. + if (retry) { + scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false)); + } + return; + } + + this.workbench.layout(); + } + } + private initServices(electronMainClient: ElectronIPCClient): Promise { const serviceCollection = new ServiceCollection(); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 76c75a02190659bc4b33f27802725fce6a995178..3e0622f6ae9790d824d203cf58ff36034569bc88 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -5,10 +5,12 @@ import 'vs/workbench/browser/style'; +import { localize } from 'vs/nls'; +import { setFileNameComparer } from 'vs/base/common/comparers'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, addClasses, scheduleAtNextAnimationFrame, addClass, removeClass, trackFocus, isAncestor, getClientArea, position, size, removeClasses } from 'vs/base/browser/dom'; -import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; +import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event'; +import { EventType, addDisposableListener, addClasses, addClass, removeClass, isAncestor, getClientArea, position, size, removeClasses } from 'vs/base/browser/dom'; +import { RunOnceScheduler, runWhenIdle, IdleValue } from 'vs/base/common/async'; import { getZoomLevel, onDidChangeFullscreen, isFullscreen, getZoomFactor } from 'vs/base/browser/browser'; import { mark } from 'vs/base/common/performance'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; @@ -17,9 +19,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { isWindows, isLinux, isMacintosh, language } from 'vs/base/common/platform'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, TextCompareEditorVisibleContext, TEXT_DIFF_EDITOR_ID, EditorsVisibleContext, InEditorZenModeContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, IUntitledResourceInput, IResourceDiffInput, SplitEditorsVertically, TextCompareEditorActiveContext, ActiveEditorContext } from 'vs/workbench/common/editor'; +import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, IUntitledResourceInput, IResourceDiffInput, InEditorZenModeContext } from 'vs/workbench/common/editor'; import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; -import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; +import { SidebarPart, SidebarVisibleContext } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; @@ -39,7 +41,7 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/ import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing'; -import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IActivityService } from 'vs/workbench/services/activity/common/activity'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IFileService } from 'vs/platform/files/common/files'; @@ -71,7 +73,6 @@ import { IDecorationsService } from 'vs/workbench/services/decorations/browser/d import { ActivityService } from 'vs/workbench/services/activity/browser/activityService'; import { URI } from 'vs/base/common/uri'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; -import { InputFocusedContext, IsMacContext, IsLinuxContext, IsWindowsContext, SupportsOpenFileFolderContext, SupportsWorkspacesContext, IsDevelopmentContext, HasMacNativeTabsContext } from 'vs/platform/contextkey/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; import { ViewsService } from 'vs/workbench/browser/parts/views/views'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -84,7 +85,7 @@ import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/no import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/services/preferences/browser/preferencesService'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; @@ -138,6 +139,7 @@ import { WorkbenchThemeService } from 'vs/workbench/services/themes/browser/work import { IProductService } from 'vs/platform/product/common/product'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { WorkbenchContextKeysHandler } from 'vs/workbench/browser/contextkeys'; // import@node import { BackupFileService, InMemoryBackupFileService } from 'vs/workbench/services/backup/node/backupFileService'; @@ -212,14 +214,6 @@ const Identifiers = { STATUSBAR_PART: 'workbench.parts.statusbar' }; -function getWorkbenchStateString(state: WorkbenchState): string { - switch (state) { - case WorkbenchState.EMPTY: return 'empty'; - case WorkbenchState.FOLDER: return 'folder'; - case WorkbenchState.WORKSPACE: return 'workspace'; - } -} - interface IZenMode { active: boolean; transitionedToFullScreen: boolean; @@ -319,7 +313,7 @@ export class Workbench extends Disposable implements IPartService { lastSidebarDimension: 300, }; - private inZenMode: IContextKey; + private inZenModeContext: IContextKey; private sideBarVisibleContext: IContextKey; private closeEmptyWindowScheduler: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.onAllEditorsClosed(), 50)); @@ -360,6 +354,15 @@ export class Workbench extends Disposable implements IPartService { // Install handler for unexpected errors setUnexpectedErrorHandler(error => this.handleUnexpectedError(error)); + + // Inform user about loading issues from the loader + (self).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 handleUnexpectedError(error: any): void { @@ -401,6 +404,18 @@ export class Workbench extends Disposable implements IPartService { // Logging this.logService.trace('workbench configuration', JSON.stringify(this.configuration)); + // 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); @@ -418,7 +433,9 @@ export class Workbench extends Disposable implements IPartService { this.initServices(this.serviceCollection); // Context Keys - this.handleContextKeys(); + this._register(this.instantiationService.createInstance(WorkbenchContextKeysHandler)); + this.inZenModeContext = InEditorZenModeContext.bindTo(this.contextKeyService); + this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService); // Register Listeners this.registerListeners(); @@ -440,9 +457,7 @@ export class Workbench extends Disposable implements IPartService { this.logService.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.'); }, 10000); - this.lifecycleService.when(LifecyclePhase.Restored).then(() => { - clearTimeout(timeoutHandle); - }); + this.lifecycleService.when(LifecyclePhase.Restored).then(() => clearTimeout(timeoutHandle)); // Restore Parts return this.restoreParts(); @@ -787,31 +802,8 @@ export class Workbench extends Disposable implements IPartService { this._register(this.editorGroupService.onDidAddGroup(() => this.centerEditorLayout(this.shouldCenterLayout))); this._register(this.editorGroupService.onDidRemoveGroup(() => this.centerEditorLayout(this.shouldCenterLayout))); - // Layout - this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); - // Prevent workbench from scrolling #55456 - this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => { - this.workbench.scrollTop = 0; - })); - } - - private onWindowResize(e: any, retry: boolean): void { - if (e.target === window) { - if (window.document && window.document.body && window.document.body.clientWidth === 0) { - // TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled - // where for some reason the window clientWidth is reported as 0 when switching - // between simple fullscreen and normal screen. In that case we schedule the layout - // call at the next animation frame once, in the hope that the dimensions are - // proper then. - if (retry) { - scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false)); - } - return; - } - - this.layout(); - } + this._register(addDisposableListener(this.workbench, EventType.SCROLL, () => this.workbench.scrollTop = 0)); } private onFullscreenChanged(): void { @@ -913,119 +905,6 @@ export class Workbench extends Disposable implements IPartService { //#endregion - private handleContextKeys(): void { - IsMacContext.bindTo(this.contextKeyService); - IsLinuxContext.bindTo(this.contextKeyService); - IsWindowsContext.bindTo(this.contextKeyService); - - const windowConfig = this.configurationService.getValue(); - HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs); - - IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment); - - SupportsWorkspacesContext.bindTo(this.contextKeyService); - SupportsOpenFileFolderContext.bindTo(this.contextKeyService).set(!!this.windowService.getConfiguration().remoteAuthority); - - this.inZenMode = InEditorZenModeContext.bindTo(this.contextKeyService); - - const sidebarVisibleContextRaw = new RawContextKey('sidebarVisible', false); - this.sideBarVisibleContext = sidebarVisibleContextRaw.bindTo(this.contextKeyService); - - const activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService); - const editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); - const textCompareEditorVisible = TextCompareEditorVisibleContext.bindTo(this.contextKeyService); - const textCompareEditorActive = TextCompareEditorActiveContext.bindTo(this.contextKeyService); - const activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService); - const multipleEditorGroups = MultipleEditorGroupsContext.bindTo(this.contextKeyService); - - const updateEditorContextKeys = () => { - const activeControl = this.editorService.activeControl; - const visibleEditors = this.editorService.visibleControls; - - textCompareEditorActive.set(!!activeControl && activeControl.getId() === TEXT_DIFF_EDITOR_ID); - textCompareEditorVisible.set(visibleEditors.some(control => control.getId() === TEXT_DIFF_EDITOR_ID)); - - if (visibleEditors.length > 0) { - editorsVisibleContext.set(true); - } else { - editorsVisibleContext.reset(); - } - - if (!this.editorService.activeEditor) { - activeEditorGroupEmpty.set(true); - } else { - activeEditorGroupEmpty.reset(); - } - - if (this.editorGroupService.count > 1) { - multipleEditorGroups.set(true); - } else { - multipleEditorGroups.reset(); - } - - if (activeControl) { - activeEditorContext.set(activeControl.getId()); - } else { - activeEditorContext.reset(); - } - }; - - this.editorPart.whenRestored.then(() => updateEditorContextKeys()); - this._register(this.editorService.onDidActiveEditorChange(() => updateEditorContextKeys())); - this._register(this.editorService.onDidVisibleEditorsChange(() => updateEditorContextKeys())); - this._register(this.editorGroupService.onDidAddGroup(() => updateEditorContextKeys())); - this._register(this.editorGroupService.onDidRemoveGroup(() => updateEditorContextKeys())); - - const inputFocused = InputFocusedContext.bindTo(this.contextKeyService); - - function activeElementIsInput(): boolean { - return !!document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA'); - } - - function trackInputFocus(): void { - const isInputFocused = activeElementIsInput(); - inputFocused.set(isInputFocused); - - if (isInputFocused) { - const tracker = trackFocus(document.activeElement as HTMLElement); - Event.once(tracker.onDidBlur)(() => { - inputFocused.set(activeElementIsInput()); - - tracker.dispose(); - }); - } - } - - this._register(addDisposableListener(window, 'focusin', () => trackInputFocus(), true)); - - const workbenchStateRawContext = new RawContextKey('workbenchState', getWorkbenchStateString(this.configurationService.getWorkbenchState())); - const workbenchStateContext = workbenchStateRawContext.bindTo(this.contextKeyService); - this._register(this.configurationService.onDidChangeWorkbenchState(() => { - workbenchStateContext.set(getWorkbenchStateString(this.configurationService.getWorkbenchState())); - })); - - const workspaceFolderCountRawContext = new RawContextKey('workspaceFolderCount', this.configurationService.getWorkspace().folders.length); - const workspaceFolderCountContext = workspaceFolderCountRawContext.bindTo(this.contextKeyService); - this._register(this.configurationService.onDidChangeWorkspaceFolders(() => { - workspaceFolderCountContext.set(this.configurationService.getWorkspace().folders.length); - })); - - const splitEditorsVerticallyContext = SplitEditorsVertically.bindTo(this.contextKeyService); - - const updateSplitEditorsVerticallyContext = () => { - const direction = preferredSideBySideGroupDirection(this.configurationService); - splitEditorsVerticallyContext.set(direction === GroupDirection.DOWN); - }; - - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('workbench.editor.openSideBySideDirection')) { - updateSplitEditorsVerticallyContext(); - } - })); - - updateSplitEditorsVerticallyContext(); - } - private restoreParts(): Promise { const restorePromises: Promise[] = []; @@ -1708,7 +1587,7 @@ export class Workbench extends Disposable implements IPartService { toggleFullScreen = this.zenMode.transitionedToFullScreen && isFullscreen(); } - this.inZenMode.set(this.zenMode.active); + this.inZenModeContext.set(this.zenMode.active); if (!skipLayout) { this.layout(); @@ -1807,7 +1686,7 @@ export class Workbench extends Disposable implements IPartService { } } - private layout(options?: ILayoutOptions): void { + layout(options?: ILayoutOptions): void { this.contextViewService.layout(); if (this.workbenchStarted && !this.workbenchShutdown) {