mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-26 02:06:38 +01:00
+34
-27
@@ -18,23 +18,38 @@ import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
|
||||
type WindowGlobal = Window & typeof globalThis;
|
||||
|
||||
interface IWindow {
|
||||
readonly window: WindowGlobal;
|
||||
readonly disposables: DisposableStore;
|
||||
}
|
||||
|
||||
export const { registerWindow, getWindows, getWindowsCount, onDidRegisterWindow, onWillUnregisterWindow, onDidUnregisterWindow } = (function () {
|
||||
const windows = new Set([window]);
|
||||
const onDidRegisterWindow = new event.Emitter<{ window: Window & typeof globalThis; disposables: DisposableStore }>();
|
||||
const onDidUnregisterWindow = new event.Emitter<Window & typeof globalThis>();
|
||||
const onWillUnregisterWindow = new event.Emitter<Window & typeof globalThis>();
|
||||
const windows = new Map<WindowGlobal, IWindow>();
|
||||
windows.set(window, { window, disposables: new DisposableStore() });
|
||||
|
||||
const onDidRegisterWindow = new event.Emitter<IWindow>();
|
||||
const onDidUnregisterWindow = new event.Emitter<WindowGlobal>();
|
||||
const onWillUnregisterWindow = new event.Emitter<WindowGlobal>();
|
||||
|
||||
return {
|
||||
onDidRegisterWindow: onDidRegisterWindow.event,
|
||||
onWillUnregisterWindow: onWillUnregisterWindow.event,
|
||||
onDidUnregisterWindow: onDidUnregisterWindow.event,
|
||||
registerWindow(window: Window & typeof globalThis): IDisposable {
|
||||
registerWindow(window: WindowGlobal): IDisposable {
|
||||
if (windows.has(window)) {
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
windows.add(window);
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const registeredWindow = {
|
||||
window,
|
||||
disposables: disposables.add(new DisposableStore())
|
||||
};
|
||||
windows.set(window, registeredWindow);
|
||||
|
||||
disposables.add(toDisposable(() => {
|
||||
windows.delete(window);
|
||||
onDidUnregisterWindow.fire(window);
|
||||
@@ -44,14 +59,12 @@ export const { registerWindow, getWindows, getWindowsCount, onDidRegisterWindow,
|
||||
onWillUnregisterWindow.fire(window);
|
||||
}));
|
||||
|
||||
const eventDisposables = new DisposableStore();
|
||||
disposables.add(eventDisposables);
|
||||
onDidRegisterWindow.fire({ window, disposables: eventDisposables });
|
||||
onDidRegisterWindow.fire(registeredWindow);
|
||||
|
||||
return disposables;
|
||||
},
|
||||
getWindows(): Iterable<Window & typeof globalThis> {
|
||||
return windows;
|
||||
getWindows(): Iterable<IWindow> {
|
||||
return windows.values();
|
||||
},
|
||||
getWindowsCount(): number {
|
||||
return windows.size;
|
||||
@@ -758,19 +771,19 @@ export function getActiveDocument(): Document {
|
||||
return document;
|
||||
}
|
||||
|
||||
const documents = Array.from(getWindows()).map(window => window.document);
|
||||
return documents.find(doc => doc.hasFocus()) ?? document;
|
||||
const documents = Array.from(getWindows()).map(({ window }) => window.document);
|
||||
return documents.find(document => document.hasFocus()) ?? document;
|
||||
}
|
||||
|
||||
export function getActiveWindow(): Window & typeof globalThis {
|
||||
export function getActiveWindow(): WindowGlobal {
|
||||
const document = getActiveDocument();
|
||||
return document.defaultView?.window ?? window;
|
||||
}
|
||||
|
||||
export function getWindow(element: Node): Window & typeof globalThis;
|
||||
export function getWindow(event: UIEvent): Window & typeof globalThis;
|
||||
export function getWindow(obj: unknown): Window & typeof globalThis;
|
||||
export function getWindow(e: unknown): Window & typeof globalThis {
|
||||
export function getWindow(element: Node): WindowGlobal;
|
||||
export function getWindow(event: UIEvent): WindowGlobal;
|
||||
export function getWindow(obj: unknown): WindowGlobal;
|
||||
export function getWindow(e: unknown): WindowGlobal {
|
||||
const candidateNode = e as Node | undefined;
|
||||
if (candidateNode?.ownerDocument?.defaultView) {
|
||||
return candidateNode.ownerDocument.defaultView.window;
|
||||
@@ -805,19 +818,13 @@ export function createStyleSheet(container: HTMLElement = document.head, beforeA
|
||||
// With <head> as container, the stylesheet becomes global and is tracked
|
||||
// to support auxiliary windows to clone the stylesheet.
|
||||
if (container === document.head) {
|
||||
for (const targetWindow of getWindows()) {
|
||||
for (const { window: targetWindow, disposables } of getWindows()) {
|
||||
if (targetWindow === window) {
|
||||
continue; // main window is already tracked
|
||||
}
|
||||
|
||||
const cloneDisposable = cloneGlobalStyleSheet(style, targetWindow);
|
||||
const cloneDisposable = disposables.add(cloneGlobalStyleSheet(style, targetWindow));
|
||||
disposableStore?.add(cloneDisposable);
|
||||
|
||||
disposableStore?.add(event.Event.once(onDidUnregisterWindow)(unregisteredWindow => {
|
||||
if (unregisteredWindow === targetWindow) {
|
||||
cloneDisposable.dispose();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ class EditorScopedQuickInputService extends QuickInputService {
|
||||
_serviceBrand: undefined,
|
||||
get hasContainer() { return true; },
|
||||
get container() { return widget.getDomNode(); },
|
||||
getContainer() { return widget.getDomNode(); },
|
||||
get containers() { return [widget.getDomNode()]; },
|
||||
get activeContainer() { return widget.getDomNode(); },
|
||||
get mainContainerDimension() { return editor.getLayoutInfo(); },
|
||||
|
||||
@@ -54,6 +54,10 @@ class StandaloneLayoutService implements ILayoutService {
|
||||
return activeCodeEditor?.getContainerDomNode() ?? this.container;
|
||||
}
|
||||
|
||||
getContainer() {
|
||||
return this.activeContainer;
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this._codeEditorService.getFocusedCodeEditor()?.focus();
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ export interface ILayoutService {
|
||||
*/
|
||||
readonly containers: Iterable<HTMLElement>;
|
||||
|
||||
/**
|
||||
* Get the container for the given window.
|
||||
*/
|
||||
getContainer(window: Window): HTMLElement;
|
||||
|
||||
/**
|
||||
* An offset to use for positioning elements inside the main container.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
|
||||
* browser helper so that it can be accessed in non-electron layers.
|
||||
*/
|
||||
export function applyZoom(zoomLevel: number): void {
|
||||
for (const window of getWindows()) {
|
||||
for (const { window } of getWindows()) {
|
||||
getGlobals(window)?.webFrame?.setZoomLevel(zoomLevel);
|
||||
}
|
||||
setZoomFactor(zoomLevelToZoomFactor(zoomLevel));
|
||||
|
||||
@@ -9,9 +9,9 @@ import { localize } from 'vs/nls';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { DomEmitter } from 'vs/base/browser/event';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable, toDisposable, dispose, DisposableStore, setDisposableTracker, DisposableTracker, DisposableInfo } from 'vs/base/common/lifecycle';
|
||||
import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $, getActiveDocument } from 'vs/base/browser/dom';
|
||||
import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $, getActiveDocument, onDidRegisterWindow, getWindows } from 'vs/base/browser/dom';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Context } from 'vs/platform/contextkey/browser/contextKeyService';
|
||||
@@ -130,13 +130,34 @@ class ToggleScreencastModeAction extends Action2 {
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const container = layoutService.container;
|
||||
const container = layoutService.activeContainer;
|
||||
|
||||
const mouseMarker = append(container, $('.screencast-mouse'));
|
||||
disposables.add(toDisposable(() => mouseMarker.remove()));
|
||||
|
||||
const onMouseDown = disposables.add(new DomEmitter(container, 'mousedown', true));
|
||||
const onMouseUp = disposables.add(new DomEmitter(container, 'mouseup', true));
|
||||
const onMouseMove = disposables.add(new DomEmitter(container, 'mousemove', true));
|
||||
const keyboardMarker = append(container, $('.screencast-keyboard'));
|
||||
disposables.add(toDisposable(() => keyboardMarker.remove()));
|
||||
|
||||
const onMouseDown = disposables.add(new Emitter<MouseEvent>());
|
||||
const onMouseUp = disposables.add(new Emitter<MouseEvent>());
|
||||
const onMouseMove = disposables.add(new Emitter<MouseEvent>());
|
||||
|
||||
function registerContainerListeners(container: HTMLElement, disposables: DisposableStore): void {
|
||||
disposables.add(disposables.add(new DomEmitter(container, 'mousedown', true)).event(e => onMouseDown.fire(e)));
|
||||
disposables.add(disposables.add(new DomEmitter(container, 'mouseup', true)).event(e => onMouseUp.fire(e)));
|
||||
disposables.add(disposables.add(new DomEmitter(container, 'mousemove', true)).event(e => onMouseMove.fire(e)));
|
||||
}
|
||||
|
||||
for (const { window, disposables } of getWindows()) {
|
||||
registerContainerListeners(layoutService.getContainer(window), disposables);
|
||||
}
|
||||
|
||||
disposables.add(onDidRegisterWindow(({ window, disposables }) => registerContainerListeners(layoutService.getContainer(window), disposables)));
|
||||
|
||||
disposables.add(layoutService.onDidChangeActiveContainer(() => {
|
||||
layoutService.activeContainer.appendChild(mouseMarker);
|
||||
layoutService.activeContainer.appendChild(keyboardMarker);
|
||||
}));
|
||||
|
||||
const updateMouseIndicatorColor = () => {
|
||||
mouseMarker.style.borderColor = Color.fromHex(configurationService.getValue<string>('screencastMode.mouseIndicatorColor')).toString();
|
||||
@@ -172,9 +193,6 @@ class ToggleScreencastModeAction extends Action2 {
|
||||
});
|
||||
}));
|
||||
|
||||
const keyboardMarker = append(container, $('.screencast-keyboard'));
|
||||
disposables.add(toDisposable(() => keyboardMarker.remove()));
|
||||
|
||||
const updateKeyboardFontSize = () => {
|
||||
keyboardMarker.style.fontSize = `${clamp(configurationService.getValue<number>('screencastMode.fontSize') || 56, 20, 100)}px`;
|
||||
};
|
||||
@@ -214,10 +232,23 @@ class ToggleScreencastModeAction extends Action2 {
|
||||
}
|
||||
}));
|
||||
|
||||
const onKeyDown = disposables.add(new DomEmitter(window, 'keydown', true));
|
||||
const onCompositionStart = disposables.add(new DomEmitter(window, 'compositionstart', true));
|
||||
const onCompositionUpdate = disposables.add(new DomEmitter(window, 'compositionupdate', true));
|
||||
const onCompositionEnd = disposables.add(new DomEmitter(window, 'compositionend', true));
|
||||
const onKeyDown = disposables.add(new Emitter<KeyboardEvent>());
|
||||
const onCompositionStart = disposables.add(new Emitter<CompositionEvent>());
|
||||
const onCompositionUpdate = disposables.add(new Emitter<CompositionEvent>());
|
||||
const onCompositionEnd = disposables.add(new Emitter<CompositionEvent>());
|
||||
|
||||
function registerWindowListeners(window: Window, disposables: DisposableStore): void {
|
||||
disposables.add(disposables.add(new DomEmitter(window, 'keydown', true)).event(e => onKeyDown.fire(e)));
|
||||
disposables.add(disposables.add(new DomEmitter(window, 'compositionstart', true)).event(e => onCompositionStart.fire(e)));
|
||||
disposables.add(disposables.add(new DomEmitter(window, 'compositionupdate', true)).event(e => onCompositionUpdate.fire(e)));
|
||||
disposables.add(disposables.add(new DomEmitter(window, 'compositionend', true)).event(e => onCompositionEnd.fire(e)));
|
||||
}
|
||||
|
||||
for (const { window, disposables } of getWindows()) {
|
||||
registerWindowListeners(window, disposables);
|
||||
}
|
||||
|
||||
disposables.add(onDidRegisterWindow(({ window, disposables }) => registerWindowListeners(window, disposables)));
|
||||
|
||||
let length = 0;
|
||||
let composing: Element | undefined = undefined;
|
||||
|
||||
@@ -52,7 +52,7 @@ import { IAuxiliaryWindowService, isAuxiliaryWindow } from 'vs/workbench/service
|
||||
//#region Layout Implementation
|
||||
|
||||
interface ILayoutRuntimeState {
|
||||
activeContainer: 'main' | number /* window ID */;
|
||||
activeContainerId: 'main' | number /* window ID */;
|
||||
fullscreen: boolean;
|
||||
maximized: boolean;
|
||||
hasFocus: boolean;
|
||||
@@ -170,7 +170,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
get activeContainer() { return this.getContainerFromDocument(getActiveDocument()); }
|
||||
get containers(): Iterable<HTMLElement> {
|
||||
const containers: HTMLElement[] = [];
|
||||
for (const window of getWindows()) {
|
||||
for (const { window } of getWindows()) {
|
||||
containers.push(this.getContainerFromDocument(window.document));
|
||||
}
|
||||
|
||||
@@ -453,8 +453,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
private onWindowFocusChanged(hasFocus: boolean): void {
|
||||
if (hasFocus) {
|
||||
const activeContainerId = this.getActiveContainerId();
|
||||
if (this.state.runtime.activeContainer !== activeContainerId) {
|
||||
this.state.runtime.activeContainer = activeContainerId;
|
||||
if (this.state.runtime.activeContainerId !== activeContainerId) {
|
||||
this.state.runtime.activeContainerId = activeContainerId;
|
||||
this._onDidChangeActiveContainer.fire();
|
||||
}
|
||||
}
|
||||
@@ -615,7 +615,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
// Layout Runtime State
|
||||
const layoutRuntimeState: ILayoutRuntimeState = {
|
||||
activeContainer: this.getActiveContainerId(),
|
||||
activeContainerId: this.getActiveContainerId(),
|
||||
fullscreen: isFullscreen(),
|
||||
hasFocus: this.hostService.hasFocus,
|
||||
maximized: false,
|
||||
@@ -1125,12 +1125,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
}
|
||||
}
|
||||
|
||||
getContainer(part: Parts): HTMLElement | undefined {
|
||||
if (!this.parts.get(part)) {
|
||||
return undefined;
|
||||
getContainer(window: Window): HTMLElement;
|
||||
getContainer(part: Parts): HTMLElement | undefined;
|
||||
getContainer(windowOrPart: Window | Parts): HTMLElement | undefined {
|
||||
if (typeof windowOrPart === 'string') {
|
||||
const part = windowOrPart;
|
||||
if (!this.parts.get(part)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.getPart(part).getContainer();
|
||||
}
|
||||
|
||||
return this.getPart(part).getContainer();
|
||||
return this.getContainerFromDocument(windowOrPart.document);
|
||||
}
|
||||
|
||||
isVisible(part: Parts): boolean {
|
||||
|
||||
@@ -250,7 +250,7 @@ abstract class BaseSwitchWindow extends Action2 {
|
||||
for (const window of mainWindows) {
|
||||
const auxiliaryWindows = mapMainWindowToAuxiliaryWindows.get(window.id);
|
||||
if (mapMainWindowToAuxiliaryWindows.size > 0) {
|
||||
picks.push({ type: 'separator', payload: -1, label: auxiliaryWindows ? localize('windowGroup', "Window Group") : undefined } as unknown as IWindowPickItem);
|
||||
picks.push({ type: 'separator', payload: -1, label: auxiliaryWindows ? localize('windowGroup', "window group") : undefined } as unknown as IWindowPickItem);
|
||||
}
|
||||
|
||||
const resource = window.filename ? URI.file(window.filename) : isSingleFolderWorkspaceIdentifier(window.workspace) ? window.workspace.uri : isWorkspaceIdentifier(window.workspace) ? window.workspace.configPath : undefined;
|
||||
|
||||
@@ -59,6 +59,14 @@ class WorkbenchHostService extends Disposable implements IHostService {
|
||||
disposables.add(focusTracker.onDidFocus(() => emitter.fire(this.hasFocus)));
|
||||
disposables.add(focusTracker.onDidBlur(() => emitter.fire(this.hasFocus)));
|
||||
disposables.add(onVisibilityChange.event(() => emitter.fire(this.hasFocus)));
|
||||
|
||||
// Workaround: the window does not immediately seem to have focus when
|
||||
// opening, so we schedule a check for focus on the next animation frame
|
||||
window.requestAnimationFrame(() => {
|
||||
if (window.document.hasFocus()) {
|
||||
emitter.fire(true);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
return emitter.event;
|
||||
|
||||
@@ -175,6 +175,7 @@ export interface IWorkbenchLayoutService extends ILayoutService {
|
||||
/**
|
||||
* Returns the parts HTML element, if there is one.
|
||||
*/
|
||||
getContainer(window: Window): HTMLElement;
|
||||
getContainer(part: Parts): HTMLElement | undefined;
|
||||
|
||||
/**
|
||||
|
||||
@@ -623,7 +623,7 @@ export class TestLayoutService implements IWorkbenchLayoutService {
|
||||
getWindowBorderRadius(): string | undefined { return undefined; }
|
||||
isVisible(_part: Parts): boolean { return true; }
|
||||
getDimension(_part: Parts): Dimension { return new Dimension(0, 0); }
|
||||
getContainer(_part: Parts): HTMLElement { return null!; }
|
||||
getContainer(): HTMLElement { return null!; }
|
||||
isTitleBarHidden(): boolean { return false; }
|
||||
isStatusBarHidden(): boolean { return false; }
|
||||
isActivityBarHidden(): boolean { return false; }
|
||||
|
||||
Reference in New Issue
Block a user