diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 23bf92d34f395b148d415a6770594dc6990be322..93a8260dcb5bbf15af42c03b0d26c27c88d3de0a 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -900,14 +900,15 @@ export class CodeMenu { const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsService.getWindowCount() > 0 }); const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow'); + this.nativeTabMenuItems = []; const nativeTabMenuItems: Electron.MenuItem[] = []; if (this.currentEnableNativeTabs) { const hasMultipleWindows = this.windowsService.getWindowCount() > 1; - this.nativeTabMenuItems.push(new MenuItem({ label: nls.localize('mShowPreviousTab', "Show Previous Tab"), enabled: hasMultipleWindows, click: () => Menu.sendActionToFirstResponder('selectPreviousTab:') })); - this.nativeTabMenuItems.push(new MenuItem({ label: nls.localize('mShowNextTab', "Show Next Tab"), enabled: hasMultipleWindows, click: () => Menu.sendActionToFirstResponder('selectNextTab:') })); - this.nativeTabMenuItems.push(new MenuItem({ label: nls.localize('mMoveTabToNewWindow', "Move Tab to New Window"), enabled: hasMultipleWindows, click: () => Menu.sendActionToFirstResponder('moveTabToNewWindow:') })); - this.nativeTabMenuItems.push(new MenuItem({ label: nls.localize('mMergeAllWindows', "Merge All Windows"), enabled: hasMultipleWindows, click: () => Menu.sendActionToFirstResponder('mergeAllWindows:') })); + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowPreviousTab', "Show Previous Tab"), 'workbench.action.showPreviousWindowTab', hasMultipleWindows)); + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowNextTab', "Show Next Tab"), 'workbench.action.showNextWindowTab', hasMultipleWindows)); + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMoveTabToNewWindow', "Move Tab to New Window"), 'workbench.action.moveWindowTabToNewWindow', hasMultipleWindows)); + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMergeAllWindows', "Merge All Windows"), 'workbench.action.mergeAllWindowTabs', hasMultipleWindows)); nativeTabMenuItems.push(__separator__(), ...this.nativeTabMenuItems); } else { diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index e57865cba3ca3b0d9c928f4e99583d2c7c8e844b..cbb0bd1434b4bfa0ff2266344916aa7b1d5001bb 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -61,6 +61,13 @@ export interface IWindowsService { quit(): TPromise; relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise; + // macOS Native Tabs + showPreviousWindowTab(): TPromise; + showNextWindowTab(): TPromise; + moveWindowTabToNewWindow(): TPromise; + mergeAllWindowTabs(): TPromise; + toggleWindowTabsBar(): TPromise; + // Shared process whenSharedProcessReady(): TPromise; toggleSharedProcess(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 378ebd62e450304571ef39459dcc143b83ed802f..93bc0c43e4b890f8a947dc1fba56bf0a21d14840 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -31,6 +31,11 @@ export interface IWindowsChannel extends IChannel { call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]): TPromise; call(command: 'clearRecentlyOpened'): TPromise; call(command: 'getRecentlyOpened', arg: number): TPromise; + call(command: 'showPreviousWindowTab', arg: number): TPromise; + call(command: 'showNextWindowTab', arg: number): TPromise; + call(command: 'moveWindowTabToNewWindow', arg: number): TPromise; + call(command: 'mergeAllWindowTabs', arg: number): TPromise; + call(command: 'toggleWindowTabsBar', arg: number): TPromise; call(command: 'focusWindow', arg: number): TPromise; call(command: 'closeWindow', arg: number): TPromise; call(command: 'isFocused', arg: number): TPromise; @@ -87,6 +92,11 @@ export class WindowsChannel implements IWindowsChannel { case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg); case 'removeFromRecentlyOpened': return this.service.removeFromRecentlyOpened(arg); case 'clearRecentlyOpened': return this.service.clearRecentlyOpened(); + 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 'getRecentlyOpened': return this.service.getRecentlyOpened(arg); case 'focusWindow': return this.service.focusWindow(arg); case 'closeWindow': return this.service.closeWindow(arg); @@ -193,6 +203,26 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('getRecentlyOpened', windowId); } + showPreviousWindowTab(): TPromise { + return this.channel.call('showPreviousWindowTab'); + } + + showNextWindowTab(): TPromise { + return this.channel.call('showNextWindowTab'); + } + + moveWindowTabToNewWindow(): TPromise { + return this.channel.call('moveWindowTabToNewWindow'); + } + + mergeAllWindowTabs(): TPromise { + return this.channel.call('mergeAllWindowTabs'); + } + + toggleWindowTabsBar(): TPromise { + return this.channel.call('toggleWindowTabsBar'); + } + focusWindow(windowId: number): TPromise { return this.channel.call('focusWindow', windowId); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 0a14db499fac43305896a1051adc944cca54093e..e0db43ae15bd5df524b8c07811ba4512fd7faf23 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -11,7 +11,7 @@ import { assign } from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import { IWindowsService, OpenContext, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { shell, crashReporter, app } from 'electron'; +import { shell, crashReporter, app, Menu } from 'electron'; import Event, { chain } from 'vs/base/common/event'; import { fromEventEmitter } from 'vs/base/node/event'; import { IURLService } from 'vs/platform/url/common/url'; @@ -192,6 +192,36 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(this.historyService.getRecentlyOpened()); } + showPreviousWindowTab(): TPromise { + Menu.sendActionToFirstResponder('selectPreviousTab:'); + + return TPromise.as(void 0); + } + + showNextWindowTab(): TPromise { + Menu.sendActionToFirstResponder('selectNextTab:'); + + return TPromise.as(void 0); + } + + moveWindowTabToNewWindow(): TPromise { + Menu.sendActionToFirstResponder('moveTabToNewWindow:'); + + return TPromise.as(void 0); + } + + mergeAllWindowTabs(): TPromise { + Menu.sendActionToFirstResponder('mergeAllWindows:'); + + return TPromise.as(void 0); + } + + toggleWindowTabsBar(): TPromise { + Menu.sendActionToFirstResponder('toggleTabBar:'); + + return TPromise.as(void 0); + } + focusWindow(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 5352574fb60b25edb409257420589fae8209bbb7..2f7d94696bcd3ad55a49d9cb31505636aa234205 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -1554,4 +1554,94 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { this.resizePart(-BaseResizeViewAction.RESIZE_INCREMENT); return TPromise.as(true); } +} + +export class ShowPreviousWindowTab extends Action { + + public static ID = 'workbench.action.showPreviousWindowTab'; + public static LABEL = nls.localize('showPreviousTab', "Show Previous Window Tab"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL); + } + + public run(): TPromise { + return this.windowsService.showPreviousWindowTab().then(() => true); + } +} + +export class ShowNextWindowTab extends Action { + + public static ID = 'workbench.action.showNextWindowTab'; + public static LABEL = nls.localize('showNextWindowTab', "Show Next Window Tab"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(ShowNextWindowTab.ID, ShowNextWindowTab.LABEL); + } + + public run(): TPromise { + return this.windowsService.showNextWindowTab().then(() => true); + } +} + +export class MoveWindowTabToNewWindow extends Action { + + public static ID = 'workbench.action.moveWindowTabToNewWindow'; + public static LABEL = nls.localize('moveWindowTabToNewWindow', "Move Window Tab to New Window"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL); + } + + public run(): TPromise { + return this.windowsService.moveWindowTabToNewWindow().then(() => true); + } +} + +export class MergeAllWindowTabs extends Action { + + public static ID = 'workbench.action.mergeAllWindowTabs'; + public static LABEL = nls.localize('mergeAllWindowTabs', "Merge All Windows"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(MergeAllWindowTabs.ID, MergeAllWindowTabs.LABEL); + } + + public run(): TPromise { + return this.windowsService.mergeAllWindowTabs().then(() => true); + } +} + +export class ToggleWindowTabsBar extends Action { + + public static ID = 'workbench.action.toggleWindowTabsBar'; + public static LABEL = nls.localize('toggleWindowTabsBar', "Toggle Window Tabs Bar"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(ToggleWindowTabsBar.ID, ToggleWindowTabsBar.LABEL); + } + + public run(): TPromise { + return this.windowsService.toggleWindowTabsBar().then(() => true); + } } \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index dd16e7cf8c502acce4906702f5d7296b7aec955c..2fcc3947c8394e9316e7383e4de0aa8f14aceebd 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,11 +14,14 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'v import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { CloseEditorAction, KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, ReportIssueAction, ReportPerformanceIssueAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, CloseMessagesAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { CloseEditorAction, KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, ReportIssueAction, ReportPerformanceIssueAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, CloseMessagesAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey } from 'vs/workbench/electron-browser/actions'; import { MessagesVisibleContext } from 'vs/workbench/electron-browser/workbench'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { registerCommands } from 'vs/workbench/electron-browser/commands'; import { AddRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; // Contribute Commands registerCommands(); @@ -95,6 +98,28 @@ const developerCategory = nls.localize('developer', "Developer"); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowStartupPerformance, ShowStartupPerformance.ID, ShowStartupPerformance.LABEL), 'Developer: Startup Performance', developerCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); +const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); + +const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigateNextInRecentFilesPickerId, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true), + when: recentFilesPickerContext, + primary: KeyMod.CtrlCmd | KeyCode.KEY_R, + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } +}); + +const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: quickOpenNavigatePreviousInRecentFilesPicker, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false), + when: recentFilesPickerContext, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } +}); + // Configuration: Workbench const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 4cc04c8a20a1032f1f488a3bd3d3a32b8c78186b..04db9a58944a5b9ca0a308952164f1538b015c87 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -94,10 +94,8 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, inRecentFilesPickerContextKey } from 'vs/workbench/electron-browser/actions'; +import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar } from 'vs/workbench/electron-browser/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { IWorkspaceEditingService, IWorkspaceMigrationService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; import URI from 'vs/base/common/uri'; @@ -411,27 +409,15 @@ export class Workbench implements IPartService { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } } : void 0), 'Developer: Toggle Developer Tools', localize('developer', "Developer")); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File")); - const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey)); - - const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker'; - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: quickOpenNavigateNextInRecentFilesPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), - handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true), - when: recentFilesPickerContext, - primary: KeyMod.CtrlCmd | KeyCode.KEY_R, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } - }); - - const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker'; - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: quickOpenNavigatePreviousInRecentFilesPicker, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), - handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false), - when: recentFilesPickerContext, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } - }); + // Actions for macOS native tabs management (only when enabled) + const windowConfig = this.configurationService.getConfiguration(); + if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousWindowTab, ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL), 'Show Previous Window Tab'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextWindowTab, ShowNextWindowTab.ID, ShowNextWindowTab.LABEL), 'Show Next Window Tab'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveWindowTabToNewWindow, MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL), 'Move Window Tab to New Window'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(MergeAllWindowTabs, MergeAllWindowTabs.ID, MergeAllWindowTabs.LABEL), 'Merge All Windows'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWindowTabsBar, ToggleWindowTabsBar.ID, ToggleWindowTabsBar.LABEL), 'Toggle Window Tabs Bar'); + } } private resolveEditorsToOpen(): TPromise { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 678cefae4b80e26e333a6e18fc847651293f5b24..30bb6e00b9028968ee9b86f1e68184c0e548fdad 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -1130,6 +1130,26 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } + showPreviousWindowTab(): Promise { + return TPromise.as(void 0); + } + + showNextWindowTab(): Promise { + return TPromise.as(void 0); + } + + moveWindowTabToNewWindow(): Promise { + return TPromise.as(void 0); + } + + mergeAllWindowTabs(): Promise { + return TPromise.as(void 0); + } + + toggleWindowTabsBar(): Promise { + return TPromise.as(void 0); + } + // This needs to be handled from browser process to prevent // foreground ordering issues on Windows openExternal(url: string): TPromise {