Skip to content

Commit

Permalink
Add a command to open a new window as tab (Sierra tabs) (fixes #25919)
Browse files Browse the repository at this point in the history
  • Loading branch information
bpasero committed Aug 7, 2018
1 parent ac60be3 commit 6f3f9ed
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/vs/code/electron-main/menubar.ts
Expand Up @@ -607,6 +607,7 @@ export class Menubar {
if (this.currentEnableNativeTabs) {
const hasMultipleWindows = this.windowsMainService.getWindowCount() > 1;

this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mNewTab', "New Tab"), 'workbench.action.newWindowTab'));
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));
Expand Down
6 changes: 6 additions & 0 deletions src/vs/code/electron-main/window.ts
Expand Up @@ -500,6 +500,12 @@ export class CodeWindow implements ICodeWindow {
});
}

addTabbedWindow(window: ICodeWindow): void {
if (isMacintosh) {
this._win.addTabbedWindow(window.win);
}
}

load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void {

// If this is the first time the window is loaded, we associate the paths
Expand Down
29 changes: 24 additions & 5 deletions src/vs/code/electron-main/windows.ts
Expand Up @@ -81,6 +81,7 @@ interface IOpenBrowserWindowOptions {
filesToWait?: IPathsToWaitFor;

forceNewWindow?: boolean;
forceNewTabbedWindow?: boolean;
windowToUse?: ICodeWindow;

emptyWindowBackupFolder?: string;
Expand Down Expand Up @@ -546,7 +547,7 @@ export class WindowsManager implements IWindowsMainService {
// Special case: we started with --wait and we got back a folder to open. In this case
// we actually prefer to not open the folder but operate purely on the file.
if (typeof bestWindowOrFolder === 'string' && filesToWait) {
//TODO: #54483 Ben This should not happen
//TODO@Ben: #54483 This should not happen
console.error(`This should not happen`, bestWindowOrFolder, WindowsManager.WINDOWS);
bestWindowOrFolder = !openFilesInNewWindow ? this.getLastActiveWindow() : null;
}
Expand Down Expand Up @@ -580,7 +581,7 @@ export class WindowsManager implements IWindowsMainService {

// We found a suitable folder to open: add it to foldersToOpen
else if (typeof bestWindowOrFolder === 'string') {
//TODO: #54483 Ben This should not happen
//TODO@Ben: #54483 Ben This should not happen
// foldersToOpen.push(bestWindowOrFolder);
console.error(`This should not happen`, bestWindowOrFolder, WindowsManager.WINDOWS);
}
Expand All @@ -595,7 +596,8 @@ export class WindowsManager implements IWindowsMainService {
filesToCreate,
filesToDiff,
filesToWait,
forceNewWindow: true
forceNewWindow: true,
forceNewTabbedWindow: openConfig.forceNewTabbedWindow
}));

// Reset these because we handled them
Expand Down Expand Up @@ -700,6 +702,7 @@ export class WindowsManager implements IWindowsMainService {
filesToDiff,
filesToWait,
forceNewWindow: true,
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
emptyWindowBackupFolder
}));

Expand All @@ -720,7 +723,8 @@ export class WindowsManager implements IWindowsMainService {
userEnv: openConfig.userEnv,
cli: openConfig.cli,
initialStartup: openConfig.initialStartup,
forceNewWindow: openFolderInNewWindow
forceNewWindow: openFolderInNewWindow,
forceNewTabbedWindow: openConfig.forceNewTabbedWindow
}));

openFolderInNewWindow = true; // any other window to open must open in new window then
Expand Down Expand Up @@ -767,6 +771,7 @@ export class WindowsManager implements IWindowsMainService {
filesToDiff,
filesToWait,
forceNewWindow,
forceNewTabbedWindow: openConfig.forceNewTabbedWindow,
windowToUse
});

Expand Down Expand Up @@ -1128,6 +1133,7 @@ export class WindowsManager implements IWindowsMainService {
}

private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {

// Build IWindowConfiguration from config and options
const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI
configuration.appRoot = this.environmentService.appRoot;
Expand All @@ -1152,7 +1158,7 @@ export class WindowsManager implements IWindowsMainService {
}

let window: ICodeWindow;
if (!options.forceNewWindow) {
if (!options.forceNewWindow && !options.forceNewTabbedWindow) {
window = options.windowToUse || this.getLastActiveWindow();
if (window) {
window.focus();
Expand All @@ -1179,12 +1185,21 @@ export class WindowsManager implements IWindowsMainService {
state.mode = WindowMode.Normal;
}

// Create the window
window = this.instantiationService.createInstance(CodeWindow, {
state,
extensionDevelopmentPath: configuration.extensionDevelopmentPath,
isExtensionTestHost: !!configuration.extensionTestsPath
});

// Add as window tab if configured (macOS only)
if (options.forceNewTabbedWindow) {
const activeWindow = this.getLastActiveWindow();
if (activeWindow) {
activeWindow.addTabbedWindow(window);
}
}

// Add to our list of windows
WindowsManager.WINDOWS.push(window);

Expand Down Expand Up @@ -1475,6 +1490,10 @@ export class WindowsManager implements IWindowsMainService {
return this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true });
}

openNewTabbedWindow(context: OpenContext): ICodeWindow[] {
return this.open({ context, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true });
}

waitForWindowCloseOrLoad(windowId: number): TPromise<void> {
return new TPromise<void>(c => {
function handler(id: number) {
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/windows/common/windows.ts
Expand Up @@ -143,6 +143,7 @@ export interface IWindowsService {
relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise<void>;

// macOS Native Tabs
newWindowTab(): TPromise<void>;
showPreviousWindowTab(): TPromise<void>;
showNextWindowTab(): TPromise<void>;
moveWindowTabToNewWindow(): TPromise<void>;
Expand Down
6 changes: 6 additions & 0 deletions src/vs/platform/windows/common/windowsIpc.ts
Expand Up @@ -44,6 +44,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void>;
call(command: 'clearRecentlyOpened'): TPromise<void>;
call(command: 'getRecentlyOpened', arg: number): TPromise<IRecentlyOpened>;
call(command: 'newWindowTab'): TPromise<void>;
call(command: 'showPreviousWindowTab'): TPromise<void>;
call(command: 'showNextWindowTab'): TPromise<void>;
call(command: 'moveWindowTabToNewWindow'): TPromise<void>;
Expand Down Expand Up @@ -147,6 +148,7 @@ export class WindowsChannel implements IWindowsChannel {
return this.service.removeFromRecentlyOpened(paths);
}
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();
Expand Down Expand Up @@ -280,6 +282,10 @@ export class WindowsChannelClient implements IWindowsService {
});
}

newWindowTab(): TPromise<void> {
return this.channel.call('newWindowTab');
}

showPreviousWindowTab(): TPromise<void> {
return this.channel.call('showPreviousWindowTab');
}
Expand Down
4 changes: 4 additions & 0 deletions src/vs/platform/windows/electron-main/windows.ts
Expand Up @@ -48,6 +48,8 @@ export interface ICodeWindow {
readyState: ReadyState;
ready(): TPromise<ICodeWindow>;

addTabbedWindow(window: ICodeWindow): void;

load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void;
reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void;

Expand Down Expand Up @@ -110,6 +112,7 @@ export interface IWindowsMainService {
getLastActiveWindow(): ICodeWindow;
waitForWindowCloseOrLoad(windowId: number): TPromise<void>;
openNewWindow(context: OpenContext): ICodeWindow[];
openNewTabbedWindow(context: OpenContext): ICodeWindow[];
sendToFocused(channel: string, ...args: any[]): void;
sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void;
getFocusedWindow(): ICodeWindow;
Expand All @@ -127,6 +130,7 @@ export interface IOpenConfiguration {
urisToOpen?: URI[];
preferNewWindow?: boolean;
forceNewWindow?: boolean;
forceNewTabbedWindow?: boolean;
forceReuseWindow?: boolean;
forceEmpty?: boolean;
diffMode?: boolean;
Expand Down
10 changes: 10 additions & 0 deletions src/vs/platform/windows/electron-main/windowsService.ts
Expand Up @@ -258,6 +258,14 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(this.historyService.getRecentlyOpened());
}

newWindowTab(): TPromise<void> {
this.logService.trace('windowsService#newWindowTab');

this.windowsMainService.openNewTabbedWindow(OpenContext.API);

return TPromise.as(void 0);
}

showPreviousWindowTab(): TPromise<void> {
this.logService.trace('windowsService#showPreviousWindowTab');
Menu.sendActionToFirstResponder('selectPreviousTab:');
Expand Down Expand Up @@ -413,7 +421,9 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable

openNewWindow(): TPromise<void> {
this.logService.trace('windowsService#openNewWindow');

this.windowsMainService.openNewWindow(OpenContext.API);

return TPromise.as(null);
}

Expand Down
18 changes: 18 additions & 0 deletions src/vs/workbench/electron-browser/actions.ts
Expand Up @@ -1459,6 +1459,24 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction {
}
}

export class NewWindowTab extends Action {

static readonly ID = 'workbench.action.newWindowTab';
static readonly LABEL = nls.localize('newTab', "New Window Tab");

constructor(
id: string,
label: string,
@IWindowsService private windowsService: IWindowsService
) {
super(NewWindowTab.ID, NewWindowTab.LABEL);
}

run(): TPromise<boolean> {
return this.windowsService.newWindowTab().then(() => true);
}
}

export class ShowPreviousWindowTab extends Action {

static readonly ID = 'workbench.action.showPreviousWindowTab';
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/electron-browser/workbench.ts
Expand Up @@ -84,7 +84,7 @@ import { MenuService } from 'vs/workbench/services/actions/common/menuService';
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions';
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction, NewWindowTab } from 'vs/workbench/electron-browser/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService';
Expand Down Expand Up @@ -317,6 +317,7 @@ export class Workbench extends Disposable implements IPartService {
// Actions for macOS native tabs management (only when enabled)
const windowConfig = this.configurationService.getValue<IWindowConfiguration>();
if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) {
registry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowTab, NewWindowTab.ID, NewWindowTab.LABEL), 'New Window Tab');
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousWindowTab, ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL), 'Show Previous Window Tab');
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextWindowTab, ShowNextWindowTab.ID, ShowNextWindowTab.LABEL), 'Show Next Window Tab');
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveWindowTabToNewWindow, MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL), 'Move Window Tab to New Window');
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/test/workbenchTestServices.ts
Expand Up @@ -1292,6 +1292,10 @@ export class TestWindowsService implements IWindowsService {
return TPromise.as(void 0);
}

newWindowTab(): TPromise<void> {
return TPromise.as(void 0);
}

showPreviousWindowTab(): TPromise<void> {
return TPromise.as(void 0);
}
Expand Down

0 comments on commit 6f3f9ed

Please sign in to comment.