diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 5a77861263b..39f5f981c7a 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -145,12 +145,12 @@ export class ToggleDevToolsAction extends Action { public static ID = 'workbench.action.toggleDevTools'; public static LABEL = nls.localize('toggleDevTools', "Toggle Developer Tools"); - constructor(id: string, label: string) { + constructor(id: string, label: string, @IWindowService private windowService: IWindowService) { super(id, label); } public run(): TPromise { - remote.getCurrentWindow().webContents.toggleDevTools(); + ipc.send('vscode:toggleDevTools', this.windowService.getWindowId()); return TPromise.as(true); } diff --git a/src/vs/workbench/electron-browser/index.html b/src/vs/workbench/electron-browser/index.html index 72eee38ac76..626446a2c05 100644 --- a/src/vs/workbench/electron-browser/index.html +++ b/src/vs/workbench/electron-browser/index.html @@ -16,11 +16,11 @@ var electron = require('electron'); var remote = electron.remote; var ipc = electron.ipcRenderer; + var windowId = remote.getCurrentWindow().id; function onError(error, enableDeveloperTools) { if (enableDeveloperTools) { - remote.getCurrentWindow().webContents.openDevTools(); - remote.getCurrentWindow().show(); + ipc.send('vscode:openDevTools', windowId); } console.error('[uncaught exception]: ' + error); @@ -88,9 +88,9 @@ window.addEventListener('keydown', function(e) { var key = extractKey(e); if (key === TOGGLE_DEV_TOOLS_KB) { - remote.getCurrentWindow().toggleDevTools(); + ipc.send('vscode:toggleDevTools', windowId); } else if (key === RELOAD_KB) { - ipc.send('vscode:reloadWindow', remote.getCurrentWindow().id); + ipc.send('vscode:reloadWindow', windowId); } }); } @@ -186,20 +186,25 @@ 'enableTypeScriptServiceModeForJS': !!process.env['CODE_TSJS'] || !!process.env['VSCODE_TSJS'] }; - var programStart = remote.getGlobal('programStart'); - var vscodeStart = remote.getGlobal('vscodeStart'); - var timers = window.MonacoEnvironment.timers = { - start: new Date(programStart || vscodeStart), + start: new Date() }; - if (programStart) { - timers.beforeProgram = new Date(programStart); - timers.afterProgram = new Date(vscodeStart); + if (configuration.enablePerformance) { + var programStart = remote.getGlobal('programStart'); + var vscodeStart = remote.getGlobal('vscodeStart'); + + if (programStart) { + timers.beforeProgram = new Date(programStart); + timers.afterProgram = new Date(vscodeStart); + } + + timers.vscodeStart = new Date(vscodeStart); + timers.start = new Date(programStart || vscodeStart); } - timers.vscodeStart = new Date(vscodeStart); timers.beforeLoad = new Date(); + require([ 'vs/workbench/workbench.main', 'vs/nls!vs/workbench/workbench.main', diff --git a/src/vs/workbench/electron-browser/integration.ts b/src/vs/workbench/electron-browser/integration.ts index f51e80eed22..512c9d8734f 100644 --- a/src/vs/workbench/electron-browser/integration.ts +++ b/src/vs/workbench/electron-browser/integration.ts @@ -28,6 +28,8 @@ import win = require('vs/workbench/electron-browser/window'); import {ipcRenderer as ipc, webFrame, remote} from 'electron'; +const currentWindow = remote.getCurrentWindow(); + const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)), new Action('redo', nls.localize('redo', "Redo"), null, true, () => document.execCommand('redo') && TPromise.as(true)), @@ -58,7 +60,7 @@ export class ElectronIntegration { public integrate(shellContainer: HTMLElement): void { // Register the active window - let activeWindow = this.instantiationService.createInstance(win.ElectronWindow, remote.getCurrentWindow(), shellContainer); + let activeWindow = this.instantiationService.createInstance(win.ElectronWindow, currentWindow, shellContainer); this.windowService.registerWindow(activeWindow); // Support runAction event diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 33046d42a97..6a290b02fc4 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -20,6 +20,8 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {ipcRenderer as ipc, shell, remote} from 'electron'; +const dialog = remote.dialog; + export interface IWindowConfiguration { window: { openFilesInNewWindow: boolean; @@ -30,6 +32,7 @@ export interface IWindowConfiguration { export class ElectronWindow { private win: Electron.BrowserWindow; + private windowId: number; constructor( win: Electron.BrowserWindow, @@ -41,6 +44,7 @@ export class ElectronWindow { @IViewletService private viewletService: IViewletService ) { this.win = win; + this.windowId = win.id; this.registerListeners(); } @@ -49,18 +53,13 @@ export class ElectronWindow { // React to editor input changes (Mac only) if (platform.platform === platform.Platform.Mac) { this.eventService.addListener(EventType.EDITOR_INPUT_CHANGED, (e: EditorEvent) => { - // if we dont use setTimeout() here for some reason there is an issue when switching between 2 files side by side - // with the mac trackpad where the editor would think the user wants to select. to reproduce, have 2 files, click - // into the non-focussed one and move the mouse down and see the editor starts to select lines. - setTimeout(() => { - let fileInput = workbenchEditorCommon.asFileEditorInput(e.editorInput, true); - if (fileInput) { - this.win.setRepresentedFilename(fileInput.getResource().fsPath); - } else { - this.win.setRepresentedFilename(''); - } - }, 0); + let fileInput = workbenchEditorCommon.asFileEditorInput(e.editorInput, true); + let representedFilename = ''; + if (fileInput) { + representedFilename = fileInput.getResource().fsPath; + } + ipc.send('vscode:setRepresentedFilename', this.windowId, representedFilename); }); } @@ -155,36 +154,34 @@ export class ElectronWindow { } public reload(): void { - ipc.send('vscode:reloadWindow', this.win.id); + ipc.send('vscode:reloadWindow', this.windowId); } public showMessageBox(options: Electron.Dialog.ShowMessageBoxOptions): number { - return remote.dialog.showMessageBox(this.win, options); + return dialog.showMessageBox(this.win, options); + } + + public showSaveDialog(options: Electron.Dialog.SaveDialogOptions, callback?: (fileName: string) => void): string { + return dialog.showSaveDialog(this.win, options, callback); } public setFullScreen(fullscreen: boolean): void { - this.win.setFullScreen(fullscreen); + ipc.send('vscode:setFullScreen', this.windowId, fullscreen); // handled from browser process } public openDevTools(): void { - this.win.webContents.openDevTools(); - } - - public isFullScreen(): boolean { - return this.win.isFullScreen(); + ipc.send('vscode:openDevTools', this.windowId); // handled from browser process } public setMenuBarVisibility(visible: boolean): void { - this.win.setMenuBarVisibility(visible); + ipc.send('vscode:setMenuBarVisibility', this.windowId, visible); // handled from browser process } public focus(): void { - if (!this.win.isFocused()) { - this.win.focus(); - } + ipc.send('vscode:focusWindow', this.windowId); // handled from browser process } public flashFrame(): void { - this.win.flashFrame(!this.win.isFocused()); + ipc.send('vscode:flashFrame', this.windowId); // handled from browser process } } \ No newline at end of file diff --git a/src/vs/workbench/electron-main/windows.ts b/src/vs/workbench/electron-main/windows.ts index 5f68ae73164..226db95b066 100644 --- a/src/vs/workbench/electron-main/windows.ts +++ b/src/vs/workbench/electron-main/windows.ts @@ -222,6 +222,79 @@ export class WindowsManager { } }); + ipc.on('vscode:setFullScreen', (event, windowId: number, fullscreen: boolean) => { + env.log('IPC#vscode:setFullScreen'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.setFullScreen(fullscreen); + } + }); + + ipc.on('vscode:toggleDevTools', (event, windowId: number) => { + env.log('IPC#vscode:toggleDevTools'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.webContents.toggleDevTools(); + } + }); + + ipc.on('vscode:openDevTools', (event, windowId: number) => { + env.log('IPC#vscode:openDevTools'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.webContents.openDevTools(); + vscodeWindow.win.show(); + } + }); + + ipc.on('vscode:setRepresentedFilename', (event, windowId: number, fileName: string) => { + env.log('IPC#vscode:setRepresentedFilename'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.setRepresentedFilename(fileName); + } + }); + + ipc.on('vscode:setMenuBarVisibility', (event, windowId: number, visibility: boolean) => { + env.log('IPC#vscode:setMenuBarVisibility'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.setMenuBarVisibility(visibility); + } + }); + + ipc.on('vscode:flashFrame', (event, windowId: number) => { + env.log('IPC#vscode:flashFrame'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.flashFrame(!vscodeWindow.win.isFocused()); + } + }); + + ipc.on('vscode:focusWindow', (event, windowId: number) => { + env.log('IPC#vscode:focusWindow'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow) { + vscodeWindow.win.focus(); + } + }); + + ipc.on('vscode:setDocumentEdited', (event, windowId: number, edited: boolean) => { + env.log('IPC#vscode:setDocumentEdited'); + + let vscodeWindow = this.getWindowById(windowId); + if (vscodeWindow && vscodeWindow.win.isDocumentEdited() !== edited) { + vscodeWindow.win.setDocumentEdited(edited); + } + }); + ipc.on('vscode:toggleMenuBar', (event, windowId: number) => { env.log('IPC#vscode:toggleMenuBar'); diff --git a/src/vs/workbench/parts/files/electron-browser/electronFileTracker.ts b/src/vs/workbench/parts/files/electron-browser/electronFileTracker.ts index 35c278eff34..987e395ef40 100644 --- a/src/vs/workbench/parts/files/electron-browser/electronFileTracker.ts +++ b/src/vs/workbench/parts/files/electron-browser/electronFileTracker.ts @@ -14,6 +14,7 @@ import {asFileEditorInput} from 'vs/workbench/common/editor'; import errors = require('vs/base/common/errors'); import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import URI from 'vs/base/common/uri'; +import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService'; import {EventType as WorkbenchEventType} from 'vs/workbench/common/events'; import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService'; import {IPartService} from 'vs/workbench/services/part/common/partService'; @@ -23,7 +24,7 @@ import {IEventService} from 'vs/platform/event/common/event'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle'; -import {ipcRenderer as ipc, remote} from 'electron'; +import {ipcRenderer as ipc} from 'electron'; export interface IPath { filePath: string; @@ -51,7 +52,8 @@ export class FileTracker implements IWorkbenchContribution { @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @IInstantiationService private instantiationService: IInstantiationService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @ILifecycleService private lifecycleService: ILifecycleService + @ILifecycleService private lifecycleService: ILifecycleService, + @IWindowService private windowService: IWindowService ) { this.toUnbind = []; this.isDocumentedEdited = false; @@ -59,8 +61,7 @@ export class FileTracker implements IWorkbenchContribution { // Make sure to reset any previous state if (plat.platform === plat.Platform.Mac) { - let win = remote.getCurrentWindow(); - win.setDocumentEdited(false); + ipc.send('vscode:setDocumentEdited', this.windowService.getWindowId(), false); // handled from browser process } this.registerListeners(); @@ -185,16 +186,10 @@ export class FileTracker implements IWorkbenchContribution { private updateDocumentEdited(): void { if (plat.platform === plat.Platform.Mac) { - process.nextTick(() => { - let win = remote.getCurrentWindow(); - let isDirtyIndicated = win.isDocumentEdited(); - let hasDirtyFiles = this.textFileService.isDirty(); - this.isDocumentedEdited = hasDirtyFiles; + let hasDirtyFiles = this.textFileService.isDirty(); + this.isDocumentedEdited = hasDirtyFiles; - if (hasDirtyFiles !== isDirtyIndicated) { - win.setDocumentEdited(hasDirtyFiles); - } - }); + ipc.send('vscode:setDocumentEdited', this.windowService.getWindowId(), hasDirtyFiles); // handled from browser process } } diff --git a/src/vs/workbench/parts/files/electron-browser/textFileServices.ts b/src/vs/workbench/parts/files/electron-browser/textFileServices.ts index 8524e5d151c..34ffbf27290 100644 --- a/src/vs/workbench/parts/files/electron-browser/textFileServices.ts +++ b/src/vs/workbench/parts/files/electron-browser/textFileServices.ts @@ -24,13 +24,10 @@ import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {IModeService} from 'vs/editor/common/services/modeService'; - -import {remote} from 'electron'; +import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService'; export class TextFileService extends AbstractTextFileService { - private modeService: IModeService; - constructor( @IWorkspaceContextService contextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, @@ -40,9 +37,11 @@ export class TextFileService extends AbstractTextFileService { @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IEventService eventService: IEventService, - @IModeService modeService: IModeService + @IModeService private modeService: IModeService, + @IWindowService private windowService: IWindowService ) { super(contextService, instantiationService, configurationService, telemetryService, lifecycleService, eventService); + this.modeService = modeService; this.init(); @@ -188,7 +187,7 @@ export class TextFileService extends AbstractTextFileService { cancelId: buttons.indexOf(cancel) }; - const choice = remote.dialog.showMessageBox(remote.getCurrentWindow(), opts); + const choice = this.windowService.getWindow().showMessageBox(opts); return buttons[choice].result; } @@ -359,14 +358,14 @@ export class TextFileService extends AbstractTextFileService { private promptForPathAsync(defaultPath?: string): TPromise { return new TPromise((c, e) => { - remote.dialog.showSaveDialog(remote.getCurrentWindow(), this.getSaveDialogOptions(defaultPath ? paths.normalize(defaultPath, true) : void 0), (path) => { + this.windowService.getWindow().showSaveDialog(this.getSaveDialogOptions(defaultPath ? paths.normalize(defaultPath, true) : void 0), (path) => { c(path); }); }); } private promptForPathSync(defaultPath?: string): string { - return remote.dialog.showSaveDialog(remote.getCurrentWindow(), this.getSaveDialogOptions(defaultPath ? paths.normalize(defaultPath, true) : void 0)); + return this.windowService.getWindow().showSaveDialog(this.getSaveDialogOptions(defaultPath ? paths.normalize(defaultPath, true) : void 0)); } private getSaveDialogOptions(defaultPath?: string): Electron.Dialog.SaveDialogOptions { diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts index 8cb857b5b03..cd7dd829839 100644 --- a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -19,7 +19,9 @@ import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingServic import {remote} from 'electron'; export class ContextMenuService implements IContextMenuService { + public serviceId = IContextMenuService; + private telemetryService: ITelemetryService; private messageService: IMessageService; private keybindingService: IKeybindingService; @@ -36,61 +38,63 @@ export class ContextMenuService implements IContextMenuService { return TPromise.as(null); } - let menu = new remote.Menu(); - let actionToRun: actions.IAction = null; + return TPromise.timeout(0).then(() => { // https://github.com/Microsoft/vscode/issues/3638 + let menu = new remote.Menu(); + let actionToRun: actions.IAction = null; - actions.forEach(a => { - if (a instanceof Separator) { - menu.append(new remote.MenuItem({ type: 'separator' })); + actions.forEach(a => { + if (a instanceof Separator) { + menu.append(new remote.MenuItem({ type: 'separator' })); + } else { + const keybinding = !!delegate.getKeyBinding ? delegate.getKeyBinding(a) : undefined; + const accelerator = keybinding && this.keybindingService.getElectronAcceleratorFor(keybinding); + + const item = new remote.MenuItem({ + label: a.label, + checked: a.checked, + accelerator, + enabled: a.enabled, + click: () => { + actionToRun = a; + } + }); + + menu.append(item); + } + }); + + const anchor = delegate.getAnchor(); + let x: number, y: number; + + if (dom.isHTMLElement(anchor)) { + const $anchor = $(anchor); + const elementPosition = $anchor.getPosition(); + const elementSize = $anchor.getTotalSize(); + + x = elementPosition.left; + y = elementPosition.top + elementSize.height; } else { - const keybinding = !!delegate.getKeyBinding ? delegate.getKeyBinding(a) : undefined; - const accelerator = keybinding && this.keybindingService.getElectronAcceleratorFor(keybinding); - - const item = new remote.MenuItem({ - label: a.label, - checked: a.checked, - accelerator, - enabled: a.enabled, - click: () => { - actionToRun = a; - } - }); - - menu.append(item); + const pos = <{ x: number; y: number; }>anchor; + x = pos.x; + y = pos.y; } + + menu.popup(remote.getCurrentWindow(), Math.floor(x), Math.floor(y)); + + if (delegate.onHide) { + delegate.onHide(false); + } + + if (!actionToRun) { + return; + } + + this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); + + const context = delegate.getActionsContext ? delegate.getActionsContext() : null; + return actionToRun.run(context) || TPromise.as(null); }); - - const anchor = delegate.getAnchor(); - let x: number, y: number; - - if (dom.isHTMLElement(anchor)) { - const $anchor = $(anchor); - const elementPosition = $anchor.getPosition(); - const elementSize = $anchor.getTotalSize(); - - x = elementPosition.left; - y = elementPosition.top + elementSize.height; - } else { - const pos = <{ x: number; y: number; }>anchor; - x = pos.x; - y = pos.y; - } - - menu.popup(remote.getCurrentWindow(), Math.floor(x), Math.floor(y)); - - if (delegate.onHide) { - delegate.onHide(false); - } - - if (!actionToRun) { - return; - } - - this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); - - const context = delegate.getActionsContext ? delegate.getActionsContext() : null; - return actionToRun.run(context) || TPromise.as(null); }) - .done(null, e => this.messageService.show(severity.Error, e)); + .done(null, e => this.messageService.show(severity.Error, e)); } } \ No newline at end of file diff --git a/src/vs/workbench/services/window/electron-browser/windowService.ts b/src/vs/workbench/services/window/electron-browser/windowService.ts index 96b1c142fbc..78faac7c20d 100644 --- a/src/vs/workbench/services/window/electron-browser/windowService.ts +++ b/src/vs/workbench/services/window/electron-browser/windowService.ts @@ -11,6 +11,8 @@ import Event, {Emitter} from 'vs/base/common/event'; import {ipcRenderer as ipc, remote} from 'electron'; +const windowId = remote.getCurrentWindow().id; + export var IWindowService = createDecorator('windowService'); export interface IWindowServices { @@ -40,10 +42,12 @@ export class WindowService implements IWindowService { public serviceId = IWindowService; private win: ElectronWindow; + private windowId: number; private _onBroadcast: Emitter; constructor() { this._onBroadcast = new Emitter(); + this.windowId = windowId; this.registerListeners(); } @@ -59,7 +63,7 @@ export class WindowService implements IWindowService { } public getWindowId(): number { - return remote.getCurrentWindow().id; + return this.windowId; } public getWindow(): ElectronWindow {