From b68e2baac8051ca1aab0e605faac6faa883a656b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 23 Nov 2016 15:55:45 +0100 Subject: [PATCH] Consider introducing ctrl+q to close all windows on Linux and Window to compliment hot exit (fixes #15261) --- src/vs/code/electron-main/menus.ts | 32 ++++--------------- src/vs/code/electron-main/windows.ts | 18 +++++++++++ src/vs/platform/windows/common/windows.ts | 1 + src/vs/platform/windows/common/windowsIpc.ts | 6 ++++ .../windows/electron-main/windowsService.ts | 5 +++ .../electron-browser/main.contribution.ts | 13 ++++++++ 6 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 402ea0f8435..4f085b57da3 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -298,7 +298,7 @@ export class VSCodeMenu { const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' }); const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' }); - const quit = new MenuItem({ label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.quit(), accelerator: 'Command+Q' }); + const quit = new MenuItem({ label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.windowsService.quit(), accelerator: this.getAccelerator('workbench.action.quit', 'Command+Q') }); const actions = [about]; actions.push(...checkForUpdates); @@ -356,7 +356,7 @@ export class VSCodeMenu { const closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder'); const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "Close &&Editor"), 'workbench.action.closeActiveEditor'); - const exit = this.createMenuItem(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit"), () => this.quit()); + const exit = new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), accelerator: this.getAccelerator('workbench.action.quit'), click: () => this.windowsService.quit() }); arrays.coalesce([ newFile, @@ -408,25 +408,6 @@ export class VSCodeMenu { return new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); } - private quit(): void { - - // If the user selected to exit from an extension development host window, do not quit, but just - // close the window unless this is the last window that is opened. - const vscodeWindow = this.windowsService.getFocusedWindow(); - if (vscodeWindow && vscodeWindow.isPluginDevelopmentHost && this.windowsService.getWindowCount() > 1) { - vscodeWindow.win.close(); - } - - // Otherwise: normal quit - else { - setTimeout(() => { - this.isQuitting = true; - - app.quit(); - }, 10 /* delay this because there is an issue with quitting while the menu is open */); - } - } - private setOpenRecentMenu(openRecentMenu: Electron.Menu): void { openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor')); @@ -869,7 +850,7 @@ export class VSCodeMenu { }); } - private getAccelerator(actionId: string): string { + private getAccelerator(actionId: string, fallback?: string): string { if (actionId) { const resolvedKeybinding = this.mapResolvedKeybindingToActionId[actionId]; if (resolvedKeybinding) { @@ -881,11 +862,12 @@ export class VSCodeMenu { } const lastKnownKeybinding = this.mapLastKnownKeybindingToActionId[actionId]; - - return lastKnownKeybinding; // return the last known keybining (chance of mismatch is very low unless it changed) + if (lastKnownKeybinding) { + return lastKnownKeybinding; // return the last known keybining (chance of mismatch is very low unless it changed) + } } - return void (0); + return fallback; } private openAboutDialog(): void { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 3c1654720a7..4a836792785 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -119,6 +119,7 @@ export interface IWindowsMainService { removeFromRecentPathsList(paths: string[]): void; clearRecentPathsList(): void; toggleMenuBar(windowId: number): void; + quit(): void; } export class WindowsManager implements IWindowsMainService { @@ -1219,4 +1220,21 @@ export class WindowsManager implements IWindowsMainService { this.logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors } } + + public quit(): void { + + // If the user selected to exit from an extension development host window, do not quit, but just + // close the window unless this is the last window that is opened. + const vscodeWindow = this.getFocusedWindow(); + if (vscodeWindow && vscodeWindow.isPluginDevelopmentHost && this.getWindowCount() > 1) { + vscodeWindow.win.close(); + } + + // Otherwise: normal quit + else { + setTimeout(() => { + app.quit(); + }, 10 /* delay to unwind callback stack (IPC) */); + } + } } \ No newline at end of file diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index b2e0ce6b23e..1ad77ba3744 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -37,6 +37,7 @@ export interface IWindowsService { unmaximizeWindow(windowId: number): TPromise; setDocumentEdited(windowId: number, flag: boolean): TPromise; toggleMenuBar(windowId: number): TPromise; + quit(): TPromise; // Global methods // TODO@joao: rename, shouldn't this be openWindow? diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 9b24d363900..42369382626 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -30,6 +30,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'unmaximizeWindow', arg: number): TPromise; call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise; call(command: 'toggleMenuBar', arg: number): TPromise; + call(command: 'quit'): TPromise; call(command: 'windowOpen', arg: [string[], boolean]): TPromise; call(command: 'openNewWindow'): TPromise; call(command: 'showWindow', arg: number): TPromise; @@ -80,6 +81,7 @@ export class WindowsChannel implements IWindowsChannel { case 'showWindow': return this.service.showWindow(arg); case 'getWindows': return this.service.getWindows(); case 'getWindowCount': return this.service.getWindowCount(); + case 'quit': return this.service.quit(); case 'log': return this.service.log(arg[0], arg[1]); case 'closeExtensionHostWindow': return this.service.closeExtensionHostWindow(arg); case 'showItemInFolder': return this.service.showItemInFolder(arg); @@ -173,6 +175,10 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('toggleMenuBar', windowId); } + quit(): TPromise { + return this.channel.call('quit'); + } + windowOpen(paths: string[], forceNewWindow?: boolean): TPromise { return this.channel.call('windowOpen', [paths, forceNewWindow]); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index a2f664cd414..031fb1c254e 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -260,6 +260,11 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } + quit(): TPromise { + this.windowsMainService.quit(); + return TPromise.as(null); + } + private openFileForURI(filePath: string): TPromise { const cli = assign(Object.create(null), this.environmentService.args, { goto: true }); const pathsToOpen = [filePath]; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index a339661733d..b31e2a1b537 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -22,6 +22,7 @@ import { IWindowIPCService } from 'vs/workbench/services/window/electron-browser import { CloseEditorAction, KeybindingsReferenceAction, ReportIssueAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseFolderAction, CloseWindowAction, SwitchWindow, NewWindowAction, CloseMessagesAction } from 'vs/workbench/electron-browser/actions'; import { MessagesVisibleContext, NoEditorsVisibleContext, InZenModeContext } from 'vs/workbench/electron-browser/workbench'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; const closeEditorOrWindowKeybindings: IKeybindings = { primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] } }; @@ -87,6 +88,18 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ primary: KeyChord(KeyCode.Escape, KeyCode.Escape) }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.quit', + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + handler(accessor: ServicesAccessor) { + const windowsService = accessor.get(IWindowsService); + windowsService.quit(); + }, + when: void 0, + primary: KeyMod.CtrlCmd | KeyCode.KEY_Q, + win: { primary: KeyMod.Alt | KeyCode.F4 } +}); + // Configuration: Workbench const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ -- GitLab