diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 6d0693d7657bbd88e81aeae013aef74d63af848e..a570fa16b493be29bf9ee788355c2a47d0258755 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -303,7 +303,7 @@ export class IssueReporter extends Disposable { const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger')); this.logService = new FollowerLogService(loggerClient, logService); - const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() + const sharedProcess = mainProcessService.getChannel('sharedProcess').call('whenSharedProcessReady') .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${configuration.windowId}`)); const instantiationService = new InstantiationService(serviceCollection, true); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index ca463cecefe6af5ec876fd04cc7eec44d056a307..a817dbcb15736126bda8a678c5fcd3f747a23bfd 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -77,6 +77,7 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; +import { ISharedProcessMainService, SharedProcessMainService } from 'vs/platform/ipc/electron-main/sharedProcessMainService'; export class CodeApplication extends Disposable { @@ -448,7 +449,8 @@ export class CodeApplication extends Disposable { } services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv])); - services.set(IWindowsService, new SyncDescriptor(LegacyWindowsMainService, [sharedProcess])); + services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess])); + services.set(IWindowsService, new SyncDescriptor(LegacyWindowsMainService)); services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics'))); @@ -545,6 +547,10 @@ export class CodeApplication extends Disposable { const electronChannel = new SimpleServiceProxyChannel(electronService); electronIpcServer.registerChannel('electron', electronChannel); + const sharedProcessMainService = accessor.get(ISharedProcessMainService); + const sharedProcessChannel = new SimpleServiceProxyChannel(sharedProcessMainService); + electronIpcServer.registerChannel('sharedProcess', sharedProcessChannel); + const workspacesMainService = accessor.get(IWorkspacesMainService); const workspacesChannel = new WorkspacesChannel(workspacesMainService); electronIpcServer.registerChannel('workspaces', workspacesChannel); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 3df7e9b55eaafc540edabeb7df586204839dbf4b..6a2f8b994c5a9259cb76168e6753765231e373ec 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -908,7 +908,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - onWindowTitleDoubleClick(): void { + handleTitleDoubleClick(): void { // Respect system settings on mac with regards to title click on windows title if (isMacintosh) { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 12a1485aef134a11ef82d8b4001304741001a706..f20f1190597400c93702071b16cf5c390935ac4a 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1763,7 +1763,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { this._onWindowClose.fire(win.id); } - async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise { + async pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { const title = localize('open', "Open"); const paths = await this.dialogs.pick({ ...options, pickFolders: true, pickFiles: true, title }); if (paths) { @@ -1775,7 +1775,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { })); this.open({ context: OpenContext.DIALOG, - contextWindowId: options.windowId, + contextWindowId: win ? win.id : undefined, cli: this.environmentService.args, urisToOpen, forceNewWindow: options.forceNewWindow @@ -1783,14 +1783,14 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } } - async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise { + async pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { const title = localize('openFolder', "Open Folder"); const paths = await this.dialogs.pick({ ...options, pickFolders: true, title }); if (paths) { this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFolder', options.telemetryExtraData); this.open({ context: OpenContext.DIALOG, - contextWindowId: options.windowId, + contextWindowId: win ? win.id : undefined, cli: this.environmentService.args, urisToOpen: paths.map(path => ({ folderUri: URI.file(path) })), forceNewWindow: options.forceNewWindow @@ -1798,14 +1798,14 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } } - async pickFileAndOpen(options: INativeOpenDialogOptions): Promise { + async pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { const title = localize('openFile', "Open File"); const paths = await this.dialogs.pick({ ...options, pickFiles: true, title }); if (paths) { this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFile', options.telemetryExtraData); this.open({ context: OpenContext.DIALOG, - contextWindowId: options.windowId, + contextWindowId: win ? win.id : undefined, cli: this.environmentService.args, urisToOpen: paths.map(path => ({ fileUri: URI.file(path) })), forceNewWindow: options.forceNewWindow @@ -1813,7 +1813,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } } - async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise { + async pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise { const title = localize('openWorkspaceTitle', "Open Workspace"); const buttonLabel = mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")); const filters = WORKSPACE_FILTER; @@ -1822,7 +1822,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { this.sendPickerTelemetry(paths, options.telemetryEventName || 'openWorkspace', options.telemetryExtraData); this.open({ context: OpenContext.DIALOG, - contextWindowId: options.windowId, + contextWindowId: win ? win.id : undefined, cli: this.environmentService.args, urisToOpen: paths.map(path => ({ workspaceUri: URI.file(path) })), forceNewWindow: options.forceNewWindow @@ -1899,7 +1899,7 @@ class Dialogs { this.noWindowDialogQueue = new Queue(); } - async pick(options: IInternalNativeOpenDialogOptions): Promise { + async pick(options: IInternalNativeOpenDialogOptions, win?: ICodeWindow): Promise { // Ensure dialog options const dialogOptions: OpenDialogOptions = { @@ -1930,9 +1930,9 @@ class Dialogs { } // Show Dialog - const focusedWindow = (typeof options.windowId === 'number' ? this.windowsMainService.getWindowById(options.windowId) : undefined) || this.windowsMainService.getFocusedWindow(); + const windowToUse = win || this.windowsMainService.getFocusedWindow(); - const result = await this.showOpenDialog(dialogOptions, focusedWindow); + const result = await this.showOpenDialog(dialogOptions, windowToUse); if (result && result.filePaths && result.filePaths.length > 0) { // Remember path in storage for next time diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index c62f264a871d7bd70022e3e2503c0ec08600dc22..e4385dd062bee8f5963a1b2357ce30665c6f6c04 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -4,12 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; -import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue } from 'electron'; +import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu } from 'electron'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { OpenContext, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; import { isMacintosh } from 'vs/base/common/platform'; +import { IElectronService } from 'vs/platform/electron/node/electron'; +import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; +import { AddContextToFunctions } from 'vs/platform/ipc/node/simpleIpcProxy'; -export class ElectronMainService { +export class ElectronMainService implements AddContextToFunctions { _serviceBrand: undefined; @@ -36,6 +39,13 @@ export class ElectronMainService { } } + async handleTitleDoubleClick(windowId: number): Promise { + const window = this.windowsMainService.getWindowById(windowId); + if (window) { + window.handleTitleDoubleClick(); + } + } + //#endregion //#region Dialog @@ -53,27 +63,19 @@ export class ElectronMainService { } async pickFileFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - options.windowId = windowId; - - return this.windowsMainService.pickFileFolderAndOpen(options); + return this.windowsMainService.pickFileFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickFileAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - options.windowId = windowId; - - return this.windowsMainService.pickFileAndOpen(options); + return this.windowsMainService.pickFileAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - options.windowId = windowId; - - return this.windowsMainService.pickFolderAndOpen(options); + return this.windowsMainService.pickFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickWorkspaceAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { - options.windowId = windowId; - - return this.windowsMainService.pickWorkspaceAndOpen(options); + return this.windowsMainService.pickWorkspaceAndOpen(options, this.windowsMainService.getWindowById(windowId)); } //#endregion @@ -98,6 +100,45 @@ export class ElectronMainService { } } + async openExternal(windowId: number, url: string): Promise { + return this.windowsMainService.openExternal(url); + } + + async updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { + const window = this.windowsMainService.getWindowById(windowId); + if (window) { + window.updateTouchBar(items); + } + } + + //#endregion + + //#region macOS Touchbar + + async newWindowTab(): Promise { + this.windowsMainService.openNewTabbedWindow(OpenContext.API); + } + + async showPreviousWindowTab(): Promise { + Menu.sendActionToFirstResponder('selectPreviousTab:'); + } + + async showNextWindowTab(): Promise { + Menu.sendActionToFirstResponder('selectNextTab:'); + } + + async moveWindowTabToNewWindow(): Promise { + Menu.sendActionToFirstResponder('moveTabToNewWindow:'); + } + + async mergeAllWindowTabs(): Promise { + Menu.sendActionToFirstResponder('mergeAllWindows:'); + } + + async toggleWindowTabsBar(): Promise { + Menu.sendActionToFirstResponder('toggleTabBar:'); + } + //#endregion //#region Lifecycle @@ -113,6 +154,32 @@ export class ElectronMainService { } } + async closeWorkpsace(windowId: number): Promise { + const window = this.windowsMainService.getWindowById(windowId); + if (window) { + return this.windowsMainService.closeWorkspace(window); + } + } + + async quit(windowId: number): Promise { + return this.windowsMainService.quit(); + } + + //#endregion + + //#region Connectivity + + async resolveProxy(windowId: number, url: string): Promise { + return new Promise(resolve => { + const window = this.windowsMainService.getWindowById(windowId); + if (window && window.win && window.win.webContents && window.win.webContents.session) { + window.win.webContents.session.resolveProxy(url, proxy => resolve(proxy)); + } else { + resolve(); + } + }); + } + //#endregion //#region Development @@ -136,19 +203,8 @@ export class ElectronMainService { } } - //#endregion - - //#region Connectivity - - async resolveProxy(windowId: number, url: string): Promise { - return new Promise(resolve => { - const window = this.windowsMainService.getWindowById(windowId); - if (window && window.win && window.win.webContents && window.win.webContents.session) { - window.win.webContents.session.resolveProxy(url, proxy => resolve(proxy)); - } else { - resolve(); - } - }); + async startCrashReporter(windowId: number, options: CrashReporterStartOptions): Promise { + crashReporter.start(options); } //#endregion diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts index 806a91f99c9b6e91c36f47cef479ca0afd8e9bb8..a346a5d7af46f6a1a50de9612a27672e0facec58 100644 --- a/src/vs/platform/electron/node/electron.ts +++ b/src/vs/platform/electron/node/electron.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue } from 'electron'; +import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; +import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; export const IElectronService = createDecorator('electronService'); @@ -17,6 +18,7 @@ export interface IElectronService { windowCount(): Promise; openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise; toggleFullScreen(): Promise; + handleTitleDoubleClick(): Promise; // Dialogs showMessageBox(options: MessageBoxOptions): Promise; @@ -32,14 +34,27 @@ export interface IElectronService { showItemInFolder(path: string): Promise; setRepresentedFilename(path: string): Promise; setDocumentEdited(edited: boolean): Promise; + openExternal(url: string): Promise; + updateTouchBar(items: ISerializableCommandAction[][]): Promise; + + // macOS Touchbar + newWindowTab(): Promise; + showPreviousWindowTab(): Promise; + showNextWindowTab(): Promise; + moveWindowTabToNewWindow(): Promise; + mergeAllWindowTabs(): Promise; + toggleWindowTabsBar(): Promise; // Lifecycle relaunch(options?: { addArgs?: string[], removeArgs?: string[] }): Promise; reload(): Promise; + closeWorkpsace(): Promise; + quit(): Promise; // Development openDevTools(options?: OpenDevToolsOptions): Promise; toggleDevTools(): Promise; + startCrashReporter(options: CrashReporterStartOptions): Promise; // Connectivity resolveProxy(url: string): Promise; diff --git a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts index dc78e22470170175c54be3222a64638321d495ea..bfeaf0ac6533ffafa69a8b51fc076bfdd47469ad 100644 --- a/src/vs/platform/ipc/electron-browser/sharedProcessService.ts +++ b/src/vs/platform/ipc/electron-browser/sharedProcessService.ts @@ -6,9 +6,10 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Client } from 'vs/base/parts/ipc/common/ipc.net'; import { connect } from 'vs/base/parts/ipc/node/ipc.net'; -import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; export const ISharedProcessService = createDecorator('sharedProcessService'); @@ -17,8 +18,10 @@ export interface ISharedProcessService { _serviceBrand: undefined; getChannel(channelName: string): IChannel; - registerChannel(channelName: string, channel: IServerChannel): void; + + whenSharedProcessReady(): Promise; + toggleSharedProcessWindow(): Promise; } export class SharedProcessService implements ISharedProcessService { @@ -26,16 +29,23 @@ export class SharedProcessService implements ISharedProcessService { _serviceBrand: undefined; private withSharedProcessConnection: Promise>; + private sharedProcessMainChannel: IChannel; constructor( - @IWindowsService windowsService: IWindowsService, + @IMainProcessService mainProcessService: IMainProcessService, @IWindowService windowService: IWindowService, @IEnvironmentService environmentService: IEnvironmentService ) { - this.withSharedProcessConnection = windowsService.whenSharedProcessReady() + this.sharedProcessMainChannel = mainProcessService.getChannel('sharedProcess'); + + this.withSharedProcessConnection = this.whenSharedProcessReady() .then(() => connect(environmentService.sharedIPCHandle, `window:${windowService.windowId}`)); } + whenSharedProcessReady(): Promise { + return this.sharedProcessMainChannel.call('whenSharedProcessReady'); + } + getChannel(channelName: string): IChannel { return getDelayedChannel(this.withSharedProcessConnection.then(connection => connection.getChannel(channelName))); } @@ -43,4 +53,8 @@ export class SharedProcessService implements ISharedProcessService { registerChannel(channelName: string, channel: IServerChannel): void { this.withSharedProcessConnection.then(connection => connection.registerChannel(channelName, channel)); } + + toggleSharedProcessWindow(): Promise { + return this.sharedProcessMainChannel.call('toggleSharedProcessWindow'); + } } diff --git a/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts b/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts new file mode 100644 index 0000000000000000000000000000000000000000..bab9a00f90d79258be0a90409c0394b9ff3db4c6 --- /dev/null +++ b/src/vs/platform/ipc/electron-main/sharedProcessMainService.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ISharedProcess } from 'vs/platform/windows/electron-main/windows'; + +export const ISharedProcessMainService = createDecorator('sharedProcessMainService'); + +export interface ISharedProcessMainService { + + _serviceBrand: undefined; + + whenSharedProcessReady(): Promise; + toggleSharedProcessWindow(): Promise; +} +export class SharedProcessMainService implements ISharedProcessMainService { + + _serviceBrand: undefined; + + constructor(private sharedProcess: ISharedProcess) { } + + whenSharedProcessReady(): Promise { + return this.sharedProcess.whenReady(); + } + + async toggleSharedProcessWindow(): Promise { + return this.sharedProcess.toggle(); + } +} diff --git a/src/vs/platform/ipc/node/simpleIpcProxy.ts b/src/vs/platform/ipc/node/simpleIpcProxy.ts index ebc3b351366aa0fa3be53cf9174d50b7aa01b4fd..66cfa69c2df5eb94e5dd4bb4b299603f96454939 100644 --- a/src/vs/platform/ipc/node/simpleIpcProxy.ts +++ b/src/vs/platform/ipc/node/simpleIpcProxy.ts @@ -11,6 +11,11 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; // for a very basic process <=> process communication over methods. // +export type AddContextToFunctions = { + // For every property: IF property is a FUNCTION ADD context as first parameter and original parameters afterwards with same return type, otherwise preserve as is + [K in keyof Target]: Target[K] extends (...args: any) => any ? (context: Context, ...args: Parameters) => ReturnType : Target[K] +}; + interface ISimpleChannelProxyContext { __$simpleIPCContextMarker: boolean; proxyContext: unknown; @@ -44,12 +49,14 @@ export class SimpleServiceProxyChannel implements IServerChannel { throw new Error(`Events are currently unsupported by SimpleServiceProxyChannel: ${event}`); } - call(_: unknown, command: string, args: any[]): Promise { + call(_: unknown, command: string, args?: any[]): Promise { const target = this.service[command]; if (typeof target === 'function') { - const context = deserializeContext(args[0]); - if (context) { - args[0] = context; + if (Array.isArray(args)) { + const context = deserializeContext(args[0]); + if (context) { + args[0] = context; + } } return target.apply(this.service, args); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 1ec824b5e695088933c02616999b5ae65023ff49..dfb3286bccdc259e67f4ed25bb2e22f480eae245 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -10,7 +10,6 @@ import { IProcessEnvironment, isMacintosh, isLinux, isWeb } from 'vs/base/common import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; -import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ExportData } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; @@ -21,7 +20,6 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async export const IWindowsService = createDecorator('windowsService'); export interface INativeOpenDialogOptions { - windowId?: number; forceNewWindow?: boolean; defaultPath?: string; @@ -35,16 +33,6 @@ export interface IEnterWorkspaceResult { backupPath?: string; } -export interface CrashReporterStartOptions { - companyName?: string; - submitURL: string; - productName?: string; - uploadToServer?: boolean; - ignoreSystemCrashHandler?: boolean; - extra?: any; - crashesDirectory?: string; -} - export interface OpenDialogOptions { title?: string; defaultPath?: string; @@ -94,7 +82,6 @@ export interface IWindowsService { readonly onWindowUnmaximize: Event; readonly onRecentlyOpenedChange: Event; - closeWorkspace(windowId: number): Promise; enterWorkspace(windowId: number, path: URI): Promise; addRecentlyOpened(recents: IRecent[]): Promise; removeFromRecentlyOpened(paths: URI[]): Promise; @@ -107,36 +94,11 @@ export interface IWindowsService { maximizeWindow(windowId: number): Promise; unmaximizeWindow(windowId: number): Promise; minimizeWindow(windowId: number): Promise; - onWindowTitleDoubleClick(windowId: number): Promise; - quit(): Promise; - - // macOS Native Tabs - newWindowTab(): Promise; - showPreviousWindowTab(): Promise; - showNextWindowTab(): Promise; - moveWindowTabToNewWindow(): Promise; - mergeAllWindowTabs(): Promise; - toggleWindowTabsBar(): Promise; - - // macOS TouchBar - updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise; - - // Shared process - whenSharedProcessReady(): Promise; - toggleSharedProcess(): Promise; // Global methods openWindow(windowId: number, uris: IURIToOpen[], options: IOpenSettings): Promise; - openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise; getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getActiveWindowId(): Promise; - - // This needs to be handled from browser process to prevent - // foreground ordering issues on Windows - openExternal(url: string): Promise; - - // TODO: this is a bit backwards - startCrashReporter(config: CrashReporterStartOptions): Promise; } export const IWindowService = createDecorator('windowService'); @@ -193,8 +155,6 @@ export interface IWindowService { readonly windowId: number; - closeWorkspace(): Promise; - updateTouchBar(items: ISerializableCommandAction[][]): Promise; enterWorkspace(path: URI): Promise; getRecentlyOpened(): Promise; addRecentlyOpened(recents: IRecent[]): Promise; @@ -207,7 +167,6 @@ export interface IWindowService { maximizeWindow(): Promise; unmaximizeWindow(): Promise; minimizeWindow(): Promise; - onWindowTitleDoubleClick(): Promise; } export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden'; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 1e614810fbcc1cea8a8409968131055307db2d67..f35759cf71438080e78679447e180d77bc6ba1b8 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -43,7 +43,6 @@ export class WindowsChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { - case 'closeWorkspace': return this.service.closeWorkspace(arg); case 'enterWorkspace': return this.service.enterWorkspace(arg[0], URI.revive(arg[1])); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg.map((recent: IRecent) => { if (isRecentFile(recent)) { @@ -57,13 +56,6 @@ export class WindowsChannel implements IServerChannel { })); case 'removeFromRecentlyOpened': return this.service.removeFromRecentlyOpened(arg.map(URI.revive)); case 'clearRecentlyOpened': return this.service.clearRecentlyOpened(); - case 'newWindowTab': return this.service.newWindowTab(); - case 'showPreviousWindowTab': return this.service.showPreviousWindowTab(); - case 'showNextWindowTab': return this.service.showNextWindowTab(); - case 'moveWindowTabToNewWindow': return this.service.moveWindowTabToNewWindow(); - case 'mergeAllWindowTabs': return this.service.mergeAllWindowTabs(); - case 'toggleWindowTabsBar': return this.service.toggleWindowTabsBar(); - case 'updateTouchBar': return this.service.updateTouchBar(arg[0], arg[1]); case 'getRecentlyOpened': return this.service.getRecentlyOpened(arg); case 'focusWindow': return this.service.focusWindow(arg); case 'closeWindow': return this.service.closeWindow(arg); @@ -72,7 +64,6 @@ export class WindowsChannel implements IServerChannel { case 'maximizeWindow': return this.service.maximizeWindow(arg); case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg); case 'minimizeWindow': return this.service.minimizeWindow(arg); - case 'onWindowTitleDoubleClick': return this.service.onWindowTitleDoubleClick(arg); case 'openWindow': { const urisToOpen: IURIToOpen[] = arg[1]; const options: IOpenSettings = arg[2]; @@ -88,14 +79,9 @@ export class WindowsChannel implements IServerChannel { options.waitMarkerFileURI = options.waitMarkerFileURI && URI.revive(options.waitMarkerFileURI); return this.service.openWindow(arg[0], urisToOpen, options); } - case 'openExtensionDevelopmentHostWindow': return this.service.openExtensionDevelopmentHostWindow(arg[0], arg[1]); + case 'openExtensionDevelopmentHostWindow': return (this.service as any).openExtensionDevelopmentHostWindow(arg[0], arg[1]); // TODO@Isidor move case 'getWindows': return this.service.getWindows(); - case 'whenSharedProcessReady': return this.service.whenSharedProcessReady(); - case 'toggleSharedProcess': return this.service.toggleSharedProcess(); - case 'quit': return this.service.quit(); case 'getActiveWindowId': return this.service.getActiveWindowId(); - case 'openExternal': return this.service.openExternal(arg); - case 'startCrashReporter': return this.service.startCrashReporter(arg); } throw new Error(`Call not found: ${command}`); diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index 1c7055f11098330602657ae934ca4c63587c0f3e..767684f248c28351af9719b7d35a315ef40b1404 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -5,10 +5,9 @@ import { Event } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IWindowsService, IEnterWorkspaceResult, CrashReporterStartOptions, IURIToOpen, IOpenSettings } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IEnterWorkspaceResult, IURIToOpen, IOpenSettings } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentWorkspace } from 'vs/platform/history/common/history'; -import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; @@ -31,10 +30,6 @@ export class WindowsService implements IWindowsService { this.channel = mainProcessService.getChannel('windows'); } - closeWorkspace(windowId: number): Promise { - return this.channel.call('closeWorkspace', windowId); - } - async enterWorkspace(windowId: number, path: URI): Promise { const result: IEnterWorkspaceResult = await this.channel.call('enterWorkspace', [windowId, path]); if (result) { @@ -64,30 +59,6 @@ export class WindowsService implements IWindowsService { return recentlyOpened; } - newWindowTab(): Promise { - return this.channel.call('newWindowTab'); - } - - showPreviousWindowTab(): Promise { - return this.channel.call('showPreviousWindowTab'); - } - - showNextWindowTab(): Promise { - return this.channel.call('showNextWindowTab'); - } - - moveWindowTabToNewWindow(): Promise { - return this.channel.call('moveWindowTabToNewWindow'); - } - - mergeAllWindowTabs(): Promise { - return this.channel.call('mergeAllWindowTabs'); - } - - toggleWindowTabsBar(): Promise { - return this.channel.call('toggleWindowTabsBar'); - } - focusWindow(windowId: number): Promise { return this.channel.call('focusWindow', windowId); } @@ -116,22 +87,6 @@ export class WindowsService implements IWindowsService { return this.channel.call('minimizeWindow', windowId); } - onWindowTitleDoubleClick(windowId: number): Promise { - return this.channel.call('onWindowTitleDoubleClick', windowId); - } - - quit(): Promise { - return this.channel.call('quit'); - } - - whenSharedProcessReady(): Promise { - return this.channel.call('whenSharedProcessReady'); - } - - toggleSharedProcess(): Promise { - return this.channel.call('toggleSharedProcess'); - } - openWindow(windowId: number, uris: IURIToOpen[], options: IOpenSettings): Promise { return this.channel.call('openWindow', [windowId, uris, options]); } @@ -165,16 +120,4 @@ export class WindowsService implements IWindowsService { getActiveWindowId(): Promise { return this.channel.call('getActiveWindowId'); } - - openExternal(url: string): Promise { - return this.channel.call('openExternal', url); - } - - startCrashReporter(config: CrashReporterStartOptions): Promise { - return this.channel.call('startCrashReporter', config); - } - - updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { - return this.channel.call('updateTouchBar', [windowId, items]); - } } diff --git a/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts b/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts index 83ad0e1bd88d05d4820be891ff065fe5a3e82a68..34039fbcbd4f9e491074d45525a1efeef5385b60 100644 --- a/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts +++ b/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts @@ -8,14 +8,13 @@ import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IWindowsService, OpenContext, IEnterWorkspaceResult, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { crashReporter, app, Menu, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue, CrashReporterStartOptions, BrowserWindow, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions } from 'electron'; +import { app, MessageBoxReturnValue, SaveDialogReturnValue, OpenDialogReturnValue, BrowserWindow, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions } from 'electron'; import { Event } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { IWindowsMainService, ISharedProcess, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { Schemas } from 'vs/base/common/network'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; @@ -41,7 +40,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ readonly onRecentlyOpenedChange: Event = this.historyMainService.onRecentlyOpenedChange; constructor( - private sharedProcess: ISharedProcess, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IURLService urlService: IURLService, @@ -75,18 +73,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ return this.withWindow(windowId, codeWindow => this.windowsMainService.showOpenDialog(options, codeWindow), () => this.windowsMainService.showOpenDialog(options))!; } - async updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { - this.logService.trace('windowsService#updateTouchBar', windowId); - - return this.withWindow(windowId, codeWindow => codeWindow.updateTouchBar(items)); - } - - async closeWorkspace(windowId: number): Promise { - this.logService.trace('windowsService#closeWorkspace', windowId); - - return this.withWindow(windowId, codeWindow => this.windowsMainService.closeWorkspace(codeWindow)); - } - async enterWorkspace(windowId: number, path: URI): Promise { this.logService.trace('windowsService#enterWorkspace', windowId); @@ -116,42 +102,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ return this.withWindow(windowId, codeWindow => this.historyMainService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyMainService.getRecentlyOpened())!; } - async newWindowTab(): Promise { - this.logService.trace('windowsService#newWindowTab'); - - this.windowsMainService.openNewTabbedWindow(OpenContext.API); - } - - async showPreviousWindowTab(): Promise { - this.logService.trace('windowsService#showPreviousWindowTab'); - - Menu.sendActionToFirstResponder('selectPreviousTab:'); - } - - async showNextWindowTab(): Promise { - this.logService.trace('windowsService#showNextWindowTab'); - - Menu.sendActionToFirstResponder('selectNextTab:'); - } - - async moveWindowTabToNewWindow(): Promise { - this.logService.trace('windowsService#moveWindowTabToNewWindow'); - - Menu.sendActionToFirstResponder('moveTabToNewWindow:'); - } - - async mergeAllWindowTabs(): Promise { - this.logService.trace('windowsService#mergeAllWindowTabs'); - - Menu.sendActionToFirstResponder('mergeAllWindows:'); - } - - async toggleWindowTabsBar(): Promise { - this.logService.trace('windowsService#toggleWindowTabsBar'); - - Menu.sendActionToFirstResponder('toggleTabBar:'); - } - async focusWindow(windowId: number): Promise { this.logService.trace('windowsService#focusWindow', windowId); @@ -198,12 +148,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ return this.withWindow(windowId, codeWindow => codeWindow.win.minimize()); } - async onWindowTitleDoubleClick(windowId: number): Promise { - this.logService.trace('windowsService#onWindowTitleDoubleClick', windowId); - - return this.withWindow(windowId, codeWindow => codeWindow.onWindowTitleDoubleClick()); - } - async openWindow(windowId: number, urisToOpen: IURIToOpen[], options: IOpenSettings): Promise { this.logService.trace('windowsService#openWindow'); if (!urisToOpen || !urisToOpen.length) { @@ -262,35 +206,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ return this._activeWindowId; } - async openExternal(url: string): Promise { - return this.windowsMainService.openExternal(url); - } - - async startCrashReporter(config: CrashReporterStartOptions): Promise { - this.logService.trace('windowsService#startCrashReporter'); - - crashReporter.start(config); - } - - async quit(): Promise { - this.logService.trace('windowsService#quit'); - - this.windowsMainService.quit(); - } - - async whenSharedProcessReady(): Promise { - this.logService.trace('windowsService#whenSharedProcessReady'); - - return this.sharedProcess.whenReady(); - } - - async toggleSharedProcess(): Promise { - this.logService.trace('windowsService#toggleSharedProcess'); - - this.sharedProcess.toggle(); - - } - async handleURL(uri: URI): Promise { // Catch file URLs diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 5f4e32873563ba4e75d633cdcc7be18f94bb64e2..c3179f7bb88fc1adc337fa49d5279eacd68a9f02 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -67,7 +67,7 @@ export interface ICodeWindow { hasHiddenTitleBarStyle(): boolean; setRepresentedFilename(name: string): void; getRepresentedFilename(): string; - onWindowTitleDoubleClick(): void; + handleTitleDoubleClick(): void; updateTouchBar(items: ISerializableCommandAction[][]): void; @@ -98,10 +98,10 @@ export interface IWindowsMainService { closeWorkspace(win: ICodeWindow): void; open(openConfig: IOpenConfiguration): ICodeWindow[]; openExtensionDevelopmentHostWindow(extensionDevelopmentPath: string[], openConfig: IOpenConfiguration): void; - pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise; - pickFolderAndOpen(options: INativeOpenDialogOptions): Promise; - pickFileAndOpen(options: INativeOpenDialogOptions): Promise; - pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise; + pickFileFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; + pickFolderAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; + pickFileAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; + pickWorkspaceAndOpen(options: INativeOpenDialogOptions, win?: ICodeWindow): Promise; showMessageBox(options: MessageBoxOptions, win?: ICodeWindow): Promise; showSaveDialog(options: SaveDialogOptions, win?: ICodeWindow): Promise; showOpenDialog(options: OpenDialogOptions, win?: ICodeWindow): Promise; diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 49a07317c43b60a11c40ba08b631dd1b33e5b3e9..8bfd8f54b5d517410a0d4eff80e58c7f2c93a5e0 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -13,10 +13,14 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { WorkbenchStateContext, SupportsWorkspacesContext } from 'vs/workbench/browser/contextkeys'; +import { WorkbenchStateContext, SupportsWorkspacesContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export class OpenFileAction extends Action { @@ -90,6 +94,32 @@ export class OpenWorkspaceAction extends Action { } } +export class CloseWorkspaceAction extends Action { + + static readonly ID = 'workbench.action.closeFolder'; + static LABEL = nls.localize('closeWorkspace', "Close Workspace"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @INotificationService private readonly notificationService: INotificationService, + @IHostService private readonly hostService: IHostService + ) { + super(id, label); + } + + run(): Promise { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); + + return Promise.resolve(undefined); + } + + return this.hostService.closeWorkspace(); + } +} + export class OpenWorkspaceConfigFileAction extends Action { static readonly ID = 'workbench.action.openWorkspaceConfigFile'; @@ -170,6 +200,7 @@ const workspacesCategory = nls.localize('workspaces', "Workspaces"); registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory, SupportsWorkspacesContext); +registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', workspacesCategory, SupportsWorkspacesContext); // --- Menu Registration @@ -194,3 +225,24 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { }, when: WorkbenchStateContext.isEqualTo('workspace') }); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseWorkspaceAction.ID, + title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), + precondition: WorkspaceFolderCountContext.notEqualsTo('0') + }, + order: 3, + when: WorkbenchStateContext.notEqualsTo('workspace') +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseWorkspaceAction.ID, + title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") + }, + order: 3, + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), SupportsWorkspacesContext) +}); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 9147246f1d1dea1b5b137532ae7f3c06a84a33f6..7a5f84cb89875372cbca3643e7f4f05cbbcf36ac 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -346,15 +346,6 @@ export class TitlebarPart extends Part implements ITitleService { this.titleUpdater.schedule(); } - // Maximize/Restore on doubleclick - if (isMacintosh && !isWeb) { - this._register(addDisposableListener(this.element, EventType.DBLCLICK, e => { - EventHelper.stop(e); - - this.onTitleDoubleclick(); - })); - } - // Context menu on title [EventType.CONTEXT_MENU, EventType.MOUSE_DOWN].forEach(event => { this._register(addDisposableListener(this.title, event, e => { @@ -405,7 +396,7 @@ export class TitlebarPart extends Part implements ITitleService { const isMaximized = this.environmentService.configuration.maximized ? true : false; this.onDidChangeMaximized(isMaximized); - this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this); + this._register(this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this)); } // Since the title area is used to drag the window, we do not want to steal focus from the @@ -477,10 +468,6 @@ export class TitlebarPart extends Part implements ITitleService { } } - private onTitleDoubleclick(): void { - this.windowService.onWindowTitleDoubleClick(); - } - private onUpdateAppIconDragBehavior() { const setting = this.configurationService.getValue('window.doubleClickIconToClose'); if (setting) { diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 340ceb5806ca40615b2dbb500da872d325f1ddeb..535d9d448b9e503c5344890207bb7d0e2e507b73 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -10,18 +10,15 @@ import { Event } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IWindowService, IEnterWorkspaceResult, IURIToOpen, IWindowsService, IOpenSettings, IWindowSettings, CrashReporterStartOptions } from 'vs/platform/windows/common/windows'; +import { IWindowService, IEnterWorkspaceResult, IURIToOpen, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/common/history'; -import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { addDisposableListener, EventType, windowOpenNoOpener } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/editorService'; import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { IProcessEnvironment } from 'vs/base/common/platform'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; //#region Window @@ -91,10 +88,6 @@ export class SimpleWindowService extends Disposable implements IWindowService { return Promise.resolve(false); } - closeWorkspace(): Promise { - return Promise.resolve(); - } - enterWorkspace(_path: URI): Promise { return Promise.resolve(undefined); } @@ -208,14 +201,6 @@ export class SimpleWindowService extends Disposable implements IWindowService { return Promise.resolve(); } - - onWindowTitleDoubleClick(): Promise { - return Promise.resolve(); - } - - updateTouchBar(_items: ISerializableCommandAction[][]): Promise { - return Promise.resolve(); - } } registerSingleton(IWindowService, SimpleWindowService); @@ -238,10 +223,6 @@ export class SimpleWindowsService implements IWindowsService { return Promise.resolve(true); } - closeWorkspace(_windowId: number): Promise { - return Promise.resolve(); - } - enterWorkspace(_windowId: number, _path: URI): Promise { return Promise.resolve(undefined); } @@ -289,79 +270,18 @@ export class SimpleWindowsService implements IWindowsService { return Promise.resolve(); } - onWindowTitleDoubleClick(_windowId: number): Promise { - return Promise.resolve(); - } - - quit(): Promise { - return Promise.resolve(); - } - - whenSharedProcessReady(): Promise { - return Promise.resolve(); - } - - toggleSharedProcess(): Promise { - return Promise.resolve(); - } - // Global methods openWindow(_windowId: number, _uris: IURIToOpen[], _options: IOpenSettings): Promise { return Promise.resolve(); } - openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { - return Promise.resolve(); - } - getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { return Promise.resolve([]); } - newWindowTab(): Promise { - return Promise.resolve(); - } - - showPreviousWindowTab(): Promise { - return Promise.resolve(); - } - - showNextWindowTab(): Promise { - return Promise.resolve(); - } - - moveWindowTabToNewWindow(): Promise { - return Promise.resolve(); - } - - mergeAllWindowTabs(): Promise { - return Promise.resolve(); - } - - toggleWindowTabsBar(): Promise { - return Promise.resolve(); - } - - updateTouchBar(_windowId: number, _items: ISerializableCommandAction[][]): Promise { - return Promise.resolve(); - } - getActiveWindowId(): Promise { return Promise.resolve(0); } - - // This needs to be handled from browser process to prevent - // foreground ordering issues on Windows - openExternal(_url: string): Promise { - windowOpenNoOpener(_url); - - return Promise.resolve(true); - } - - // TODO: this is a bit backwards - startCrashReporter(_config: CrashReporterStartOptions): Promise { - return Promise.resolve(); - } } registerSingleton(IWindowsService, SimpleWindowsService); diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-disabled.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-disabled.svg index faf015508c637024d220e0133016c0e2ae65e8e1..ea246058e0f6a1f2fcbaa54a233eaba7ec387f79 100644 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-disabled.svg +++ b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-disabled.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-unverified.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-unverified.svg index f83544babf69adc0d0b863e1b1e9f4671c9b7a81..ae8ed0ba7b69665efb6e53e0397b35498bb2f678 100644 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-unverified.svg +++ b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log-unverified.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log.svg index 509e255b91e00cd8aa2f04da26803477a4b594d2..fc72afc7e2b3e2bc2adeece731185c3f378b5549 100644 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-log.svg +++ b/src/vs/workbench/contrib/debug/browser/media/breakpoint-log.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/debug/browser/media/breakpoint-unsupported.svg b/src/vs/workbench/contrib/debug/browser/media/breakpoint-unsupported.svg index 4a40c0fd1e4de3076a40355b687b57a401cf3f46..624b9f60c80da7fbbae062ead9021c0eb35c17e5 100644 --- a/src/vs/workbench/contrib/debug/browser/media/breakpoint-unsupported.svg +++ b/src/vs/workbench/contrib/debug/browser/media/breakpoint-unsupported.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts index dad63fd4fb0e1bdf36bf2227e4c3cbe309adb2d9..d3c1b8057665497f0bf4571117bbc97a6815a5e4 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts @@ -10,6 +10,7 @@ import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } f import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient { @@ -22,7 +23,7 @@ export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient { openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { // TODO@Isidor use debug IPC channel - return this.windowsService.openExtensionDevelopmentHostWindow(args, env); + return (this.windowsService as WindowsService).openExtensionDevelopmentHostWindow(args, env); } } diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 14b90229a2298bdd44d59e72f6747e1bd9633056..0072552bf87cef588dbd075a7b591c345ce4ea20 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -23,12 +23,14 @@ const enum ForceOpenAs { Text, Binary } -const memoizer = createMemoizer(); /** * A file editor input is the input type for the file editor of file system resources. */ export class FileEditorInput extends EditorInput implements IFileEditorInput { + + private static readonly MEMOIZER = createMemoizer(); + private preferredEncoding: string; private preferredMode: string; @@ -69,7 +71,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { this._register(this.textFileService.models.onModelSaved(e => this.onDirtyStateChange(e))); this._register(this.textFileService.models.onModelReverted(e => this.onDirtyStateChange(e))); this._register(this.textFileService.models.onModelOrphanedChanged(e => this.onModelOrphanedChanged(e))); - this._register(this.labelService.onDidChangeFormatters(() => memoizer.clear())); + this._register(this.labelService.onDidChangeFormatters(() => FileEditorInput.MEMOIZER.clear())); } private onDirtyStateChange(e: TextFileModelChangeEvent): void { @@ -145,22 +147,22 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { return FILE_EDITOR_INPUT_ID; } - @memoizer + @FileEditorInput.MEMOIZER getName(): string { return this.decorateLabel(this.labelService.getUriBasenameLabel(this.resource)); } - @memoizer + @FileEditorInput.MEMOIZER private get shortDescription(): string { return this.labelService.getUriBasenameLabel(dirname(this.resource)); } - @memoizer + @FileEditorInput.MEMOIZER private get mediumDescription(): string { return this.labelService.getUriLabel(dirname(this.resource), { relative: true }); } - @memoizer + @FileEditorInput.MEMOIZER private get longDescription(): string { return this.labelService.getUriLabel(dirname(this.resource)); } @@ -177,17 +179,17 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } } - @memoizer + @FileEditorInput.MEMOIZER private get shortTitle(): string { return this.getName(); } - @memoizer + @FileEditorInput.MEMOIZER private get mediumTitle(): string { return this.labelService.getUriLabel(this.resource, { relative: true }); } - @memoizer + @FileEditorInput.MEMOIZER private get longTitle(): string { return this.labelService.getUriLabel(this.resource); } diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index e0c7f7f6bd2d44f97074a062dcc536306a4f8807..2a945fc8e68c58d21d62bcc0a806b76ff0df79e1 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -13,7 +13,7 @@ import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lif import product from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService } from 'vs/platform/update/common/update'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IElectronService } from 'vs/platform/electron/node/electron'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import * as files from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -27,7 +27,7 @@ export class StartupTimings implements IWorkbenchContribution { constructor( @ITimerService private readonly _timerService: ITimerService, - @IWindowsService private readonly _windowsService: IWindowsService, + @IElectronService private readonly _electronService: IElectronService, @IEditorService private readonly _editorService: IEditorService, @IViewletService private readonly _viewletService: IViewletService, @IPanelService private readonly _panelService: IPanelService, @@ -76,10 +76,10 @@ export class StartupTimings implements IWorkbenchContribution { ]).then(([startupMetrics]) => { return promisify(appendFile)(appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${(product.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`); }).then(() => { - this._windowsService.quit(); + this._electronService.quit(); }).catch(err => { console.error(err); - this._windowsService.quit(); + this._electronService.quit(); }); } diff --git a/src/vs/workbench/contrib/preferences/browser/media/preferences.css b/src/vs/workbench/contrib/preferences/browser/media/preferences.css index 18a8d426dacf355948a25808e934d0accc7e84e4..c9feed2905588632c32617aa9f7a17e1a1eaf39a 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/preferences.css +++ b/src/vs/workbench/contrib/preferences/browser/media/preferences.css @@ -3,6 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.preferences-editor { + display: flex; + flex-direction: column; +} + .preferences-editor > .preferences-header { padding-left: 27px; padding-right: 32px; @@ -11,7 +16,7 @@ } .preferences-editor > .preferences-editors-container.side-by-side-preferences-editor { - position: relative; + flex: 1; } .preferences-editor > .preferences-editors-container.side-by-side-preferences-editor .preferences-header-container { @@ -245,4 +250,4 @@ padding: 10px; border-radius: 5px; cursor: pointer; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 86750798cd7b32f2005c936ef43b111f611e827a..d2f8c0bcd5f78dfdc1b1447c0a9bb8dd55482742 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -12,13 +12,13 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; @@ -39,8 +39,8 @@ import { RemoteConnectionState, Deprecated_RemoteAuthorityContext, RemoteFileDia import { IDownloadService } from 'vs/platform/download/common/download'; import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/simpleFileDialog'; -const WINDOW_ACTIONS_COMMAND_ID = 'remote.showActions'; -const CLOSE_REMOTE_COMMAND_ID = 'remote.closeRemote'; +const WINDOW_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; +const CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenchContribution { @@ -68,13 +68,32 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc this.windowCommandMenu = this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService); this._register(this.windowCommandMenu); - this._register(CommandsRegistry.registerCommand(WINDOW_ACTIONS_COMMAND_ID, _ => this.showIndicatorActions(this.windowCommandMenu))); - this._register(CommandsRegistry.registerCommand(CLOSE_REMOTE_COMMAND_ID, _ => this.remoteAuthority && hostService.openEmptyWindow({ reuse: true }))); + const category = nls.localize('remote.category', "Remote"); + + registerAction({ + id: WINDOW_ACTIONS_COMMAND_ID, + category, + title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, + menu: { + menuId: MenuId.CommandPalette + }, + handler: (_accessor) => this.showIndicatorActions(this.windowCommandMenu) + }); this.remoteAuthority = environmentService.configuration.remoteAuthority; Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(this.remoteAuthority || ''); if (this.remoteAuthority) { + registerAction({ + id: CLOSE_REMOTE_COMMAND_ID, + category, + title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, + menu: { + menuId: MenuId.CommandPalette + }, + handler: (_accessor) => this.remoteAuthority && hostService.openEmptyWindow({ reuse: true }) + }); + // Pending entry until extensions are ready this.renderWindowIndicator(nls.localize('host.open', "$(sync~spin) Opening Remote..."), undefined, WINDOW_ACTIONS_COMMAND_ID); this.connectionState = 'initializing'; @@ -305,6 +324,7 @@ class RemoteTelemetryEnablementUpdater extends Disposable implements IWorkbenchC } } + class RemoteEmptyWorkbenchPresentation extends Disposable implements IWorkbenchContribution { constructor( @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts index 6d6dadb15513dfed0287435f46aaa266e0f5c372..c857ad870ad612cc7285a888ac1e2e2453263a16 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPanel.ts @@ -493,7 +493,7 @@ class ViewModel { export class ToggleViewModeAction extends Action { static readonly ID = 'workbench.scm.action.toggleViewMode'; - static readonly LABEL = localize('toggleViewMode', "ToggleViewMode"); + static readonly LABEL = localize('toggleViewMode', "Toggle View Mode"); constructor(private viewModel: ViewModel) { super(ToggleViewModeAction.ID, ToggleViewModeAction.LABEL); diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index d0e83e2e000f670331a8c181935b713d89cfddb2..56cc9ad1c0c687f04d297562f07752397f327a0a 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -1239,7 +1239,8 @@ namespace ConfigurationProperties { { property: 'name' }, { property: 'identifier' }, { property: 'group' }, { property: 'isBackground' }, { property: 'promptOnClose' }, { property: 'dependsOn' }, - { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' } + { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' }, + { property: 'options' } ]; export function from(this: void, external: ConfigurationProperties & { [key: string]: any; }, context: ParseContext, includeCommandOptions: boolean, properties?: IJSONSchemaMap): Tasks.ConfigurationProperties | undefined { diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index 996a7e141d1833b595acf2db15d8ed3526c28459..d9b50a3f494ea3ec47587416758ef619faf2d70e 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { Action } from 'vs/base/common/actions'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import { IElectronService } from 'vs/platform/electron/node/electron'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; export class ToggleDevToolsAction extends Action { @@ -27,11 +27,11 @@ export class ToggleSharedProcessAction extends Action { static readonly ID = 'workbench.action.toggleSharedProcess'; static LABEL = nls.localize('toggleSharedProcess', "Toggle Shared Process"); - constructor(id: string, label: string, @IWindowsService private readonly windowsService: IWindowsService) { + constructor(id: string, label: string, @ISharedProcessService private readonly sharedProcessService: ISharedProcessService) { super(id, label); } run(): Promise { - return this.windowsService.toggleSharedProcess(); + return this.sharedProcessService.toggleSharedProcessWindow(); } } diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 9d7a145fcdf723f26641340296360133ec5b6965..0ca6666189a951b18c328f5fe69ab8166883aac2 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -258,25 +258,25 @@ export class QuickSwitchWindow extends BaseSwitchWindow { } export const NewWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).newWindowTab(); + return accessor.get(IElectronService).newWindowTab(); }; export const ShowPreviousWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).showPreviousWindowTab(); + return accessor.get(IElectronService).showPreviousWindowTab(); }; export const ShowNextWindowTabHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).showNextWindowTab(); + return accessor.get(IElectronService).showNextWindowTab(); }; export const MoveWindowTabToNewWindowHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).moveWindowTabToNewWindow(); + return accessor.get(IElectronService).moveWindowTabToNewWindow(); }; export const MergeWindowTabsHandlerHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).mergeAllWindowTabs(); + return accessor.get(IElectronService).mergeAllWindowTabs(); }; export const ToggleWindowTabsBarHandler: ICommandHandler = function (accessor: ServicesAccessor) { - return accessor.get(IWindowsService).toggleWindowTabsBar(); + return accessor.get(IElectronService).toggleWindowTabsBar(); }; diff --git a/src/vs/workbench/electron-browser/actions/workspaceActions.ts b/src/vs/workbench/electron-browser/actions/workspaceActions.ts index c01580637dd6c181f8fe639f17114bcc0b3b8351..a3539a12ca5672a6565ebeda63e65cfdebc105e7 100644 --- a/src/vs/workbench/electron-browser/actions/workspaceActions.ts +++ b/src/vs/workbench/electron-browser/actions/workspaceActions.ts @@ -10,7 +10,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; export class SaveWorkspaceAsAction extends Action { @@ -69,29 +68,3 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true }); } } - -export class CloseWorkspaceAction extends Action { - - static readonly ID = 'workbench.action.closeFolder'; - static LABEL = nls.localize('closeWorkspace', "Close Workspace"); - - constructor( - id: string, - label: string, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - run(): Promise { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); - - return Promise.resolve(undefined); - } - - return this.windowService.closeWorkspace(); - } -} diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 6fd7e5f39666958576c42fccbcdd3ff5433e06aa..3148f3206bae6516b51a6928a3749413bb306747 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -9,30 +9,24 @@ import * as os from 'os'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { ToggleSharedProcessAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, QuickSwitchWindow, RestartWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; -import { SaveWorkspaceAsAction, DuplicateWorkspaceInNewWindowAction, CloseWorkspaceAction } from 'vs/workbench/electron-browser/actions/workspaceActions'; +import { SaveWorkspaceAsAction, DuplicateWorkspaceInNewWindowAction } from 'vs/workbench/electron-browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; +import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IElectronService } from 'vs/platform/electron/node/electron'; // Actions (function registerActions(): void { const registry = Registry.as(Extensions.WorkbenchActions); - // Actions: File - (function registerFileActions(): void { - const fileCategory = nls.localize('file', "File"); - - registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory, SupportsWorkspacesContext); - })(); - // Actions: View (function registerViewActions(): void { const viewCategory = nls.localize('view', "View"); @@ -63,8 +57,8 @@ import { IWindowService, IWindowsService } from 'vs/platform/windows/common/wind id: 'workbench.action.quit', weight: KeybindingWeight.WorkbenchContrib, handler(accessor: ServicesAccessor) { - const windowsService = accessor.get(IWindowsService); - windowsService.quit(); + const electronService = accessor.get(IElectronService); + electronService.quit(); }, when: undefined, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_Q }, @@ -130,27 +124,6 @@ import { IWindowService, IWindowsService } from 'vs/platform/windows/common/wind when: SupportsWorkspacesContext }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: CloseWorkspaceAction.ID, - title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), - precondition: WorkspaceFolderCountContext.notEqualsTo('0') - }, - order: 3, - when: WorkbenchStateContext.notEqualsTo('workspace') - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: CloseWorkspaceAction.ID, - title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") - }, - order: 3, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), SupportsWorkspacesContext) - }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 7231a7e2ecfdf8fdb3bdfeb90e99ebd9cd06ba7b..c028ae450a6f4b0317fd81ac5c170cb64d9e0de9 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -59,6 +59,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; import { posix, dirname } from 'vs/base/common/path'; import { getBaseLabel } from 'vs/base/common/labels'; import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel'; +import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -88,7 +89,6 @@ export class ElectronWindow extends Disposable { constructor( @IEditorService private readonly editorService: EditorServiceImpl, - @IWindowsService private readonly windowsService: IWindowsService, @IWindowService private readonly windowService: IWindowService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITitleService private readonly titleService: ITitleService, @@ -111,6 +111,7 @@ export class ElectronWindow extends Disposable { @IOpenerService private readonly openerService: IOpenerService, @IElectronService private readonly electronService: IElectronService, @ITunnelService private readonly tunnelService: ITunnelService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { super(); @@ -264,6 +265,17 @@ export class ElectronWindow extends Disposable { this.provideCustomTitleContextMenu(file ? file.fsPath : undefined); })); } + + // Maximize/Restore on doubleclick (for macOS custom title) + if (isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + const titlePart = this.layoutService.getContainer(Parts.TITLEBAR_PART); + + this._register(DOM.addDisposableListener(titlePart, DOM.EventType.DBLCLICK, e => { + DOM.EventHelper.stop(e); + + this.electronService.handleTitleDoubleClick(); + })); + } } private onDidVisibleEditorsChange(): void { @@ -426,7 +438,7 @@ export class ElectronWindow extends Disposable { // the main process to prevent window focus issues. if (this.shouldOpenExternal(resource, options)) { const { resolved } = await this.openerService.resolveExternalUri(resource); - const success = await this.windowsService.openExternal(encodeURI(resolved.toString(true))); + const success = await this.electronService.openExternal(encodeURI(resolved.toString(true))); if (!success && resolved.scheme === Schemas.file) { // if opening failed, and this is a file, we can still try to reveal it await this.electronService.showItemInFolder(resolved.fsPath); @@ -526,7 +538,7 @@ export class ElectronWindow extends Disposable { // Only update if the actions have changed if (!equals(this.lastInstalledTouchedBar, items)) { this.lastInstalledTouchedBar = items; - this.windowService.updateTouchBar(items); + this.electronService.updateTouchBar(items); } } @@ -554,7 +566,7 @@ export class ElectronWindow extends Disposable { crashReporter.start(deepClone(options)); // start crash reporter in the main process - return this.windowsService.startCrashReporter(options); + return this.electronService.startCrashReporter(options); } private onAddFoldersRequest(request: IAddFoldersRequest): void { diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index f12caabd2f65a8c7a23f2238e71f8631a413f864..fef71bd611e6b67565a674eb135cc0d1a4e03d7d 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -70,6 +70,10 @@ export class BrowserHostService implements IHostService { async reload(): Promise { window.location.reload(); } + + async closeWorkspace(): Promise { + return this.openEmptyWindow({ reuse: true }); + } } registerSingleton(IHostService, BrowserHostService, true); diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index 824ffdb4e001f723ef465ea7ad8ee1e4cbceb7dc..568f0c638c2cdad53db0d2e6e5f32c733067442e 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -24,14 +24,30 @@ export interface IHostService { */ openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise; + /** + * Switch between fullscreen and normal window. + */ toggleFullScreen(): Promise; //#endregion //#region Lifecycle + /** + * Restart the entire application. + */ restart(): Promise; + + /** + * Reload the currently active window. + */ reload(): Promise; + /** + * Closes the currently opened folder/workspace and returns to an empty + * window. + */ + closeWorkspace(): Promise; + //#endregion } diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts index 1e79f713326d0ff922d0869a8326a8ea0947e06c..e8a46cfb15f80f3682894a2b56d0c397db332c50 100644 --- a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts @@ -34,6 +34,10 @@ export class DesktopHostService implements IHostService { reload(): Promise { return this.electronService.reload(); } + + closeWorkspace(): Promise { + return this.electronService.closeWorkpsace(); + } } registerSingleton(IHostService, DesktopHostService, true); diff --git a/src/vs/workbench/services/window/electron-browser/windowService.ts b/src/vs/workbench/services/window/electron-browser/windowService.ts index b6cfde45e1db524cfb2bc2476aaaf677b1a65467..672a01a85e21a82f68a18c6d25f94fab0194da35 100644 --- a/src/vs/workbench/services/window/electron-browser/windowService.ts +++ b/src/vs/workbench/services/window/electron-browser/windowService.ts @@ -6,7 +6,6 @@ import { Event } from 'vs/base/common/event'; import { IWindowService, IWindowsService, IEnterWorkspaceResult, IOpenSettings, IURIToOpen, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; -import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -52,10 +51,6 @@ export class WindowService extends Disposable implements IWindowService { return this._windowId; } - closeWorkspace(): Promise { - return this.windowsService.closeWorkspace(this.windowId); - } - enterWorkspace(path: URI): Promise { return this.windowsService.enterWorkspace(this.windowId, path); } @@ -108,14 +103,6 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.minimizeWindow(this.windowId); } - onWindowTitleDoubleClick(): Promise { - return this.windowsService.onWindowTitleDoubleClick(this.windowId); - } - - updateTouchBar(items: ISerializableCommandAction[][]): Promise { - return this.windowsService.updateTouchBar(this.windowId, items); - } - private getRecentLabel(u: IURIToOpen): string { if (isFolderToOpen(u)) { return this.labelService.getWorkspaceLabel(u.folderUri, { verbose: true }); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 0cacc4e1176aec03cfb15a35988fa0dd696169e7..5b4e60758963005a0ef6136dd7d8a12e18a230e0 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -35,17 +35,17 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IWindowsService, IWindowService, IEnterWorkspaceResult, MenuBarVisibility, IURIToOpen, IOpenSettings, IWindowConfiguration, CrashReporterStartOptions } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, IEnterWorkspaceResult, MenuBarVisibility, IURIToOpen, IOpenSettings, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position'; -import { IMenuService, MenuId, IMenu, ISerializableCommandAction } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, IModelDecorationOptions, ITextModel, ITextSnapshot } from 'vs/editor/common/model'; @@ -71,7 +71,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletDescriptor, Viewlet } from 'vs/workbench/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; -import { isLinux, isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; import { Part } from 'vs/workbench/browser/part'; @@ -1196,10 +1196,6 @@ export class TestWindowService implements IWindowService { return Promise.resolve(false); } - closeWorkspace(): Promise { - return Promise.resolve(); - } - enterWorkspace(_path: URI): Promise { return Promise.resolve(undefined); } @@ -1242,14 +1238,6 @@ export class TestWindowService implements IWindowService { closeWindow(): Promise { return Promise.resolve(); } - - onWindowTitleDoubleClick(): Promise { - return Promise.resolve(); - } - - updateTouchBar(_items: ISerializableCommandAction[][]): Promise { - return Promise.resolve(); - } } export class TestLifecycleService implements ILifecycleService { @@ -1306,10 +1294,6 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(false); } - closeWorkspace(_windowId: number): Promise { - return Promise.resolve(); - } - enterWorkspace(_windowId: number, _path: URI): Promise { return Promise.resolve(undefined); } @@ -1357,77 +1341,18 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } - onWindowTitleDoubleClick(_windowId: number): Promise { - return Promise.resolve(); - } - - quit(): Promise { - return Promise.resolve(); - } - - whenSharedProcessReady(): Promise { - return Promise.resolve(); - } - - toggleSharedProcess(): Promise { - return Promise.resolve(); - } - // Global methods openWindow(_windowId: number, _uris: IURIToOpen[], _options: IOpenSettings): Promise { return Promise.resolve(); } - openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { - return Promise.resolve(); - } - getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { throw new Error('not implemented'); } - newWindowTab(): Promise { - return Promise.resolve(); - } - - showPreviousWindowTab(): Promise { - return Promise.resolve(); - } - - showNextWindowTab(): Promise { - return Promise.resolve(); - } - - moveWindowTabToNewWindow(): Promise { - return Promise.resolve(); - } - - mergeAllWindowTabs(): Promise { - return Promise.resolve(); - } - - toggleWindowTabsBar(): Promise { - return Promise.resolve(); - } - - updateTouchBar(_windowId: number, _items: ISerializableCommandAction[][]): Promise { - return Promise.resolve(); - } - getActiveWindowId(): Promise { return Promise.resolve(undefined); } - - // This needs to be handled from browser process to prevent - // foreground ordering issues on Windows - openExternal(_url: string): Promise { - return Promise.resolve(true); - } - - // TODO: this is a bit backwards - startCrashReporter(_config: CrashReporterStartOptions): Promise { - return Promise.resolve(); - } } export class TestTextResourceConfigurationService implements ITextResourceConfigurationService { @@ -1478,6 +1403,9 @@ export class TestSharedProcessService implements ISharedProcessService { } registerChannel(channelName: string, channel: any): void { } + + async toggleSharedProcessWindow(): Promise { } + async whenSharedProcessReady(): Promise { } } export class RemoteFileSystemProvider implements IFileSystemProvider { @@ -1517,10 +1445,11 @@ export class TestHostService implements IHostService { windowCount = Promise.resolve(1); - restart(): Promise { return Promise.resolve(); } - reload(): Promise { return Promise.resolve(); } + async restart(): Promise { } + async reload(): Promise { } + async closeWorkspace(): Promise { } - openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise { return Promise.resolve(); } + async openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise { } - toggleFullScreen(): Promise { return Promise.resolve(); } + async toggleFullScreen(): Promise { } }