diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index afbdcccfc92217f0e4e6a39a1b52fecc9ffa3aea..8be2df9955889d9f0c47262419e0ff488f46cfb2 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -187,6 +187,9 @@ export class CodeWindow implements ICodeWindow { }); } + this._win.on('maximize', (e) => app.emit('browser-window-maximize', e, this._win)); + this._win.on('unmaximize', (e) => app.emit('browser-window-unmaximize', e, this._win)); + if (isFullscreenOrMaximized) { this._win.maximize(); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index ecc0b6643cbc6142e78dfa1ebaf3bcedd32eae7a..d54ff859171e1a53bcaeb508c2b941d8a2e14357 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -33,6 +33,8 @@ export interface IWindowsService { onWindowOpen: Event; onWindowFocus: Event; onWindowBlur: Event; + onWindowMaximize: Event; + onWindowUnmaximize: Event; pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; @@ -55,6 +57,7 @@ export interface IWindowsService { isMaximized(windowId: number): TPromise; maximizeWindow(windowId: number): TPromise; unmaximizeWindow(windowId: number): TPromise; + minimizeWindow(windowId: number): TPromise; onWindowTitleDoubleClick(windowId: number): TPromise; setDocumentEdited(windowId: number, flag: boolean): TPromise; quit(): TPromise; @@ -88,6 +91,7 @@ export interface IWindowService { _serviceBrand: any; onDidChangeFocus: Event; + onDidChangeMaximize: Event; getCurrentWindowId(): number; pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; @@ -109,6 +113,7 @@ export interface IWindowService { isMaximized(): TPromise; maximizeWindow(): TPromise; unmaximizeWindow(): TPromise; + minimizeWindow(): TPromise; onWindowTitleDoubleClick(): TPromise; showMessageBox(options: Electron.ShowMessageBoxOptions): number; showSaveDialog(options: Electron.SaveDialogOptions, callback?: (fileName: string) => void): string; @@ -223,4 +228,4 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { perfStartTime?: number; perfAppReady?: number; perfWindowLoadTime?: number; -} \ No newline at end of file +} diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index dd1f6f599f0fdd27d2a29dfebb0967af85a23f57..a5e2249966f354b15342f059f7a74c4f46a42296 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -36,6 +36,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'isMaximized', arg: number): TPromise; call(command: 'maximizeWindow', arg: number): TPromise; call(command: 'unmaximizeWindow', arg: number): TPromise; + call(command: 'minimizeWindow', arg: number): TPromise; call(command: 'onWindowTitleDoubleClick', arg: number): TPromise; call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise; call(command: 'quit'): TPromise; @@ -59,11 +60,15 @@ export class WindowsChannel implements IWindowsChannel { private onWindowOpen: Event; private onWindowFocus: Event; private onWindowBlur: Event; + private onWindowMaximize: Event; + private onWindowUnmaximize: Event; constructor(private service: IWindowsService) { this.onWindowOpen = buffer(service.onWindowOpen, true); this.onWindowFocus = buffer(service.onWindowFocus, true); this.onWindowBlur = buffer(service.onWindowBlur, true); + this.onWindowMaximize = buffer(service.onWindowMaximize, true); + this.onWindowUnmaximize = buffer(service.onWindowUnmaximize, true); } call(command: string, arg?: any): TPromise { @@ -71,6 +76,8 @@ export class WindowsChannel implements IWindowsChannel { case 'event:onWindowOpen': return eventToCall(this.onWindowOpen); case 'event:onWindowFocus': return eventToCall(this.onWindowFocus); case 'event:onWindowBlur': return eventToCall(this.onWindowBlur); + case 'event:onWindowMaximize': return eventToCall(this.onWindowMaximize); + case 'event:onWindowUnmaximize': return eventToCall(this.onWindowUnmaximize); case 'pickFileFolderAndOpen': return this.service.pickFileFolderAndOpen(arg); case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg); case 'pickFolderAndOpen': return this.service.pickFolderAndOpen(arg); @@ -92,6 +99,7 @@ export class WindowsChannel implements IWindowsChannel { case 'isMaximized': return this.service.isMaximized(arg); case 'maximizeWindow': return this.service.maximizeWindow(arg); case 'unmaximizeWindow': return this.service.unmaximizeWindow(arg); + case 'minimizeWindow': return this.service.minimizeWindow(arg); case 'onWindowTitleDoubleClick': return this.service.onWindowTitleDoubleClick(arg); case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]); case 'openWindow': return this.service.openWindow(arg[0], arg[1]); @@ -127,6 +135,13 @@ export class WindowsChannelClient implements IWindowsService { private _onWindowBlur: Event = eventFromCall(this.channel, 'event:onWindowBlur'); get onWindowBlur(): Event { return this._onWindowBlur; } + private _onWindowMaximize: Event = eventFromCall(this.channel, 'event:onWindowMaximize'); + get onWindowMaximize(): Event { return this._onWindowMaximize; } + + private _onWindowUnmaximize: Event = eventFromCall(this.channel, 'event:onWindowUnmaximize'); + get onWindowUnmaximize(): Event { return this._onWindowUnmaximize; } + + pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise { return this.channel.call('pickFileFolderAndOpen', options); } @@ -211,6 +226,14 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('unmaximizeWindow', windowId); } + minimizeWindow(windowId: number): TPromise { + return this.channel.call('minimizeWindow', windowId); + } + + addMaximizeListener(windowId: number, listener: (maximized: boolean) => void): TPromise { + return TPromise.as(null); + } + onWindowTitleDoubleClick(windowId: number): TPromise { return this.channel.call('onWindowTitleDoubleClick', windowId); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 3ebc232734c51263d8f97de857fc738b8886ffb1..e38d1fa623350d3e2d06d1d4d83f6784f7314c2b 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -14,6 +14,7 @@ import { IRecentlyOpened } from "vs/platform/history/common/history"; export class WindowService implements IWindowService { readonly onDidChangeFocus: Event; + readonly onDidChangeMaximize: Event; _serviceBrand: any; @@ -23,7 +24,10 @@ export class WindowService implements IWindowService { ) { const onThisWindowFocus = mapEvent(filterEvent(windowsService.onWindowFocus, id => id === windowId), _ => true); const onThisWindowBlur = mapEvent(filterEvent(windowsService.onWindowBlur, id => id === windowId), _ => false); + const onThisWindowMaximize = mapEvent(filterEvent(windowsService.onWindowMaximize, id => id === windowId), _ => true); + const onThisWindowUnmaximize = mapEvent(filterEvent(windowsService.onWindowUnmaximize, id => id === windowId), _ => false); this.onDidChangeFocus = any(onThisWindowFocus, onThisWindowBlur); + this.onDidChangeMaximize = any(onThisWindowMaximize, onThisWindowUnmaximize); } getCurrentWindowId(): number { @@ -108,6 +112,10 @@ export class WindowService implements IWindowService { return this.windowsService.unmaximizeWindow(this.windowId); } + minimizeWindow(): TPromise { + return this.windowsService.minimizeWindow(this.windowId); + } + onWindowTitleDoubleClick(): TPromise { return this.windowsService.onWindowTitleDoubleClick(this.windowId); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 73a1b67d44f6805a26c2b843b9e3beef48ff7911..113c6bf738d9fdd73a74c68fd8aacf2136ff1e8b 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -29,6 +29,8 @@ export class WindowsService implements IWindowsService, IDisposable { readonly onWindowOpen: Event = fromEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id); readonly onWindowFocus: Event = fromEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id); readonly onWindowBlur: Event = fromEventEmitter(app, 'browser-window-blur', (_, w: Electron.BrowserWindow) => w.id); + readonly onWindowMaximize: Event = fromEventEmitter(app, 'browser-window-maximize', (_, w: Electron.BrowserWindow) => w.id); + readonly onWindowUnmaximize: Event = fromEventEmitter(app, 'browser-window-unmaximize', (_, w: Electron.BrowserWindow) => w.id); constructor( private sharedProcess: ISharedProcess, @@ -242,6 +244,16 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } + minimizeWindow(windowId: number): TPromise { + const codeWindow = this.windowsMainService.getWindowById(windowId); + + if (codeWindow) { + codeWindow.win.minimize(); + } + + return TPromise.as(null); + } + onWindowTitleDoubleClick(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); @@ -365,4 +377,4 @@ export class WindowsService implements IWindowsService, IDisposable { dispose(): void { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 8144355596fa756b256c282b919e718ff519152d..56f913fec9a9e1e083d0fa10fb6161637c67be8f 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -54,7 +54,7 @@ -webkit-app-region: no-drag; } -.window-maximize { +.window-unmaximize { display: none; } @@ -69,7 +69,7 @@ } .monaco-workbench.windows > .part.titlebar.light > .window-icon:hover { - background-color: rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.1); } .monaco-workbench.windows > .part.titlebar.titlebar > .window-close:hover { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 4cbb45aa8fe3ecad40d577751716a5fc55690716..1453b2118babd33d8a50a9db470cb1693e3b9aeb 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -277,18 +277,21 @@ export class TitlebarPart extends Part implements ITitleService { }; if (isWindows) { + // The svgs and styles for the titlebar come from the electron-titlebar-windows package $(this.titleContainer).div({ class: 'window-icon' }, (builder) => { const svg = $svg('svg', { x: 0, y: 0, viewBox: '0 0 10 1' }); svg.appendChild($svg('rect', { fill: 'currentColor', width: 10, height: 1 })); builder.getHTMLElement().appendChild(svg); + }).on(DOM.EventType.CLICK, () => { + this.windowService.minimizeWindow().then(null, errors.onUnexpectedError);; }); $(this.titleContainer).div({ class: 'window-icon' }, (builder) => { - const svgf = $svg('svg', { class: 'window-fullscreen', x: 0, y: 0, viewBox: '0 0 10 10' }); + const svgf = $svg('svg', { class: 'window-maximize', x: 0, y: 0, viewBox: '0 0 10 10' }); svgf.appendChild($svg('path', { fill: 'currentColor', d: 'M 0 0 L 0 10 L 10 10 L 10 0 L 0 0 z M 1 1 L 9 1 L 9 9 L 1 9 L 1 1 z' })); builder.getHTMLElement().appendChild(svgf); - const svgm = $svg('svg', { class: 'window-maximize', x: 0, y: 0, viewBox: '0 0 10 10' }); + const svgm = $svg('svg', { class: 'window-unmaximize', x: 0, y: 0, viewBox: '0 0 10 10' }); const mask = $svg('mask', { id: 'Mask' }); mask.appendChild($svg('rect', { fill: '#fff', width: 10, height: 10 })); mask.appendChild($svg('path', { fill: '#000', d: 'M 3 1 L 9 1 L 9 7 L 8 7 L 8 2 L 3 2 L 3 1 z' })); @@ -296,13 +299,29 @@ export class TitlebarPart extends Part implements ITitleService { svgm.appendChild(mask); svgm.appendChild($svg('path', { fill: 'currentColor', d: 'M 2 0 L 10 0 L 10 8 L 8 8 L 8 10 L 0 10 L 0 2 L 2 2 L 2 0 z', mask: 'url(#Mask)' })); builder.getHTMLElement().appendChild(svgm); + }).on(DOM.EventType.CLICK, () => { + this.windowService.isMaximized().then((maximized) => { + if (maximized) { + return this.windowService.unmaximizeWindow(); + } else { + return this.windowService.maximizeWindow(); + } + }).then(null, errors.onUnexpectedError); }); $(this.titleContainer).div({ class: 'window-icon window-close' }, (builder) => { const svg = $svg('svg', { x: '0', y: '0', viewBox: '0 0 10 10' }); svg.appendChild($svg('polygon', { fill: 'currentColor', points: '10,1 9,0 5,4 1,0 0,1 4,5 0,9 1,10 5,6 9,10 10,9 6,5' })); builder.getHTMLElement().appendChild(svg); + }).on(DOM.EventType.CLICK, () => { + this.windowService.closeWindow().then(null, errors.onUnexpectedError);; }); + + this.windowService.onDidChangeMaximize((mazimized) => { + console.log('change!'); + $(this.titleContainer).select('.window-maximize').display(mazimized ? 'none' : 'inline'); + $(this.titleContainer).select('.window-unmaximize').display(mazimized ? 'inline' : 'none'); + }, this); } return this.titleContainer; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index a9ffb50bf78e63ac10cd0eb9db682a6c47f56476..b44a0578fafe5ec99dd21617caf530509990eabb 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -841,6 +841,7 @@ export class TestWindowService implements IWindowService { public _serviceBrand: any; onDidChangeFocus: Event; + onDidChangeMaximize: Event; isFocused(): TPromise { return TPromise.as(false); @@ -922,6 +923,14 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } + minimizeWindow(): TPromise { + return TPromise.as(void 0); + } + + addMaximizeListener(listener: (maximized: boolean) => void): TPromise { + return TPromise.as(void 0); + } + onWindowTitleDoubleClick(): TPromise { return TPromise.as(void 0); } @@ -981,6 +990,8 @@ export class TestWindowsService implements IWindowsService { onWindowOpen: Event; onWindowFocus: Event; onWindowBlur: Event; + onWindowMaximize: Event; + onWindowUnmaximize: Event; isFocused(windowId: number): TPromise { return TPromise.as(false); @@ -1066,6 +1077,14 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } + minimizeWindow(windowId: number): TPromise { + return TPromise.as(void 0); + } + + addMaximizeListener(windowId: number, listener: (maximized: boolean) => void): TPromise { + return TPromise.as(void 0); + } + onWindowTitleDoubleClick(windowId: number): TPromise { return TPromise.as(void 0); }