提交 15bae1e7 编写于 作者: S SteVen Batten

handling zoom for titlebar controls and icons as well as toggling title

上级 9f039873
......@@ -31,7 +31,6 @@ import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPar
import { MenubarPart } from 'vs/workbench/browser/parts/menubar/menubarPart';
const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30;
const MAXIMIZED_TITLE_BAR_HEIGHT = isMacintosh ? 22 : 23;
const STATUS_BAR_HEIGHT = 22;
const ACTIVITY_BAR_WIDTH = 50;
......@@ -65,7 +64,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
private _sidebarWidth: number;
private sidebarHeight: number;
private titlebarBaseHeight: number;
private titlebarHeight: number;
private menubarHeight: number;
private headingHeight: number;
......@@ -109,8 +107,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
this.sashXTwo = new Sash(this.workbenchContainer, this);
this.sashY = new Sash(this.workbenchContainer, this, { orientation: Orientation.HORIZONTAL });
this.onMaximizeChange(false);
this.registerListeners();
}
......@@ -228,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
},
......@@ -434,8 +433,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
}
this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height;
this.titlebarHeight = isTitlebarHidden ? 0 : this.titlebarBaseHeight / getZoomFactor(); // adjust for zoom prevention
this.menubarHeight = isMenubarHidden ? 0 : this.titlebarBaseHeight;
this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / getZoomFactor(); // adjust for zoom prevention
this.menubarHeight = isMenubarHidden ? 0 : this.partLayoutInfo.menubar.height / getZoomFactor();
this.headingHeight = Math.max(this.menubarHeight, this.titlebarHeight);
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.headingHeight;
......@@ -664,9 +663,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
this.sashXTwo.show();
}
let menubarOffset = this.parts.menubar.getMenubarItemsDimensions();
this.parts.titlebar.setTitleOffset(menubarOffset);
// 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));
......@@ -679,12 +675,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr
this.contextViewService.layout();
}
onMaximizeChange(maximized: boolean): void {
this.titlebarBaseHeight = maximized ? MAXIMIZED_TITLE_BAR_HEIGHT : this.partLayoutInfo.titlebar.height;
this.layout();
}
getVerticalSashTop(sash: Sash): number {
return this.headingHeight;
}
......
......@@ -6,10 +6,11 @@
.monaco-workbench > .part.menubar {
display: flex;
position: absolute;
order: 3;
font-size: 12px;
box-sizing: border-box;
padding-left: 35px;
padding-left: 30px;
padding-right: 150px;
height: 30px;
}
.monaco-workbench.fullscreen > .part.menubar {
......@@ -30,6 +31,11 @@
z-index: 10;
cursor: pointer;
-webkit-app-region: no-drag;
zoom: 1;
}
.monaco-workbench > .part.menubar > .menubar-menu-title {
zoom: 1;
}
.monaco-workbench > .part.menubar > .menubar-menu-button.open,
......
......@@ -18,7 +18,7 @@ import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/commo
import { ActionRunner, IActionRunner, IAction } from 'vs/base/common/actions';
import { Builder, $ } from 'vs/base/browser/builder';
import { Separator, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { EventType } from 'vs/base/browser/dom';
import { EventType, Dimension } from 'vs/base/browser/dom';
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { isWindows, isMacintosh } from 'vs/base/common/platform';
......@@ -29,7 +29,7 @@ import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRe
import URI from 'vs/base/common/uri';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { Color } from 'vs/base/common/color';
import { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { domEvent } from 'vs/base/browser/event';
......@@ -97,6 +97,7 @@ export class MenubarPart extends Part {
private actionRunner: IActionRunner;
private container: Builder;
private _isFocused: boolean;
private _onVisibilityChange: Emitter<Dimension>;
constructor(
id: string,
......@@ -139,6 +140,8 @@ export class MenubarPart extends Part {
this.topLevelMenus[topLevelMenuName].onDidChange(() => this.setupNativeMenubar());
}
this._onVisibilityChange = new Emitter<Dimension>();
this.setupNativeMenubar();
this.isFocused = false;
......@@ -202,7 +205,7 @@ export class MenubarPart extends Part {
if (!this._isFocused && this.currentMenubarVisibility === 'toggle') {
if (this.container) {
this.container.style('display', 'none');
this.hideMenubar();
}
}
}
......@@ -217,12 +220,22 @@ export class MenubarPart extends Part {
}
}
private hideMenubar(): void {
this._onVisibilityChange.fire(new Dimension(0, 0));
this.container.style('visibility', 'hidden');
}
private showMenubar(): void {
this._onVisibilityChange.fire(this.getMenubarItemsDimensions());
this.container.style('visibility', null);
}
private onAltKeyToggled(altKeyDown: boolean): void {
if (this.currentMenubarVisibility === 'toggle') {
if (altKeyDown) {
this.container.style('display', null);
this.showMenubar();
} else if (!this.isFocused) {
this.container.style('display', 'none');
this.hideMenubar();
}
}
......@@ -341,10 +354,6 @@ export class MenubarPart extends Part {
this.container.empty();
this.container.attr('role', 'menubar');
if (this.currentMenubarVisibility === 'toggle') {
this.container.style('display', 'none');
}
this.customMenus = [];
let idx = 0;
......@@ -560,8 +569,6 @@ export class MenubarPart extends Part {
menuHolder.addClass('menubar-menu-items-holder-open context-view');
menuHolder.style({
// 'background-color': this.getColor(TITLE_BAR_ACTIVE_BACKGROUND),
// 'color': this.getColor(TITLE_BAR_ACTIVE_FOREGROUND),
'top': `${this.container.getClientArea().height}px`
});
......@@ -613,12 +620,32 @@ export class MenubarPart extends Part {
}
}
public getMenubarItemsDimensions(): number {
public get onVisibilityChange(): Event<Dimension> {
return this._onVisibilityChange.event;
}
public layout(dimension: Dimension): Dimension[] {
// this.container.style({
// 'transform': `scale(${1 / browser.getZoomFactor()}, ${1 /browser.getZoomFactor()})`
// });
if (this.currentMenubarVisibility === 'toggle') {
this.hideMenubar();
} else {
this.showMenubar();
}
return super.layout(dimension);
}
public getMenubarItemsDimensions(): Dimension {
if (this.customMenus) {
return this.customMenus[this.customMenus.length - 1].titleElement.getHTMLElement().getBoundingClientRect().right;
const left = this.customMenus[0].titleElement.getHTMLElement().getBoundingClientRect().left;
const right = this.customMenus[this.customMenus.length - 1].titleElement.getHTMLElement().getBoundingClientRect().right;
return new Dimension(right - left, this.container.getClientArea().height);
}
return 0;
return new Dimension(0, 0);
}
public createContentArea(parent: HTMLElement): HTMLElement {
......
......@@ -13,11 +13,11 @@
align-items: center;
justify-content: center;
user-select: none;
-webkit-app-region: no-drag;
-webkit-app-region: drag;
zoom: 1; /* prevent zooming */
line-height: 22px;
height: 22px;
display: flex;
order: 1;
}
.monaco-workbench > .part.titlebar > .window-title {
......@@ -25,66 +25,72 @@
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
zoom: 1; /* prevent zooming */
-webkit-app-region: drag;
zoom: 1; /* prevent zooming */
}
/***********************\
| Windows and Linux CSS |
\***********************/
.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 > .window-title,
.monaco-workbench.linux > .part.titlebar > .window-title {
box-sizing: border-box;
text-align: center;
padding-right: 150px;
padding-left: 150px;
.monaco-workbench.windows > .part.titlebar > .resizer,
.monaco-workbench.linux > .part.titlebar > .resizer {
-webkit-app-region: no-drag;
position: absolute;
margin: 1px 2px;
top: 0;
width: 100%;
left: calc(-100vw + 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 {
height: 15px;
width: 15px;
padding-left: 10px;
margin-right: 113px;
-webkit-app-region: no-drag;
margin: 0 10px;
position: relative;
z-index: 99;
order: 2;
order: 1;
image-rendering: crisp-edges;
}
.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: 95%;
width: 46px;
background-size: 10px;
background-position: center center;
background-repeat: no-repeat;
}
.monaco-workbench > .part.titlebar > .window-controls-container {
display: flex;
flex-grow: 0;
flex-shrink: 0;
margin-left: auto;
text-align: center;
position: relative;
z-index: 99;
-webkit-app-region: no-drag;
height: 100%;
order: 4;
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;
......@@ -110,22 +116,22 @@
order: 2;
}
.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-maximize {
.monaco-workbench > .part.titlebar > .window-controls-container > .window-maximize {
background-image: url('chrome-maximize-dark.svg');
order: 2;
}
.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-maximize {
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-maximize {
background-image: url('chrome-maximize.svg');
order: 2;
}
.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-minimize {
.monaco-workbench > .part.titlebar > .window-controls-container > .window-minimize {
background-image: url('chrome-minimize-dark.svg');
order: 1;
}
.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-minimize {
.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-minimize {
background-image: url('chrome-minimize.svg');
order: 1;
}
......@@ -138,7 +144,7 @@
background-color: rgba(0, 0, 0, 0.1);
}
.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-close:hover {
.monaco-workbench > .part.titlebar > .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';
......@@ -32,6 +32,7 @@ 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 { IPartService } from 'vs/workbench/services/part/common/partService';
export class TitlebarPart extends Part implements ITitleService {
......@@ -45,9 +46,19 @@ 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;
} = {};
private isInactive: boolean;
......@@ -63,6 +74,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);
......@@ -81,6 +93,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 {
......@@ -99,6 +112,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
......@@ -157,16 +176,6 @@ export class TitlebarPart extends Part implements ITitleService {
return title;
}
public setTitleOffset(offset: number) {
if (this.title) {
if (offset) {
this.title.style('padding-left', offset + 'px');
} else {
this.title.style('padding-left', null);
}
}
}
public updateProperties(properties: ITitleProperties): void {
const isAdmin = typeof properties.isAdmin === 'boolean' ? properties.isAdmin : this.properties.isAdmin;
const isPure = typeof properties.isPure === 'boolean' ? properties.isPure : this.properties.isPure;
......@@ -240,13 +249,17 @@ export class TitlebarPart extends Part implements ITitleService {
this.titleContainer = $(parent);
if (!isMacintosh) {
$(this.titleContainer).img({
// App Icon
this.appIcon = $(this.titleContainer).img({
class: 'window-appicon',
src: paths.join(this.environmentService.appRoot, '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
......@@ -272,13 +285,13 @@ export class TitlebarPart extends Part implements ITitleService {
});
if (!isMacintosh) {
let windowControls = $(this.titleContainer).div({ class: 'window-controls-container' });
this.windowControls = $(this.titleContainer).div({ class: 'window-controls-container' });
$(windowControls).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => {
$(this.windowControls).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => {
this.windowService.minimizeWindow().then(null, errors.onUnexpectedError);
});
let maxRestore = $(windowControls).div({ class: 'window-icon window-max-restore' });
let maxRestore = $(this.windowControls).div({ class: 'window-icon window-max-restore' });
maxRestore.on(EventType.CLICK, (e, builder) => {
this.windowService.isMaximized().then((maximized) => {
if (maximized) {
......@@ -289,7 +302,7 @@ export class TitlebarPart extends Part implements ITitleService {
}).then(null, errors.onUnexpectedError);
});
$(windowControls).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => {
$(this.windowControls).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => {
this.windowService.closeWindow().then(null, errors.onUnexpectedError);
});
......@@ -413,13 +426,72 @@ 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);
}
this.titleContainer.style({ fontSize: `${this.initialTitleFontSize / getZoomFactor()}px` });
// 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`
});
if (!isMacintosh) {
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;
}
// Adjust app icon
this.appIcon.style({
width: `${newAppIconWidth}px`,
'padding-left': `${newAppIconPaddingLeft}px`,
'margin-right': `${newControlsWidth - newAppIconWidth - newAppIconPaddingLeft + this.menubarWidth}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);
}
}
}
public layout(dimension: Dimension): Dimension[] {
this.updateLayout();
return super.layout(dimension);
}
......
......@@ -954,15 +954,6 @@ export class Workbench extends Disposable implements IPartService {
this.notificationsCenter,
this.notificationsToasts
);
if (!isMacintosh && this.getCustomTitleBarStyle()) {
this.windowService.isMaximized().then((max) => {
this.workbenchLayout.onMaximizeChange(max);
this.workbenchLayout.layout();
});
this.windowService.onDidChangeMaximize(this.workbenchLayout.onMaximizeChange, this.workbenchLayout);
}
}
private renderWorkbench(): void {
......@@ -1026,6 +1017,9 @@ export class Workbench extends Disposable implements IPartService {
});
this.menubarPart.create(menubarContainer.getHTMLElement());
this.menubarPart.onVisibilityChange((dimension => {
this._onMenubarVisibilityChange.fire(dimension);
}));
}
private createActivityBarPart(): void {
......@@ -1142,6 +1136,9 @@ export class Workbench extends Disposable implements IPartService {
private _onTitleBarVisibilityChange: Emitter<void> = new Emitter<void>();
get onTitleBarVisibilityChange(): Event<void> { return this._onTitleBarVisibilityChange.event; }
private _onMenubarVisibilityChange: Emitter<DOM.Dimension> = new Emitter<DOM.Dimension>();
get onMenubarVisibilityChange(): Event<DOM.Dimension> { return this._onMenubarVisibilityChange.event; }
get onEditorLayout(): Event<IDimension> { return this.editorPart.onDidLayout; }
isCreated(): boolean {
......
......@@ -44,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;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册