diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index d87a1b17431d3cb44d6d3fe6d0585f803034fc62..83b1e3abc693853658b180697ab98584690e5bf3 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -33,6 +33,7 @@ export interface IWindowsService { clearRecentPathsList(): TPromise; getRecentlyOpen(windowId: number): TPromise<{ files: string[]; folders: string[]; }>; focusWindow(windowId: number): TPromise; + isFocused(windowId: number): TPromise; isMaximized(windowId: number): TPromise; maximizeWindow(windowId: number): TPromise; unmaximizeWindow(windowId: number): TPromise; @@ -83,6 +84,7 @@ export interface IWindowService { removeFromRecentlyOpen(paths: string[]): TPromise; getRecentlyOpen(): TPromise<{ files: string[]; folders: string[]; }>; focusWindow(): TPromise; + isFocused(): TPromise; setDocumentEdited(flag: boolean): TPromise; isMaximized(): TPromise; maximizeWindow(): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 9e94ce2bea9f574c0149c12225d306ff7356b673..2d9e9d36506108faf9469e1efb7f750dd955253c 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -27,6 +27,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'clearRecentPathsList'): TPromise; call(command: 'getRecentlyOpen', arg: number): TPromise<{ files: string[]; folders: string[]; }>; call(command: 'focusWindow', arg: number): TPromise; + call(command: 'isFocused', arg: number): TPromise; call(command: 'isMaximized', arg: number): TPromise; call(command: 'maximizeWindow', arg: number): TPromise; call(command: 'unmaximizeWindow', arg: number): TPromise; @@ -76,6 +77,7 @@ export class WindowsChannel implements IWindowsChannel { case 'clearRecentPathsList': return this.service.clearRecentPathsList(); case 'getRecentlyOpen': return this.service.getRecentlyOpen(arg); case 'focusWindow': return this.service.focusWindow(arg); + case 'isFocused': return this.service.isFocused(arg); case 'isMaximized': return this.service.isMaximized(arg); case 'maximizeWindow': return this.service.maximizeWindow(arg); case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg); @@ -167,6 +169,10 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('focusWindow', windowId); } + isFocused(windowId: number): TPromise { + return this.channel.call('isFocused', windowId); + } + isMaximized(windowId: number): TPromise { return this.channel.call('isMaximized', windowId); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index fe25d1689bb97cc42e3fa2b478fc200b0426bba9..c5d2e5c7e8a7d195b9d39d1b2aad05056ebc63be 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -74,6 +74,10 @@ export class WindowService implements IWindowService { return this.windowsService.focusWindow(this.windowId); } + isFocused(): TPromise { + return this.windowsService.isFocused(this.windowId); + } + isMaximized(): TPromise { return this.windowsService.isMaximized(this.windowId); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 21c2bb9722a18c4faf17eda9b419b19bd1ed99ed..2f4a346c53aa2d58e945fd34f5b213a9cf8d1cf0 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -166,6 +166,16 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } + isFocused(windowId: number): TPromise { + const vscodeWindow = this.windowsMainService.getWindowById(windowId); + + if (vscodeWindow) { + return TPromise.as(vscodeWindow.win.isFocused()); + } + + return TPromise.as(null); + } + isMaximized(windowId: number): TPromise { const vscodeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/workbench/electron-browser/workbench.main.ts b/src/vs/workbench/electron-browser/workbench.main.ts index 2dcb5623956ca3e058ec64e858e3d148b6d51a85..1845a488d23f54922e966ee3480360da4822f937 100644 --- a/src/vs/workbench/electron-browser/workbench.main.ts +++ b/src/vs/workbench/electron-browser/workbench.main.ts @@ -82,6 +82,8 @@ import 'vs/workbench/electron-browser/workbench'; import 'vs/workbench/parts/trust/electron-browser/unsupportedWorkspaceSettings.contribution'; +import 'vs/workbench/parts/relauncher/electron-browser/relauncher.contribution'; + import 'vs/workbench/parts/tasks/electron-browser/task.contribution'; import 'vs/workbench/parts/emmet/browser/emmet.browser.contribution'; diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4936dd27a880c78154d8d046bd5173c60c6faab --- /dev/null +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/platform'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +import { IWindowsService, IWindowService } from "vs/platform/windows/common/windows"; +import { IConfigurationService } from "vs/platform/configuration/common/configuration"; +import { IWindowConfiguration } from "vs/workbench/electron-browser/common"; +import { localize } from "vs/nls"; +import { IEnvironmentService } from "vs/platform/environment/common/environment"; + +interface IConfiguration extends IWindowConfiguration { + update: { channel: string; }; + telemetry: { enableCrashReporter: boolean }; +} + +export class SettingsChangeRelauncher implements IWorkbenchContribution { + + private toDispose: IDisposable[] = []; + + private titleBarStyle: 'native' | 'custom'; + private updateChannel: string; + private enableCrashReporter: boolean; + + constructor( + @IWindowsService private windowsService: IWindowsService, + @IWindowService private windowService: IWindowService, + @IConfigurationService private configurationService: IConfigurationService, + @IPreferencesService private preferencesService: IPreferencesService, + @IEnvironmentService private envService: IEnvironmentService, + @IMessageService private messageService: IMessageService + ) { + this.onConfigurationChange(configurationService.getConfiguration(), false); + + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(e.config, true))); + } + + private onConfigurationChange(config: IConfiguration, notify: boolean): void { + let changed = false; + + // Titlebar style + if (config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) { + this.titleBarStyle = config.window.titleBarStyle; + changed = true; + } + + // Update channel + if (config.update && config.update.channel !== this.updateChannel) { + this.updateChannel = config.update.channel; + changed = true; + } + + // Crash reporter + if (config.telemetry && config.telemetry.enableCrashReporter !== this.enableCrashReporter) { + this.enableCrashReporter = config.telemetry.enableCrashReporter; + changed = true; + } + + // Notify only when changed and we are the focused window (avoids notification spam across windows) + if (notify && changed) { + this.windowService.isFocused().then(focused => { + if (focused) { + const relaunch = this.messageService.confirm({ + type: 'info', + message: localize('relaunchMessage', "A setting has changed that requires a restart to take effect."), + detail: localize('relaunchDetail', "Press the restart button to restart {0} and enable the setting.", this.envService.appNameLong), + primaryButton: localize('restart', "Restart") + }); + + if (relaunch) { + this.windowsService.relaunch(Object.create(null)); + } + } + }); + } + } + + getId(): string { + return 'workbench.relauncher'; + } + + public dispose(): void { + this.toDispose = dispose(this.toDispose); + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index d17745efd551c722aad0e8845fd3aa47d5d2d42a..8db0081e65056674ef63a030b4cf49a56f4992d2 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -775,6 +775,10 @@ export class TestWindowService implements IWindowService { public _serviceBrand: any; + isFocused(): TPromise { + return TPromise.as(false); + } + getCurrentWindowId(): number { return 0; } @@ -886,6 +890,10 @@ export class TestWindowsService implements IWindowsService { onWindowOpen: Event; onWindowFocus: Event; + isFocused(windowId: number): TPromise { + return TPromise.as(false); + } + openFileFolderPicker(windowId: number, forceNewWindow?: boolean): TPromise { return TPromise.as(void 0); }