titlebarPart.ts 4.9 KB
Newer Older
B
Benjamin Pasero 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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 'vs/css!./media/titlebarpart';
B
Benjamin Pasero 已提交
9
import { TPromise } from 'vs/base/common/winjs.base';
B
Benjamin Pasero 已提交
10
import { Builder, $, Dimension } from 'vs/base/browser/builder';
B
Benjamin Pasero 已提交
11 12
import * as DOM from 'vs/base/browser/dom';
import * as paths from 'vs/base/common/paths';
B
Benjamin Pasero 已提交
13 14 15
import { Part } from 'vs/workbench/browser/part';
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
import { getZoomFactor } from 'vs/base/browser/browser';
B
Benjamin Pasero 已提交
16 17 18 19 20
import { IWindowService, IWindowsService } 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';
import { IAction, Action } from 'vs/base/common/actions';
B
Benjamin Pasero 已提交
21 22 23 24 25 26 27 28 29

export class TitlebarPart extends Part implements ITitleService {

	public _serviceBrand: any;

	private titleContainer: Builder;
	private title: Builder;
	private pendingTitle: string;
	private initialTitleFontSize: number;
B
Benjamin Pasero 已提交
30
	private representedFileName: string;
B
Benjamin Pasero 已提交
31

32 33
	constructor(
		id: string,
B
Benjamin Pasero 已提交
34 35 36
		@IContextMenuService private contextMenuService: IContextMenuService,
		@IWindowService private windowService: IWindowService,
		@IWindowsService private windowsService: IWindowsService
37 38 39 40
	) {
		super(id);
	}

B
Benjamin Pasero 已提交
41 42 43 44 45 46 47 48 49
	public createContentArea(parent: Builder): Builder {
		this.titleContainer = $(parent);

		// Title
		this.title = $(this.titleContainer).div({ class: 'window-title' });
		if (this.pendingTitle) {
			this.title.text(this.pendingTitle);
		}

50 51 52 53 54 55 56
		// Maximize/Restore on doubleclick
		this.titleContainer.on(DOM.EventType.DBLCLICK, (e) => {
			DOM.EventHelper.stop(e);

			this.onTitleDoubleclick();
		});

B
Benjamin Pasero 已提交
57 58 59 60 61 62 63 64 65
		// Context menu on title
		this.title.on([DOM.EventType.CONTEXT_MENU, DOM.EventType.MOUSE_DOWN], (e: MouseEvent) => {
			if (e.type === DOM.EventType.CONTEXT_MENU || e.metaKey) {
				DOM.EventHelper.stop(e);

				this.onContextMenu(e);
			}
		});

B
Benjamin Pasero 已提交
66 67 68
		return this.titleContainer;
	}

69 70 71 72 73 74 75 76 77 78
	private onTitleDoubleclick(): void {
		this.windowService.isMaximized().then(maximized => {
			if (maximized) {
				this.windowService.unmaximizeWindow().done(null, errors.onUnexpectedError);
			} else {
				this.windowService.maximizeWindow().done(null, errors.onUnexpectedError);
			}
		}, errors.onUnexpectedError);
	}

B
Benjamin Pasero 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	private onContextMenu(e: MouseEvent): void {

		// Find target anchor
		const event = new StandardMouseEvent(e);
		const anchor = { x: event.posx, y: event.posy };

		// Show menu
		const actions = this.getContextMenuActions();
		if (actions.length) {
			this.contextMenuService.showContextMenu({
				getAnchor: () => anchor,
				getActions: () => TPromise.as(actions),
				onHide: () => actions.forEach(a => a.dispose())
			});
		}
	}

	private getContextMenuActions(): IAction[] {
		const actions: IAction[] = [];

		if (this.representedFileName) {
			const segments = this.representedFileName.split(paths.sep);
			for (let i = segments.length; i > 0; i--) {
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
				const isFile = (i === segments.length);

				let pathOffset = i;
				if (!isFile) {
					pathOffset++; // for segments which are not the file name we want to open the folder
				}

				const path = segments.slice(0, pathOffset).join(paths.sep);

				let label = paths.basename(path);
				if (!isFile) {
					label = paths.basename(paths.dirname(path));
				}

				actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService));
B
Benjamin Pasero 已提交
117 118 119 120 121 122
			}
		}

		return actions;
	}

B
Benjamin Pasero 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135
	public updateTitle(title: string): void {

		// Always set the native window title to identify us properly to the OS
		window.document.title = title;

		// Apply if we can
		if (this.title) {
			this.title.text(title);
		} else {
			this.pendingTitle = title;
		}
	}

B
Benjamin Pasero 已提交
136 137 138 139 140 141 142 143 144
	public setRepresentedFilename(path: string): void {

		// Apply to window
		this.windowService.setRepresentedFilename(path);

		// Keep for context menu
		this.representedFileName = path;
	}

B
Benjamin Pasero 已提交
145 146 147 148 149 150 151 152 153 154
	public layout(dimension: Dimension): Dimension[] {

		// 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);
		}
		this.titleContainer.style({ fontSize: `${this.initialTitleFontSize / getZoomFactor()}px` });

		return super.layout(dimension);
	}
B
Benjamin Pasero 已提交
155 156 157 158
}

class ShowItemInFolderAction extends Action {

159 160
	constructor(private path: string, label: string, private windowsService: IWindowsService) {
		super('showItemInFolder.action.id', label);
B
Benjamin Pasero 已提交
161 162 163 164 165
	}

	public run(): TPromise<void> {
		return this.windowsService.showItemInFolder(this.path);
	}
B
Benjamin Pasero 已提交
166
}