未验证 提交 b5ac96b5 编写于 作者: B Benjamin Pasero 提交者: GitHub

Merge pull request #51422 from sbatten/titlebarplus

Custom Menubar Implementation
......@@ -43,6 +43,8 @@ export class BaseActionItem implements IActionItem {
public _context: any;
public _action: IAction;
static MNEMONIC_REGEX: RegExp = /&&(.)/g;
private _actionRunner: IActionRunner;
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
......@@ -167,12 +169,14 @@ export class BaseActionItem implements IActionItem {
public focus(): void {
if (this.builder) {
this.builder.domFocus();
this.builder.addClass('focused');
}
}
public blur(): void {
if (this.builder) {
this.builder.domBlur();
this.builder.removeClass('focused');
}
}
......@@ -273,7 +277,11 @@ export class ActionItem extends BaseActionItem {
public _updateLabel(): void {
if (this.options.label) {
this.$e.text(this.getAction().label);
let label = this.getAction().label;
if (label && this.options.isMenu) {
label = label.replace(BaseActionItem.MNEMONIC_REGEX, '$1\u0332');
}
this.$e.text(label);
}
}
......@@ -372,7 +380,6 @@ export class ActionBar implements IActionRunner {
// Items
public items: IActionItem[];
private focusedItem: number;
private focusTracker: DOM.IFocusTracker;
......@@ -487,7 +494,7 @@ export class ActionBar implements IActionRunner {
this.actionsList = document.createElement('ul');
this.actionsList.className = 'actions-container';
if (this.options.isMenu) {
this.actionsList.setAttribute('role', 'menubar');
this.actionsList.setAttribute('role', 'menu');
} else {
this.actionsList.setAttribute('role', 'toolbar');
}
......@@ -558,6 +565,15 @@ export class ActionBar implements IActionRunner {
return this.domNode;
}
private _addMnemonic(action: IAction, actionItemElement: HTMLElement): void {
let matches = BaseActionItem.MNEMONIC_REGEX.exec(action.label);
if (matches && matches.length === 2) {
let mnemonic = matches[1];
actionItemElement.accessKey = mnemonic.toLocaleLowerCase();
}
}
public push(arg: IAction | IAction[], options: IActionOptions = {}): void {
const actions: IAction[] = !Array.isArray(arg) ? [arg] : arg;
......@@ -575,6 +591,10 @@ export class ActionBar implements IActionRunner {
e.stopPropagation();
});
if (options.isMenu) {
this._addMnemonic(action, actionItemElement);
}
let item: IActionItem = null;
if (this.options.actionItemProvider) {
......@@ -808,4 +828,4 @@ export class SelectActionItem extends BaseActionItem {
super.dispose();
}
}
}
\ No newline at end of file
......@@ -87,6 +87,10 @@
color: inherit;
}
.monaco-menu .monaco-action-bar.vertical .action-label.checked:after {
content: ' \2713';
}
/* Context Menu */
.context-view.monaco-menu-container {
......
......@@ -18,6 +18,7 @@ export interface IMenuOptions {
actionItemProvider?: IActionItemProvider;
actionRunner?: IActionRunner;
getKeyBinding?: (action: IAction) => ResolvedKeybinding;
ariaLabel?: string;
}
export class Menu {
......@@ -27,9 +28,11 @@ export class Menu {
constructor(container: HTMLElement, actions: IAction[], options: IMenuOptions = {}) {
addClass(container, 'monaco-menu-container');
container.setAttribute('role', 'presentation');
let menuContainer = document.createElement('div');
addClass(menuContainer, 'monaco-menu');
menuContainer.setAttribute('role', 'presentation');
container.appendChild(menuContainer);
this.actionBar = new ActionBar(menuContainer, {
......@@ -37,10 +40,11 @@ export class Menu {
actionItemProvider: options.actionItemProvider,
context: options.context,
actionRunner: options.actionRunner,
isMenu: true
isMenu: true,
ariaLabel: options.ariaLabel
});
this.actionBar.push(actions, { icon: true, label: true });
this.actionBar.push(actions, { icon: true, label: true, isMenu: true });
}
public get onDidCancel(): Event<void> {
......
......@@ -12,7 +12,6 @@ import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/w
import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc';
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { CodeMenu } from 'vs/code/electron-main/menus';
import { getShellEnvironment } from 'vs/code/node/shellEnv';
import { IUpdateService } from 'vs/platform/update/common/update';
import { UpdateChannel } from 'vs/platform/update/common/updateIpc';
......@@ -61,6 +60,11 @@ import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc';
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener';
import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver';
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
import { MenubarChannel } from 'vs/platform/menubar/common/menubarIpc';
// TODO@sbatten: Remove after conversion to new dynamic menubar
import { CodeMenu } from 'vs/code/electron-main/menus';
export class CodeApplication {
......@@ -340,6 +344,7 @@ export class CodeApplication {
services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess));
services.set(ILaunchService, new SyncDescriptor(LaunchService));
services.set(IIssueService, new SyncDescriptor(IssueService, machineId, this.userEnv));
services.set(IMenubarService, new SyncDescriptor(MenubarService));
// Telemtry
if (this.environmentService.isBuilt && !this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) {
......@@ -383,6 +388,10 @@ export class CodeApplication {
this.electronIpcServer.registerChannel('windows', windowsChannel);
this.sharedProcessClient.done(client => client.registerChannel('windows', windowsChannel));
const menubarService = accessor.get(IMenubarService);
const menubarChannel = new MenubarChannel(menubarService);
this.electronIpcServer.registerChannel('menubar', menubarChannel);
const urlService = accessor.get(IURLService);
const urlChannel = new URLServiceChannel(urlService);
this.electronIpcServer.registerChannel('url', urlChannel);
......@@ -447,7 +456,6 @@ export class CodeApplication {
}
private afterWindowOpen(accessor: ServicesAccessor): void {
const appInstantiationService = accessor.get(IInstantiationService);
const windowsMainService = accessor.get(IWindowsMainService);
let windowsMutex: Mutex = null;
......@@ -487,8 +495,13 @@ export class CodeApplication {
}
}
// TODO@sbatten: Remove when menu is converted
// Install Menu
appInstantiationService.createInstance(CodeMenu);
const instantiationService = accessor.get(IInstantiationService);
const configurationService = accessor.get(IConfigurationService);
if (platform.isMacintosh || configurationService.getValue<string>('window.titleBarStyle') !== 'custom') {
instantiationService.createInstance(CodeMenu);
}
// Jump List
this.historyMainService.updateWindowsJumpList();
......
此差异已折叠。
......@@ -166,11 +166,15 @@ export class CodeWindow implements ICodeWindow {
}
let useCustomTitleStyle = false;
if (isMacintosh && (!windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom')) {
if (isMacintosh) {
useCustomTitleStyle = !windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom'; // Default to custom on macOS
const isDev = !this.environmentService.isBuilt || !!config.extensionDevelopmentPath;
if (!isDev) {
useCustomTitleStyle = true; // not enabled when developing due to https://github.com/electron/electron/issues/3647
if (isDev) {
useCustomTitleStyle = false; // not enabled when developing due to https://github.com/electron/electron/issues/3647
}
} else {
useCustomTitleStyle = windowConfig && windowConfig.titleBarStyle === 'custom'; // Must be specified on Windows/Linux
}
if (useNativeTabs) {
......@@ -180,6 +184,9 @@ export class CodeWindow implements ICodeWindow {
if (useCustomTitleStyle) {
options.titleBarStyle = 'hidden';
this.hiddenTitleBarStyle = true;
if (!isMacintosh) {
options.frame = false;
}
}
// Create the browser window.
......@@ -376,6 +383,23 @@ export class CodeWindow implements ICodeWindow {
this._lastFocusTime = Date.now();
});
// Window (Un)Maximize
this._win.on('maximize', (e) => {
if (this.currentConfig) {
this.currentConfig.maximized = true;
}
app.emit('browser-window-maximize', e, this._win);
});
this._win.on('unmaximize', (e) => {
if (this.currentConfig) {
this.currentConfig.maximized = false;
}
app.emit('browser-window-unmaximize', e, this._win);
});
// Window Fullscreen
this._win.on('enter-full-screen', () => {
this.sendWhenReady('vscode:enterFullScreen');
......@@ -591,6 +615,10 @@ export class CodeWindow implements ICodeWindow {
windowConfiguration.baseTheme = this.getBaseTheme();
windowConfiguration.backgroundColor = this.getBackgroundColor();
// Title style related
windowConfiguration.maximized = this._win.isMaximized();
windowConfiguration.frameless = this.hasHiddenTitleBarStyle() && !isMacintosh;
// Perf Counters
windowConfiguration.perfEntries = exportEntries();
windowConfiguration.perfStartTime = (<any>global).perfStartTime;
......
......@@ -69,6 +69,18 @@ export class MenuId {
static readonly ViewItemContext = new MenuId();
static readonly TouchBarContext = new MenuId();
static readonly SearchContext = new MenuId();
static readonly MenubarFileMenu = new MenuId();
static readonly MenubarEditMenu = new MenuId();
static readonly MenubarRecentMenu = new MenuId();
static readonly MenubarSelectionMenu = new MenuId();
static readonly MenubarViewMenu = new MenuId();
static readonly MenubarLayoutMenu = new MenuId();
static readonly MenubarGoMenu = new MenuId();
static readonly MenubarDebugMenu = new MenuId();
static readonly MenubarTasksMenu = new MenuId();
static readonly MenubarWindowMenu = new MenuId();
static readonly MenubarPreferencesMenu = new MenuId();
static readonly MenubarHelpMenu = new MenuId();
readonly id: string = String(MenuId.ID++);
}
......
/*---------------------------------------------------------------------------------------------
* 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 { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IMenubarService = createDecorator<IMenubarService>('menubarService');
export interface IMenubarService {
_serviceBrand: any;
updateMenubar(windowId: number, menus: IMenubarData): TPromise<void>;
}
export interface IMenubarData {
'Files'?: IMenubarMenu;
'Edit'?: IMenubarMenu;
[id: string]: IMenubarMenu;
}
export interface IMenubarMenu {
items: Array<IMenubarMenuItemAction | IMenubarMenuItemSeparator>;
}
export interface IMenubarMenuItemAction {
id: string;
label: string;
checked: boolean;
enabled: boolean;
}
export interface IMenubarMenuItemSeparator {
id: 'vscode.menubar.separator';
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar';
export interface IMenubarChannel extends IChannel {
call(command: 'updateMenubar', arg: [number, IMenubarData]): TPromise<void>;
call(command: string, arg?: any): TPromise<any>;
}
export class MenubarChannel implements IMenubarChannel {
constructor(private service: IMenubarService) { }
call(command: string, arg?: any): TPromise<any> {
switch (command) {
case 'updateMenubar': return this.service.updateMenubar(arg[0], arg[1]);
}
return undefined;
}
}
export class MenubarChannelClient implements IMenubarService {
_serviceBrand: any;
constructor(private channel: IMenubarChannel) { }
updateMenubar(windowId: number, menus: IMenubarData): TPromise<void> {
return this.channel.call('updateMenubar', [windowId, menus]);
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* 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 { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar';
import { Menubar } from 'vs/code/electron-main/menubar';
import { ILogService } from 'vs/platform/log/common/log';
import { TPromise } from 'vs/base/common/winjs.base';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
export class MenubarService implements IMenubarService {
_serviceBrand: any;
private _menubar: Menubar;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@ILogService private logService: ILogService
) {
// Install Menu
// TODO@sbatten: Remove if block
if (isMacintosh && isWindows) {
this._menubar = this.instantiationService.createInstance(Menubar);
}
}
updateMenubar(windowId: number, menus: IMenubarData): TPromise<void> {
this.logService.trace('menubarService#updateMenubar', windowId);
if (this._menubar) {
this._menubar.updateMenu(menus, windowId);
}
return TPromise.as(null);
}
}
\ No newline at end of file
......@@ -103,6 +103,8 @@ export interface IWindowsService {
onWindowOpen: Event<number>;
onWindowFocus: Event<number>;
onWindowBlur: Event<number>;
onWindowMaximize: Event<number>;
onWindowUnmaximize: Event<number>;
// Dialogs
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void>;
......@@ -131,6 +133,7 @@ export interface IWindowsService {
isMaximized(windowId: number): TPromise<boolean>;
maximizeWindow(windowId: number): TPromise<void>;
unmaximizeWindow(windowId: number): TPromise<void>;
minimizeWindow(windowId: number): TPromise<void>;
onWindowTitleDoubleClick(windowId: number): TPromise<void>;
setDocumentEdited(windowId: number, flag: boolean): TPromise<void>;
quit(): TPromise<void>;
......@@ -181,6 +184,7 @@ export interface IWindowService {
_serviceBrand: any;
onDidChangeFocus: Event<boolean>;
onDidChangeMaximize: Event<boolean>;
getConfiguration(): IWindowConfiguration;
getCurrentWindowId(): number;
......@@ -203,6 +207,10 @@ export interface IWindowService {
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise<void>;
isFocused(): TPromise<boolean>;
setDocumentEdited(flag: boolean): TPromise<void>;
isMaximized(): TPromise<boolean>;
maximizeWindow(): TPromise<void>;
unmaximizeWindow(): TPromise<void>;
minimizeWindow(): TPromise<void>;
onWindowTitleDoubleClick(): TPromise<void>;
show(): TPromise<void>;
showMessageBox(options: MessageBoxOptions): TPromise<IMessageBoxResult>;
......@@ -326,9 +334,11 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest {
zoomLevel?: number;
fullscreen?: boolean;
maximized?: boolean;
highContrast?: boolean;
baseTheme?: string;
backgroundColor?: string;
frameless?: boolean;
accessibilitySupport?: boolean;
perfEntries: PerformanceEntry[];
......
......@@ -49,6 +49,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'isMaximized', arg: number): TPromise<boolean>;
call(command: 'maximizeWindow', arg: number): TPromise<void>;
call(command: 'unmaximizeWindow', arg: number): TPromise<void>;
call(command: 'minimizeWindow', arg: number): TPromise<void>;
call(command: 'onWindowTitleDoubleClick', arg: number): TPromise<void>;
call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise<void>;
call(command: 'quit'): TPromise<void>;
......@@ -73,11 +74,15 @@ export class WindowsChannel implements IWindowsChannel {
private onWindowOpen: Event<number>;
private onWindowFocus: Event<number>;
private onWindowBlur: Event<number>;
private onWindowMaximize: Event<number>;
private onWindowUnmaximize: Event<number>;
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<any> {
......@@ -85,6 +90,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);
......@@ -129,6 +136,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], arg[2]);
......@@ -165,6 +173,12 @@ export class WindowsChannelClient implements IWindowsService {
private _onWindowBlur: Event<number> = eventFromCall<number>(this.channel, 'event:onWindowBlur');
get onWindowBlur(): Event<number> { return this._onWindowBlur; }
private _onWindowMaximize: Event<number> = eventFromCall<number>(this.channel, 'event:onWindowMaximize');
get onWindowMaximize(): Event<number> { return this._onWindowMaximize; }
private _onWindowUnmaximize: Event<number> = eventFromCall<number>(this.channel, 'event:onWindowUnmaximize');
get onWindowUnmaximize(): Event<number> { return this._onWindowUnmaximize; }
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
return this.channel.call('pickFileFolderAndOpen', options);
}
......@@ -285,6 +299,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('unmaximizeWindow', windowId);
}
minimizeWindow(windowId: number): TPromise<void> {
return this.channel.call('minimizeWindow', windowId);
}
onWindowTitleDoubleClick(windowId: number): TPromise<void> {
return this.channel.call('onWindowTitleDoubleClick', windowId);
}
......
......@@ -16,6 +16,7 @@ import { ParsedArgs } from 'vs/platform/environment/common/environment';
export class WindowService implements IWindowService {
readonly onDidChangeFocus: Event<boolean>;
readonly onDidChangeMaximize: Event<boolean>;
_serviceBrand: any;
......@@ -26,7 +27,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 = anyEvent(onThisWindowFocus, onThisWindowBlur);
this.onDidChangeMaximize = anyEvent(onThisWindowMaximize, onThisWindowUnmaximize);
}
getCurrentWindowId(): number {
......@@ -113,6 +117,22 @@ export class WindowService implements IWindowService {
return this.windowsService.isFocused(this.windowId);
}
isMaximized(): TPromise<boolean> {
return this.windowsService.isMaximized(this.windowId);
}
maximizeWindow(): TPromise<void> {
return this.windowsService.maximizeWindow(this.windowId);
}
unmaximizeWindow(): TPromise<void> {
return this.windowsService.unmaximizeWindow(this.windowId);
}
minimizeWindow(): TPromise<void> {
return this.windowsService.minimizeWindow(this.windowId);
}
onWindowTitleDoubleClick(): TPromise<void> {
return this.windowsService.onWindowTitleDoubleClick(this.windowId);
}
......
......@@ -39,6 +39,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
);
readonly onWindowBlur: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-blur', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onWindowMaximize: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-maximize', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onWindowUnmaximize: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-unmaximize', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
constructor(
private sharedProcess: ISharedProcess,
......@@ -338,6 +340,17 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(null);
}
minimizeWindow(windowId: number): TPromise<void> {
this.logService.trace('windowsService#minimizeWindow', windowId);
const codeWindow = this.windowsMainService.getWindowById(windowId);
if (codeWindow) {
codeWindow.win.minimize();
}
return TPromise.as(null);
}
onWindowTitleDoubleClick(windowId: number): TPromise<void> {
this.logService.trace('windowsService#onWindowTitleDoubleClick', windowId);
const codeWindow = this.windowsMainService.getWindowById(windowId);
......
......@@ -16,6 +16,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { Disposable } from 'vs/base/common/lifecycle';
import { getZoomFactor } from 'vs/base/browser/browser';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { isMacintosh } from 'vs/base/common/platform';
import { memoize } from 'vs/base/common/decorators';
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
......@@ -27,8 +28,9 @@ import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activity
import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart';
import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart';
import { MenubarPart } from 'vs/workbench/browser/parts/menubar/menubarPart';
const TITLE_BAR_HEIGHT = 22;
const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30;
const STATUS_BAR_HEIGHT = 22;
const ACTIVITY_BAR_WIDTH = 50;
......@@ -63,6 +65,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
private _sidebarWidth: number;
private sidebarHeight: number;
private titlebarHeight: number;
private menubarHeight: number;
private headingHeight: number;
private statusbarHeight: number;
private panelSizeBeforeMaximized: number;
private panelMaximized: boolean;
......@@ -75,6 +79,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
private workbenchContainer: HTMLElement,
private parts: {
titlebar: TitlebarPart,
menubar: MenubarPart,
activitybar: ActivitybarPart,
editor: EditorPart,
sidebar: SidebarPart,
......@@ -219,6 +224,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
titlebar: {
height: TITLE_BAR_HEIGHT
},
menubar: {
height: TITLE_BAR_HEIGHT
},
activitybar: {
width: ACTIVITY_BAR_WIDTH
},
......@@ -311,7 +319,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) {
let dragCompensation = this.partLayoutInfo.panel.minHeight - HIDE_PANEL_HEIGHT_THRESHOLD;
promise = this.partService.setPanelHidden(true);
startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation);
startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.headingHeight, e.currentY + dragCompensation);
this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from
}
......@@ -412,6 +420,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART);
const isMenubarHidden = !this.partService.isVisible(Parts.MENUBAR_PART);
const isPanelHidden = !this.partService.isVisible(Parts.PANEL_PART);
const isStatusbarHidden = !this.partService.isVisible(Parts.STATUSBAR_PART);
const isSidebarHidden = !this.partService.isVisible(Parts.SIDEBAR_PART);
......@@ -425,8 +434,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height;
this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / getZoomFactor(); // adjust for zoom prevention
this.menubarHeight = isMenubarHidden ? 0 : this.partLayoutInfo.menubar.height / getZoomFactor(); // adjust for zoom prevention
this.headingHeight = Math.max(this.menubarHeight, this.titlebarHeight);
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.headingHeight;
let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight);
// Activity Bar
......@@ -563,6 +574,14 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
show(titleContainer);
}
// Menubar
const menubarContainer = this.parts.menubar.getContainer();
if (isMenubarHidden) {
hide(menubarContainer);
} else {
show(menubarContainer);
}
// Editor Part and Panel part
const editorContainer = this.parts.editor.getContainer();
const panelContainer = this.parts.panel.getContainer();
......@@ -571,19 +590,19 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
if (panelPosition === Position.BOTTOM) {
if (sidebarPosition === Position.LEFT) {
position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(editorContainer, this.headingHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
position(panelContainer, editorSize.height + this.headingHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
} else {
position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
position(editorContainer, this.headingHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
position(panelContainer, editorSize.height + this.headingHeight, sidebarSize.width, this.statusbarHeight, 0);
}
} else {
if (sidebarPosition === Position.LEFT) {
position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
position(editorContainer, this.headingHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(panelContainer, this.headingHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
} else {
position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
position(editorContainer, this.headingHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
position(panelContainer, this.headingHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
}
}
......@@ -592,10 +611,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
size(activitybarContainer, null, activityBarSize.height);
if (sidebarPosition === Position.LEFT) {
this.parts.activitybar.getContainer().style.right = '';
position(activitybarContainer, this.titlebarHeight, null, 0, 0);
position(activitybarContainer, this.headingHeight, null, 0, 0);
} else {
this.parts.activitybar.getContainer().style.left = '';
position(activitybarContainer, this.titlebarHeight, 0, 0, null);
position(activitybarContainer, this.headingHeight, 0, 0, null);
}
if (isActivityBarHidden) {
hide(activitybarContainer);
......@@ -608,9 +627,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
size(sidebarContainer, sidebarSize.width, sidebarSize.height);
const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
if (sidebarPosition === Position.LEFT) {
position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
position(sidebarContainer, this.headingHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
} else {
position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
position(sidebarContainer, this.headingHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
}
// Statusbar Part
......@@ -646,6 +665,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
// Propagate to Part Layouts
this.parts.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight));
this.parts.menubar.layout(new Dimension(this.workbenchSize.width, this.menubarHeight));
this.parts.editor.layout(new Dimension(editorSize.width, editorSize.height));
this.parts.sidebar.layout(sidebarSize);
this.parts.panel.layout(panelDimension);
......@@ -656,7 +676,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
}
getVerticalSashTop(sash: Sash): number {
return this.titlebarHeight;
return this.headingHeight;
}
getVerticalSashLeft(sash: Sash): number {
......@@ -683,7 +703,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
getHorizontalSashTop(sash: Sash): number {
const offset = 2; // Horizontal sash should be a bit lower than the editor area, thus add 2px #5524
return offset + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.titlebarHeight : this.sidebarHeight + this.titlebarHeight);
return offset + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.headingHeight : this.sidebarHeight + this.headingHeight);
}
getHorizontalSashLeft(sash: Sash): number {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .part.menubar {
display: flex;
position: absolute;
font-size: 12px;
box-sizing: border-box;
padding-left: 30px;
padding-right: 138px;
height: 30px;
}
.monaco-workbench.fullscreen > .part.menubar {
position: absolute;
width: 100%;
margin: 0px;
padding: 0px 5px;
}
.monaco-workbench > .part.menubar > .menubar-menu-button {
display: flex;
flex-shrink: 0;
align-items: center;
box-sizing: border-box;
padding: 0px 5px;
position: relative;
z-index: 10;
cursor: pointer;
-webkit-app-region: no-drag;
zoom: 1;
}
.monaco-workbench > .part.menubar > .menubar-menu-button.open,
.monaco-workbench > .part.menubar > .menubar-menu-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.monaco-workbench > .part.menubar.light > .menubar-menu-button.open,
.monaco-workbench > .part.menubar.light > .menubar-menu-button:hover {
background-color: rgba(0, 0, 0, 0.1);
}
.menubar-menu-items-holder {
position: absolute;
left: 0px;
opacity: 1;
}
.menubar-menu-items-holder.monaco-menu-container {
box-shadow: 0 2px 8px #A8A8A8;
}
.vs-dark .menubar-menu-items-holder.monaco-menu-container {
box-shadow: 0 2px 8px #000;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import URI from 'vs/base/common/uri';
export const FILE_MENU_FAKE_OPEN_FILE_COMMAND_ID = 'workbench.action.fakeOpenFile';
export function setup(): void {
registerMenubarCommands();
}
function registerMenubarCommands() {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: FILE_MENU_FAKE_OPEN_FILE_COMMAND_ID,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyMod.CtrlCmd | KeyCode.F6,
win: { primary: KeyMod.CtrlCmd | KeyCode.F6 },
handler: (accessor, resource: URI | object) => {
alert('fake open successful');
console.log('fake open triggered');
}
});
}
\ No newline at end of file
此差异已折叠。
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.279 5.5L11 10.221l-.779.779L5.5 6.279.779 11 0 10.221 4.721 5.5 0 .779.779 0 5.5 4.721 10.221 0 11 .779 6.279 5.5z" fill="#fff"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.279 5.5L11 10.221l-.779.779L5.5 6.279.779 11 0 10.221 4.721 5.5 0 .779.779 0 5.5 4.721 10.221 0 11 .779 6.279 5.5z" fill="#000"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 0v11H0V0h11zM9.899 1.101H1.1V9.9H9.9V1.1z" fill="#fff"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 0v11H0V0h11zM9.899 1.101H1.1V9.9H9.9V1.1z" fill="#000"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.399V5.5H0V4.399h11z" fill="#fff"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 4.399V5.5H0V4.399h11z" fill="#000"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 8.798H8.798V11H0V2.202h2.202V0H11v8.798zm-3.298-5.5h-6.6v6.6h6.6v-6.6zM9.9 1.1H3.298v1.101h5.5v5.5h1.1v-6.6z" fill="#fff"/></svg>
\ No newline at end of file
<svg width="11" height="11" viewBox="0 0 11 11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 8.798H8.798V11H0V2.202h2.202V0H11v8.798zm-3.298-5.5h-6.6v6.6h6.6v-6.6zM9.9 1.1H3.298v1.101h5.5v5.5h1.1v-6.6z" fill="#000"/></svg>
\ No newline at end of file
......@@ -24,8 +24,127 @@
flex: 0 1 auto;
overflow: hidden;
white-space: nowrap;
line-height: 22px;
text-overflow: ellipsis;
-webkit-app-region: drag;
zoom: 1; /* prevent zooming */
}
/* Windows/Linux: Rules for custom title (icon, window controls) */
.monaco-workbench.windows > .part.titlebar,
.monaco-workbench.linux > .part.titlebar {
padding: 0;
height: 30px;
line-height: 30px;
justify-content: space-between;
}
.monaco-workbench.windows > .part.titlebar > .resizer,
.monaco-workbench.linux > .part.titlebar > .resizer {
-webkit-app-region: no-drag;
position: absolute;
top: 0;
width: 100%;
height: 1px;
}
.monaco-workbench.windows > .part.titlebar > .window-title,
.monaco-workbench.linux > .part.titlebar > .window-title {
order: 2;
}
.monaco-workbench > .part.titlebar > .window-appicon {
width: 15px;
padding-left: 10px;
margin-right: 113px;
-webkit-app-region: no-drag;
position: relative;
z-index: 99;
order: 1;
image-rendering: crisp-edges;
}
.monaco-workbench > .part.titlebar > .window-controls-container {
display: flex;
flex-grow: 0;
flex-shrink: 0;
text-align: center;
position: relative;
z-index: 99;
-webkit-app-region: no-drag;
height: 100%;
width: 138px;
order: 3;
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon {
display: inline-block;
-webkit-app-region: no-drag;
-webkit-transition: background-color .2s;
transition: background-color .2s;
height: 100%;
width: 33.34%;
background-size: 20%;
background-position: center center;
background-repeat: no-repeat;
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon svg {
shape-rendering: crispEdges;
text-align: center;
}
.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-close {
background-image: url('chrome-close-dark.svg');
order: 3;
}
.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-close {
background-image: url('chrome-close.svg');
order: 3;
}
.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-unmaximize {
background-image: url('chrome-restore-dark.svg');
order: 2;
}
.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-unmaximize {
background-image: url('chrome-restore.svg');
order: 2;
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-maximize {
background-image: url('chrome-maximize-dark.svg');
order: 2;
}
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-maximize {
background-image: url('chrome-maximize.svg');
order: 2;
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-minimize {
background-image: url('chrome-minimize-dark.svg');
order: 1;
}
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-minimize {
background-image: url('chrome-minimize.svg');
order: 1;
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-icon:hover {
background-color: rgba(0, 0, 0, 0.1);
}
.monaco-workbench > .part.titlebar > .window-controls-container > .window-close:hover,
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-close:hover {
background-color: rgba(232, 17, 35, 0.9);
background-image: url('chrome-close-dark.svg');
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ import * as paths from 'vs/base/common/paths';
import { Part } from 'vs/workbench/browser/part';
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
import { getZoomFactor } from 'vs/base/browser/browser';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowService, IWindowsService, MenuBarVisibility } from 'vs/platform/windows/common/windows';
import * as errors from 'vs/base/common/errors';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
......@@ -27,10 +27,12 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme';
import { isMacintosh, isWindows } from 'vs/base/common/platform';
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
import URI from 'vs/base/common/uri';
import { Color } from 'vs/base/common/color';
import { trim } from 'vs/base/common/strings';
import { addDisposableListener, EventType, EventHelper, Dimension } from 'vs/base/browser/dom';
import { addDisposableListener, EventType, EventHelper, Dimension, addClass, removeClass } from 'vs/base/browser/dom';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export class TitlebarPart extends Part implements ITitleService {
......@@ -44,9 +46,20 @@ export class TitlebarPart extends Part implements ITitleService {
private titleContainer: Builder;
private title: Builder;
private windowControls: Builder;
private appIcon: Builder;
private pendingTitle: string;
private initialTitleFontSize: number;
private representedFileName: string;
private menubarWidth: number;
private initialSizing: {
titleFontSize?: number;
titlebarHeight?: number;
controlsWidth?: number;
appIconWidth?: number;
appIconLeftPadding?: number;
} = Object.create(null);
private isInactive: boolean;
......@@ -62,6 +75,7 @@ export class TitlebarPart extends Part implements ITitleService {
@IEditorService private editorService: IEditorService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IPartService private partService: IPartService,
@IThemeService themeService: IThemeService
) {
super(id, { hasTitle: false }, themeService);
......@@ -80,6 +94,7 @@ export class TitlebarPart extends Part implements ITitleService {
this.toUnbind.push(this.contextService.onDidChangeWorkspaceFolders(() => this.setTitle(this.getWindowTitle())));
this.toUnbind.push(this.contextService.onDidChangeWorkbenchState(() => this.setTitle(this.getWindowTitle())));
this.toUnbind.push(this.contextService.onDidChangeWorkspaceName(() => this.setTitle(this.getWindowTitle())));
this.toUnbind.push(this.partService.onMenubarVisibilityChange(this.onMenubarVisibilityChanged, this));
}
private onBlur(): void {
......@@ -98,6 +113,12 @@ export class TitlebarPart extends Part implements ITitleService {
}
}
private onMenubarVisibilityChanged(dimension: Dimension): void {
this.menubarWidth = dimension.width;
this.updateLayout();
}
private onActiveEditorChange(): void {
// Dispose old listeners
......@@ -228,6 +249,21 @@ export class TitlebarPart extends Part implements ITitleService {
public createContentArea(parent: HTMLElement): HTMLElement {
this.titleContainer = $(parent);
// App Icon (Windows/Linux)
if (!isMacintosh) {
this.appIcon = $(this.titleContainer).img({
class: 'window-appicon',
src: paths.join(this.environmentService.appRoot, isWindows ? 'resources/win32/code.ico' : 'resources/linux/code.png')
}).on(EventType.DBLCLICK, e => {
EventHelper.stop(e, true);
this.windowService.closeWindow().then(null, errors.onUnexpectedError);
});
// Resizer
$(this.titleContainer).div({ class: 'resizer' });
}
// Title
this.title = $(this.titleContainer).div({ class: 'window-title' });
if (this.pendingTitle) {
......@@ -250,6 +286,36 @@ export class TitlebarPart extends Part implements ITitleService {
}
});
// Window Controls (Windows/Linux)
if (!isMacintosh) {
this.windowControls = $(this.titleContainer).div({ class: 'window-controls-container' });
// Minimize
$(this.windowControls).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => {
this.windowService.minimizeWindow().then(null, errors.onUnexpectedError);
});
// Restore
$(this.windowControls).div({ class: 'window-icon window-max-restore' }).on(EventType.CLICK, () => {
this.windowService.isMaximized().then((maximized) => {
if (maximized) {
return this.windowService.unmaximizeWindow();
}
return this.windowService.maximizeWindow();
}).then(null, errors.onUnexpectedError);
});
// Close
$(this.windowControls).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => {
this.windowService.closeWindow().then(null, errors.onUnexpectedError);
});
const isMaximized = this.windowService.getConfiguration().maximized ? true : false;
this.onDidChangeMaximized(isMaximized);
this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this);
}
// Since the title area is used to drag the window, we do not want to steal focus from the
// currently active element. So we restore focus after a timeout back to where it was.
this.titleContainer.on([EventType.MOUSE_DOWN], () => {
......@@ -264,13 +330,36 @@ export class TitlebarPart extends Part implements ITitleService {
return this.titleContainer.getHTMLElement();
}
private onDidChangeMaximized(maximized: boolean) {
const element = $(this.titleContainer).getHTMLElement().querySelector('.window-max-restore') as HTMLElement;
if (!element) {
return;
}
if (maximized) {
removeClass(element, 'window-maximize');
addClass(element, 'window-unmaximize');
} else {
removeClass(element, 'window-unmaximize');
addClass(element, 'window-maximize');
}
}
protected updateStyles(): void {
super.updateStyles();
// Part container
if (this.titleContainer) {
this.titleContainer.style('color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND));
this.titleContainer.style('background-color', this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND));
const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND);
this.titleContainer.style('background-color', titleBackground);
if (Color.fromHex(titleBackground).isLighter()) {
this.titleContainer.addClass('light');
} else {
this.titleContainer.removeClass('light');
}
const titleForeground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_FOREGROUND : TITLE_BAR_ACTIVE_FOREGROUND);
this.titleContainer.style('color', titleForeground);
const titleBorder = this.getColor(TITLE_BAR_BORDER);
this.titleContainer.style('border-bottom', titleBorder ? `1px solid ${titleBorder}` : null);
......@@ -340,13 +429,85 @@ export class TitlebarPart extends Part implements ITitleService {
}
}
public layout(dimension: Dimension): Dimension[] {
private updateLayout() {
// To prevent zooming we need to adjust the font size with the zoom factor
if (typeof this.initialTitleFontSize !== 'number') {
this.initialTitleFontSize = parseInt(this.titleContainer.getComputedStyle().fontSize, 10);
if (typeof this.initialSizing.titleFontSize !== 'number') {
this.initialSizing.titleFontSize = parseInt(this.titleContainer.getComputedStyle().fontSize, 10);
}
if (typeof this.initialSizing.titlebarHeight !== 'number') {
this.initialSizing.titlebarHeight = parseInt(this.titleContainer.getComputedStyle().height, 10);
}
// Set font size and line height
const newHeight = this.initialSizing.titlebarHeight / getZoomFactor();
this.titleContainer.style({
fontSize: `${this.initialSizing.titleFontSize / getZoomFactor()}px`,
'line-height': `${newHeight}px`
});
// Windows/Linux specific layout
if (isWindows || isLinux) {
if (typeof this.initialSizing.controlsWidth !== 'number') {
this.initialSizing.controlsWidth = parseInt(this.windowControls.getComputedStyle().width, 10);
}
if (typeof this.initialSizing.appIconWidth !== 'number') {
this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10);
}
if (typeof this.initialSizing.appIconLeftPadding !== 'number') {
this.initialSizing.appIconLeftPadding = parseInt(this.appIcon.getComputedStyle().paddingLeft, 10);
}
const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10);
const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor();
const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor();
const newAppIconPaddingLeft = this.initialSizing.appIconLeftPadding / getZoomFactor();
if (!this.menubarWidth) {
this.menubarWidth = 0;
}
// If we can center the title in the titlebar, we should
const fullWidth = parseInt(this.titleContainer.getComputedStyle().width, 10);
const titleWidth = parseInt(this.title.getComputedStyle().width, 10);
const freeSpace = fullWidth - newAppIconWidth - newControlsWidth - titleWidth;
const leftSideTitle = newAppIconWidth + (freeSpace / 2);
let bufferWidth = this.menubarWidth;
if (newAppIconWidth + this.menubarWidth < leftSideTitle) {
bufferWidth = 0;
}
// Adjust app icon mimic menubar
this.appIcon.style({
width: `${newAppIconWidth}px`,
'padding-left': `${newAppIconPaddingLeft}px`,
'margin-right': `${newControlsWidth - newAppIconWidth - newAppIconPaddingLeft + bufferWidth}px`,
'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`,
'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px`
});
// Adjust windows controls
this.windowControls.style({
'width': `${newControlsWidth}px`
});
// Hide title when toggling menu bar
let menubarToggled = this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'toggle';
if (menubarToggled && this.menubarWidth) {
this.title.style('visibility', 'hidden');
} else {
this.title.style('visibility', null);
}
}
this.titleContainer.style({ fontSize: `${this.initialTitleFontSize / getZoomFactor()}px` });
}
public layout(dimension: Dimension): Dimension[] {
this.updateLayout();
return super.layout(dimension);
}
......
......@@ -436,10 +436,9 @@ configurationRegistry.registerConfiguration({
'window.titleBarStyle': {
'type': 'string',
'enum': ['native', 'custom'],
'default': 'custom',
'default': isMacintosh ? 'custom' : 'native',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply."),
'included': isMacintosh
'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.")
},
'window.nativeTabs': {
'type': 'boolean',
......
......@@ -47,6 +47,8 @@ import { IssueChannelClient } from 'vs/platform/issue/common/issueIpc';
import { IIssueService } from 'vs/platform/issue/common/issue';
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { RelayURLService } from 'vs/platform/url/common/urlService';
import { MenubarChannelClient } from 'vs/platform/menubar/common/menubarIpc';
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
gracefulFs.gracefulify(fs); // enable gracefulFs
......@@ -227,6 +229,9 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu
const issueChannel = mainProcessClient.getChannel('issue');
serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel));
const menubarChannel = mainProcessClient.getChannel('menubar');
serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, menubarChannel));
const workspacesChannel = mainProcessClient.getChannel('workspaces');
serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
......
......@@ -65,6 +65,15 @@
cursor: pointer;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical {
padding: .5em 0;
}
.monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label,
.monaco-shell .monaco-menu .monaco-action-bar.vertical .keybinding {
padding: 0.5em 2em;
}
/* START Keyboard Focus Indication Styles */
.monaco-shell [tabindex="0"]:focus,
......
......@@ -30,6 +30,7 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart';
import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart';
import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { MenubarPart } from 'vs/workbench/browser/parts/menubar/menubarPart';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { WorkbenchLayout } from 'vs/workbench/browser/layout';
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
......@@ -41,7 +42,8 @@ import { getServices } from 'vs/platform/instantiation/common/extensions';
import { Position, Parts, IPartService, ILayoutOptions, IDimension } from 'vs/workbench/services/part/common/partService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService';
import { ContextMenuService as NativeContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService';
import { ContextMenuService as HTMLContextMenuService } from 'vs/platform/contextview/browser/contextMenuService';
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationService';
......@@ -77,11 +79,11 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService';
import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath } from 'vs/platform/windows/common/windows';
import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath, MenuBarVisibility } from 'vs/platform/windows/common/windows';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { MenuService } from 'vs/workbench/services/actions/common/menuService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
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';
......@@ -111,6 +113,9 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co
import { IEditorGroupsService, GroupDirection, preferredSideBySideGroupDirection, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { IExtensionUrlHandler, ExtensionUrlHandler } from 'vs/platform/url/electron-browser/inactiveExtensionUrlHandler';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
......@@ -146,7 +151,8 @@ const Identifiers = {
SIDEBAR_PART: 'workbench.parts.sidebar',
PANEL_PART: 'workbench.parts.panel',
EDITOR_PART: 'workbench.parts.editor',
STATUSBAR_PART: 'workbench.parts.statusbar'
STATUSBAR_PART: 'workbench.parts.statusbar',
MENUBAR_PART: 'workbench.parts.menubar'
};
function getWorkbenchStateString(state: WorkbenchState): string {
......@@ -169,6 +175,7 @@ interface IZenMode {
export class Workbench extends Disposable implements IPartService {
private static readonly sidebarHiddenStorageKey = 'workbench.sidebar.hidden';
private static readonly menubarVisibilityConfigurationKey = 'window.menuBarVisibility';
private static readonly sidebarRestoreStorageKey = 'workbench.sidebar.restore';
private static readonly panelHiddenStorageKey = 'workbench.panel.hidden';
private static readonly zenModeActiveStorageKey = 'workbench.zenmode.active';
......@@ -202,6 +209,7 @@ export class Workbench extends Disposable implements IPartService {
private workbenchLayout: WorkbenchLayout;
private titlebarPart: TitlebarPart;
private menubarPart: MenubarPart;
private activitybarPart: ActivitybarPart;
private sidebarPart: SidebarPart;
private panelPart: PanelPart;
......@@ -217,6 +225,7 @@ export class Workbench extends Disposable implements IPartService {
private sideBarPosition: Position;
private panelPosition: Position;
private panelHidden: boolean;
private menubarHidden: boolean;
private zenMode: IZenMode;
private centeredEditorLayoutActive: boolean;
private fontAliasing: FontAliasingOption;
......@@ -241,7 +250,9 @@ export class Workbench extends Disposable implements IPartService {
@IWorkbenchThemeService private themeService: WorkbenchThemeService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWindowService private windowService: IWindowService,
@INotificationService private notificationService: NotificationService
@INotificationService private notificationService: NotificationService,
@IContextViewService private contextViewService: ContextViewService,
@ITelemetryService private telemetryService: TelemetryService
) {
super();
......@@ -345,8 +356,12 @@ export class Workbench extends Disposable implements IPartService {
// List
serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));
// Context Menu
serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService));
// Use themable context menus when custom titlebar is enabled to match custom menubar
if (!isMacintosh && this.getCustomTitleBarStyle() === 'custom') {
serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, null, this.telemetryService, this.notificationService, this.contextViewService));
} else {
serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService));
}
// Menus/Actions
serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
......@@ -393,9 +408,13 @@ export class Workbench extends Disposable implements IPartService {
this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART);
this._register(toDisposable(() => this.titlebarPart.shutdown()));
serviceCollection.set(ITitleService, this.titlebarPart);
// History
serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService));
// Menubar
this.menubarPart = this.instantiationService.createInstance(MenubarPart, Identifiers.MENUBAR_PART);
// Backup File Service
this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath);
serviceCollection.set(IBackupFileService, this.backupFileService);
......@@ -562,6 +581,9 @@ export class Workbench extends Disposable implements IPartService {
this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout);
}
}
const newMenubarVisibility = this.configurationService.getValue<MenuBarVisibility>(Workbench.menubarVisibilityConfigurationKey);
this.setMenubarHidden(newMenubarVisibility, skipLayout);
}
//#endregion
......@@ -826,6 +848,10 @@ export class Workbench extends Disposable implements IPartService {
// Panel position
this.setPanelPositionFromStorageOrConfig();
// Menubar visibility
const menuBarVisibility = this.configurationService.getValue<MenuBarVisibility>(Workbench.menubarVisibilityConfigurationKey);
this.setMenubarHidden(menuBarVisibility, true);
// Statusbar visibility
const statusBarVisible = this.configurationService.getValue<string>(Workbench.statusbarVisibleConfigurationKey);
this.statusBarHidden = !statusBarVisible;
......@@ -858,12 +884,8 @@ export class Workbench extends Disposable implements IPartService {
}
private getCustomTitleBarStyle(): 'custom' {
if (!isMacintosh) {
return null; // custom title bar is only supported on Mac currently
}
const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
if (isDev) {
if (isMacintosh && isDev) {
return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647
}
......@@ -918,6 +940,7 @@ export class Workbench extends Disposable implements IPartService {
this.workbench.getHTMLElement(),
{
titlebar: this.titlebarPart,
menubar: this.menubarPart,
activitybar: this.activitybarPart,
editor: this.editorPart,
sidebar: this.sidebarPart,
......@@ -960,6 +983,7 @@ export class Workbench extends Disposable implements IPartService {
// Create Parts
this.createTitlebarPart();
this.createMenubarPart();
this.createActivityBarPart();
this.createSidebarPart();
this.createEditorPart();
......@@ -983,6 +1007,20 @@ export class Workbench extends Disposable implements IPartService {
this.titlebarPart.create(titlebarContainer.getHTMLElement());
}
private createMenubarPart(): void {
const menubarContainer = $(this.workbench).div({
'class': ['part', 'menubar'],
id: Identifiers.MENUBAR_PART,
role: 'menubar'
});
this.menubarPart.create(menubarContainer.getHTMLElement());
this._register(this.menubarPart.onVisibilityChange((dimension => {
this._onMenubarVisibilityChange.fire(dimension);
})));
}
private createActivityBarPart(): void {
const activitybarPartContainer = $(this.workbench)
.div({
......@@ -1094,9 +1132,12 @@ export class Workbench extends Disposable implements IPartService {
//#region IPartService
private _onTitleBarVisibilityChange: Emitter<void> = new Emitter<void>();
private _onTitleBarVisibilityChange: Emitter<void> = this._register(new Emitter<void>());
get onTitleBarVisibilityChange(): Event<void> { return this._onTitleBarVisibilityChange.event; }
private _onMenubarVisibilityChange: Emitter<DOM.Dimension> = this._register(new Emitter<DOM.Dimension>());
get onMenubarVisibilityChange(): Event<DOM.Dimension> { return this._onMenubarVisibilityChange.event; }
get onEditorLayout(): Event<IDimension> { return this.editorPart.onDidLayout; }
isCreated(): boolean {
......@@ -1119,6 +1160,9 @@ export class Workbench extends Disposable implements IPartService {
case Parts.TITLEBAR_PART:
container = this.titlebarPart.getContainer();
break;
case Parts.MENUBAR_PART:
container = this.menubarPart.getContainer();
break;
case Parts.ACTIVITYBAR_PART:
container = this.activitybarPart.getContainer();
break;
......@@ -1142,7 +1186,9 @@ export class Workbench extends Disposable implements IPartService {
isVisible(part: Parts): boolean {
switch (part) {
case Parts.TITLEBAR_PART:
return this.getCustomTitleBarStyle() && !browser.isFullscreen();
return this.getCustomTitleBarStyle() === 'custom' && !browser.isFullscreen();
case Parts.MENUBAR_PART:
return this.getCustomTitleBarStyle() === 'custom' && !this.menubarHidden;
case Parts.SIDEBAR_PART:
return !this.sideBarHidden;
case Parts.PANEL_PART:
......@@ -1436,6 +1482,14 @@ export class Workbench extends Disposable implements IPartService {
this.workbenchLayout.layout();
}
setMenubarHidden(visibility: MenuBarVisibility, skipLayout: boolean): void {
this.menubarHidden = visibility === 'hidden' || (visibility === 'default' && browser.isFullscreen());
if (!skipLayout) {
this.workbenchLayout.layout();
}
}
getPanelPosition(): Position {
return this.panelPosition;
}
......
......@@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { GlobalNewUntitledFileAction, ShowOpenedFileInNewWindow, CopyPathAction, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, ShowOpenedFileInNewWindow, CopyPathAction, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/parts/files/electron-browser/saveErrorHandler';
import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
......@@ -40,6 +40,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshExplorerView, R
registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'File: New Untitled File', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithClipboardAction, CompareWithClipboardAction.ID, CompareWithClipboardAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_C) }), 'File: Compare Active File with Clipboard', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category);
// Commands
CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand);
......
......@@ -8,6 +8,7 @@
import 'vs/css!./media/fileactions';
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { sequence, ITask, always } from 'vs/base/common/async';
import * as paths from 'vs/base/common/paths';
......@@ -24,7 +25,7 @@ import { ITree, IHighlightEvent } from 'vs/base/parts/tree/browser/tree';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IFileService, IFileStat, AutoSaveConfiguration } from 'vs/platform/files/common/files';
import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor';
import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel';
import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView';
......@@ -1185,6 +1186,36 @@ export class RefreshViewExplorerAction extends Action {
}
}
export class ToggleAutoSaveAction extends Action {
public static readonly ID = 'workbench.action.toggleAutoSave';
public static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save");
constructor(
id: string,
label: string,
@IConfigurationService private configurationService: IConfigurationService
) {
super(id, label);
}
public run(): TPromise<any> {
const setting = this.configurationService.inspect('files.autoSave');
let userAutoSaveConfig = setting.user;
if (types.isUndefinedOrNull(userAutoSaveConfig)) {
userAutoSaveConfig = setting.default; // use default if setting not defined
}
let newAutoSaveValue: string;
if ([AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => s === userAutoSaveConfig)) {
newAutoSaveValue = AutoSaveConfiguration.OFF;
} else {
newAutoSaveValue = AutoSaveConfiguration.AFTER_DELAY;
}
return this.configurationService.updateValue('files.autoSave', newAutoSaveValue, ConfigurationTarget.USER);
}
}
export abstract class BaseSaveAllAction extends BaseErrorReportingAction {
private toDispose: IDisposable[];
private lastIsDirty: boolean;
......
......@@ -77,8 +77,8 @@ export class SettingsChangeRelauncher implements IWorkbenchContribution {
private onConfigurationChange(config: IConfiguration, notify: boolean): void {
let changed = false;
// macOS: Titlebar style
if (isMacintosh && config.window && config.window.titleBarStyle !== this.titleBarStyle && (config.window.titleBarStyle === 'native' || config.window.titleBarStyle === 'custom')) {
// 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;
}
......
......@@ -14,7 +14,8 @@ export enum Parts {
PANEL_PART,
EDITOR_PART,
STATUSBAR_PART,
TITLEBAR_PART
TITLEBAR_PART,
MENUBAR_PART
}
export enum Position {
......@@ -43,6 +44,11 @@ export interface IPartService {
*/
onTitleBarVisibilityChange: Event<void>;
/**
* Emits when the visibility of the menubar changes.
*/
onMenubarVisibilityChange: Event<IDimension>;
/**
* Emits when the editor part's layout changes.
*/
......
......@@ -73,6 +73,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { Dimension } from 'vs/base/browser/dom';
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, void 0);
......@@ -374,12 +375,17 @@ export class TestPartService implements IPartService {
public _serviceBrand: any;
private _onTitleBarVisibilityChange = new Emitter<void>();
private _onMenubarVisibilityChange = new Emitter<Dimension>();
private _onEditorLayout = new Emitter<IDimension>();
public get onTitleBarVisibilityChange(): Event<void> {
return this._onTitleBarVisibilityChange.event;
}
public get onMenubarVisibilityChange(): Event<Dimension> {
return this._onMenubarVisibilityChange.event;
}
public get onEditorLayout(): Event<IDimension> {
return this._onEditorLayout.event;
}
......@@ -958,11 +964,16 @@ export class TestWindowService implements IWindowService {
public _serviceBrand: any;
onDidChangeFocus: Event<boolean> = new Emitter<boolean>().event;
onDidChangeMaximize: Event<boolean>;
isFocused(): TPromise<boolean> {
return TPromise.as(false);
}
isMaximized(): TPromise<boolean> {
return TPromise.as(false);
}
getConfiguration(): IWindowConfiguration {
return Object.create(null);
}
......@@ -1027,6 +1038,18 @@ export class TestWindowService implements IWindowService {
return TPromise.as(void 0);
}
maximizeWindow(): TPromise<void> {
return TPromise.as(void 0);
}
unmaximizeWindow(): TPromise<void> {
return TPromise.as(void 0);
}
minimizeWindow(): TPromise<void> {
return TPromise.as(void 0);
}
openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise<void> {
return TPromise.as(void 0);
}
......@@ -1104,6 +1127,8 @@ export class TestWindowsService implements IWindowsService {
onWindowOpen: Event<number>;
onWindowFocus: Event<number>;
onWindowBlur: Event<number>;
onWindowMaximize: Event<number>;
onWindowUnmaximize: Event<number>;
isFocused(windowId: number): TPromise<boolean> {
return TPromise.as(false);
......@@ -1189,6 +1214,10 @@ export class TestWindowsService implements IWindowsService {
return TPromise.as(void 0);
}
minimizeWindow(windowId: number): TPromise<void> {
return TPromise.as(void 0);
}
unmaximizeWindow(windowId: number): TPromise<void> {
return TPromise.as(void 0);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册