/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, latch, anyEvent } from 'vs/base/common/event'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ExportData } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; export const IWindowsService = createDecorator('windowsService'); export interface INativeOpenDialogOptions { windowId?: number; forceNewWindow?: boolean; dialogOptions?: OpenDialogOptions; telemetryEventName?: string; telemetryExtraData?: ITelemetryData; } export interface IEnterWorkspaceResult { workspace: IWorkspaceIdentifier; backupPath: string; } export interface CrashReporterStartOptions { companyName?: string; submitURL: string; productName?: string; uploadToServer?: boolean; ignoreSystemCrashHandler?: boolean; extra?: any; crashesDirectory?: string; } export interface OpenDialogOptions { title?: string; defaultPath?: string; buttonLabel?: string; filters?: FileFilter[]; properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>; message?: string; } export interface FileFilter { extensions: string[]; name: string; } export interface MessageBoxOptions { type?: string; buttons?: string[]; defaultId?: number; title?: string; message: string; detail?: string; checkboxLabel?: string; checkboxChecked?: boolean; cancelId?: number; noLink?: boolean; normalizeAccessKeys?: boolean; } export interface SaveDialogOptions { title?: string; defaultPath?: string; buttonLabel?: string; filters?: FileFilter[]; message?: string; nameFieldLabel?: string; showsTagField?: boolean; } export interface INewWindowOptions { } export interface IDevToolsOptions { mode: 'right' | 'bottom' | 'undocked' | 'detach'; } export interface IWindowsService { _serviceBrand: any; onWindowOpen: Event; onWindowFocus: Event; onWindowBlur: Event; onWindowMaximize: Event; onWindowUnmaximize: Event; onRecentlyOpenedChange: Event; // Dialogs pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; showMessageBox(windowId: number, options: MessageBoxOptions): TPromise; showSaveDialog(windowId: number, options: SaveDialogOptions): TPromise; showOpenDialog(windowId: number, options: OpenDialogOptions): TPromise; reloadWindow(windowId: number, args?: ParsedArgs): TPromise; openDevTools(windowId: number, options?: IDevToolsOptions): TPromise; toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; enterWorkspace(windowId: number, path: string): TPromise; createAndEnterWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], path?: string): TPromise; saveAndEnterWorkspace(windowId: number, path: string): TPromise; toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; addRecentlyOpened(files: URI[]): TPromise; removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): TPromise; clearRecentlyOpened(): TPromise; getRecentlyOpened(windowId: number): TPromise; focusWindow(windowId: number): TPromise; closeWindow(windowId: number): TPromise; isFocused(windowId: number): TPromise; isMaximized(windowId: number): TPromise; maximizeWindow(windowId: number): TPromise; unmaximizeWindow(windowId: number): TPromise; minimizeWindow(windowId: number): TPromise; onWindowTitleDoubleClick(windowId: number): TPromise; setDocumentEdited(windowId: number, flag: boolean): TPromise; quit(): TPromise; relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise; // macOS Native Tabs newWindowTab(): TPromise; showPreviousWindowTab(): TPromise; showNextWindowTab(): TPromise; moveWindowTabToNewWindow(): TPromise; mergeAllWindowTabs(): TPromise; toggleWindowTabsBar(): TPromise; // macOS TouchBar updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): TPromise; // Shared process whenSharedProcessReady(): TPromise; toggleSharedProcess(): TPromise; // Global methods openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; openNewWindow(options?: INewWindowOptions): TPromise; showWindow(windowId: number): TPromise; getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getWindowCount(): TPromise; log(severity: string, ...messages: string[]): TPromise; showItemInFolder(path: string): TPromise; getActiveWindowId(): TPromise; // This needs to be handled from browser process to prevent // foreground ordering issues on Windows openExternal(url: string): TPromise; // TODO: this is a bit backwards startCrashReporter(config: CrashReporterStartOptions): TPromise; openAboutDialog(): TPromise; resolveProxy(windowId: number, url: string): Promise; } export const IWindowService = createDecorator('windowService'); export interface IMessageBoxResult { button: number; checkboxChecked?: boolean; } export interface IWindowService { _serviceBrand: any; onDidChangeFocus: Event; onDidChangeMaximize: Event; getConfiguration(): IWindowConfiguration; getCurrentWindowId(): number; pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; reloadWindow(args?: ParsedArgs): TPromise; openDevTools(options?: IDevToolsOptions): TPromise; toggleDevTools(): TPromise; closeWorkspace(): TPromise; updateTouchBar(items: ISerializableCommandAction[][]): TPromise; enterWorkspace(path: string): TPromise; createAndEnterWorkspace(folders?: IWorkspaceFolderCreationData[], path?: string): TPromise; saveAndEnterWorkspace(path: string): TPromise; toggleFullScreen(): TPromise; setRepresentedFilename(fileName: string): TPromise; getRecentlyOpened(): TPromise; focusWindow(): TPromise; closeWindow(): TPromise; openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; isFocused(): TPromise; setDocumentEdited(flag: boolean): TPromise; isMaximized(): TPromise; maximizeWindow(): TPromise; unmaximizeWindow(): TPromise; minimizeWindow(): TPromise; onWindowTitleDoubleClick(): TPromise; show(): TPromise; showMessageBox(options: MessageBoxOptions): TPromise; showSaveDialog(options: SaveDialogOptions): TPromise; showOpenDialog(options: OpenDialogOptions): TPromise; resolveProxy(url: string): Promise; } export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden'; export interface IWindowsConfiguration { window: IWindowSettings; } export interface IWindowSettings { openFilesInNewWindow: 'on' | 'off' | 'default'; openFoldersInNewWindow: 'on' | 'off' | 'default'; openWithoutArgumentsInNewWindow: 'on' | 'off'; restoreWindows: 'all' | 'folders' | 'one' | 'none'; restoreFullscreen: boolean; zoomLevel: number; titleBarStyle: 'native' | 'custom'; autoDetectHighContrast: boolean; menuBarVisibility: MenuBarVisibility; newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen'; nativeTabs: boolean; enableMenuBarMnemonics: boolean; closeWhenEmpty: boolean; clickThroughInactive: boolean; } export const enum OpenContext { // opening when running from the command line CLI, // macOS only: opening from the dock (also when opening files to a running instance from desktop) DOCK, // opening from the main application window MENU, // opening from a file or folder dialog DIALOG, // opening from the OS's UI DESKTOP, // opening through the API API } export const enum ReadyState { /** * This window has not loaded any HTML yet */ NONE, /** * This window is loading HTML */ LOADING, /** * This window is navigating to another HTML */ NAVIGATING, /** * This window is done loading HTML */ READY } export interface IPath extends IPathData { // the file path to open within a Code instance fileUri?: URI; } export interface IPathsToWaitFor extends IPathsToWaitForData { paths: IPath[]; } export interface IPathsToWaitForData { paths: IPathData[]; waitMarkerFilePath: string; } export interface IPathData { // the file path to open within a Code instance fileUri?: UriComponents; // the line number in the file path to open lineNumber?: number; // the column number in the file path to open columnNumber?: number; } export interface IOpenFileRequest { filesToOpen?: IPathData[]; filesToCreate?: IPathData[]; filesToDiff?: IPathData[]; filesToWait?: IPathsToWaitForData; termProgram?: string; } export interface IAddFoldersRequest { foldersToAdd: UriComponents[]; } export interface IWindowConfiguration extends ParsedArgs { machineId: string; windowId: number; logLevel: LogLevel; mainPid: number; appRoot: string; execPath: string; isInitialStartup?: boolean; userEnv: IProcessEnvironment; nodeCachedDataDir: string; backupPath?: string; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; zoomLevel?: number; fullscreen?: boolean; maximized?: boolean; highContrast?: boolean; frameless?: boolean; accessibilitySupport?: boolean; perfStartTime?: number; perfAppReady?: number; perfWindowLoadTime?: number; perfEntries: ExportData; filesToOpen?: IPath[]; filesToCreate?: IPath[]; filesToDiff?: IPath[]; filesToWait?: IPathsToWaitFor; termProgram?: string; } export interface IRunActionInWindowRequest { id: string; from: 'menu' | 'touchbar' | 'mouse'; } export class ActiveWindowManager implements IDisposable { private disposables: IDisposable[] = []; private firstActiveWindowIdPromise: TPromise | null; private _activeWindowId: number | undefined; constructor(@IWindowsService windowsService: IWindowsService) { const onActiveWindowChange = latch(anyEvent(windowsService.onWindowOpen, windowsService.onWindowFocus)); onActiveWindowChange(this.setActiveWindow, this, this.disposables); this.firstActiveWindowIdPromise = windowsService.getActiveWindowId() .then(id => (typeof this._activeWindowId === 'undefined') && this.setActiveWindow(id)); } private setActiveWindow(windowId: number | undefined) { if (this.firstActiveWindowIdPromise) { this.firstActiveWindowIdPromise = null; } this._activeWindowId = windowId; } getActiveClientId(): TPromise { if (this.firstActiveWindowIdPromise) { return this.firstActiveWindowIdPromise; } return TPromise.as(`window:${this._activeWindowId}`); } dispose() { this.disposables = dispose(this.disposables); } }