diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index ed0c07bc840868bdec3af2d19882e4c2373f2645..23ddd6cbea082699d7409977a3ec19e6aab9a799 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1437,18 +1437,18 @@ export namespace WebFileSystemAccess { } } -type ModifierKey = 'alt' | 'ctrl' | 'shift'; +type ModifierKey = 'alt' | 'ctrl' | 'shift' | 'meta'; export interface IModifierKeyStatus { altKey: boolean; shiftKey: boolean; ctrlKey: boolean; + metaKey: boolean; lastKeyPressed?: ModifierKey; lastKeyReleased?: ModifierKey; event?: KeyboardEvent; } - export class ModifierKeyEmitter extends Emitter { private readonly _subscriptions = new DisposableStore(); @@ -1461,7 +1461,8 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus = { altKey: false, shiftKey: false, - ctrlKey: false + ctrlKey: false, + metaKey: false }; this._subscriptions.add(domEvent(document.body, 'keydown', true)(e => { @@ -1471,6 +1472,8 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.lastKeyPressed = 'alt'; } else if (e.ctrlKey && !this._keyStatus.ctrlKey) { this._keyStatus.lastKeyPressed = 'ctrl'; + } else if (e.metaKey && !this._keyStatus.metaKey) { + this._keyStatus.lastKeyPressed = 'meta'; } else if (e.shiftKey && !this._keyStatus.shiftKey) { this._keyStatus.lastKeyPressed = 'shift'; } else if (event.keyCode !== KeyCode.Alt) { @@ -1481,6 +1484,7 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.altKey = e.altKey; this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.metaKey = e.metaKey; this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyPressed) { @@ -1494,6 +1498,8 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.lastKeyReleased = 'alt'; } else if (!e.ctrlKey && this._keyStatus.ctrlKey) { this._keyStatus.lastKeyReleased = 'ctrl'; + } else if (!e.metaKey && this._keyStatus.metaKey) { + this._keyStatus.lastKeyReleased = 'meta'; } else if (!e.shiftKey && this._keyStatus.shiftKey) { this._keyStatus.lastKeyReleased = 'shift'; } else { @@ -1506,6 +1512,7 @@ export class ModifierKeyEmitter extends Emitter { this._keyStatus.altKey = e.altKey; this._keyStatus.ctrlKey = e.ctrlKey; + this._keyStatus.metaKey = e.metaKey; this._keyStatus.shiftKey = e.shiftKey; if (this._keyStatus.lastKeyReleased) { @@ -1529,13 +1536,7 @@ export class ModifierKeyEmitter extends Emitter { })); this._subscriptions.add(domEvent(window, 'blur')(e => { - this._keyStatus.lastKeyPressed = undefined; - this._keyStatus.lastKeyReleased = undefined; - this._keyStatus.altKey = false; - this._keyStatus.shiftKey = false; - this._keyStatus.shiftKey = false; - - this.fire(this._keyStatus); + this.resetKeyStatus(); })); } @@ -1543,14 +1544,25 @@ export class ModifierKeyEmitter extends Emitter { return this._keyStatus; } - // This method is a workaround because we do not get keyboard events while a context menu is shown #109062 + get isModifierPressed(): boolean { + return this._keyStatus.altKey || this._keyStatus.ctrlKey || this._keyStatus.metaKey || this._keyStatus.shiftKey; + } + + /** + * Allows to explicitly reset the key status based on more knowledge (#109062) + */ resetKeyStatus(): void { + this.doResetKeyStatus(); + this.fire(this._keyStatus); + } + + private doResetKeyStatus(): void { this._keyStatus = { altKey: false, shiftKey: false, - ctrlKey: false + ctrlKey: false, + metaKey: false }; - this.fire(this._keyStatus); } static getInstance() { diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index f8226d5dccd1155ffb494d2c56cf7c6d52b055ec..cb2b3fc0cac03c749252cfc8999d00b7aeda224c 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -860,7 +860,7 @@ export class MenuBar extends Disposable { } private onModifierKeyToggled(modifierKeyStatus: DOM.IModifierKeyStatus): void { - const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; + const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey && !modifierKeyStatus.metaKey; if (this.options.visibility === 'hidden') { return; diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 530133592778dad6fdc09f99c60fe9438d381235..0f92adfe6bd7ce0a72c19aace31d9f1674df643c 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -13,7 +13,7 @@ import { IWindowSettings, IWindowOpenable, IOpenWindowOptions, isFolderToOpen, i import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; -import { addDisposableListener, EventType, trackFocus } from 'vs/base/browser/dom'; +import { IModifierKeyStatus, ModifierKeyEmitter, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -113,12 +113,8 @@ export class BrowserHostService extends Disposable implements IHostService { // Veto shutdown depending on `window.confirmBeforeClose` setting this._register(this.lifecycleService.onBeforeShutdown(e => this.onBeforeShutdown(e))); - // Track certain DOM events to detect keybinding usage - this._register(addDisposableListener(this.layoutService.container, EventType.KEY_DOWN, e => this.updateShutdownReasonFromEvent(e))); - this._register(addDisposableListener(this.layoutService.container, EventType.KEY_UP, () => this.updateShutdownReasonFromEvent(undefined))); - this._register(addDisposableListener(this.layoutService.container, EventType.MOUSE_DOWN, () => this.updateShutdownReasonFromEvent(undefined))); - this._register(addDisposableListener(this.layoutService.container, EventType.MOUSE_UP, () => this.updateShutdownReasonFromEvent(undefined))); - this._register(this.onDidChangeFocus(() => this.updateShutdownReasonFromEvent(undefined))); + // Track modifier keys to detect keybinding usage + this._register(ModifierKeyEmitter.getInstance().event(e => this.updateShutdownReasonFromEvent(e))); } private onBeforeShutdown(e: BeforeShutdownEvent): void { @@ -134,12 +130,16 @@ export class BrowserHostService extends Disposable implements IHostService { this.shutdownReason = HostShutdownReason.Unknown; } - private updateShutdownReasonFromEvent(e: KeyboardEvent | undefined): void { + private updateShutdownReasonFromEvent(e: IModifierKeyStatus): void { if (this.shutdownReason === HostShutdownReason.Api) { return; // do not overwrite any explicitly set shutdown reason } - this.shutdownReason = e ? HostShutdownReason.Keyboard : HostShutdownReason.Unknown; + if (ModifierKeyEmitter.getInstance().isModifierPressed) { + this.shutdownReason = HostShutdownReason.Keyboard; + } else { + this.shutdownReason = HostShutdownReason.Unknown; + } } //#region Focus @@ -405,21 +405,23 @@ export class BrowserHostService extends Disposable implements IHostService { } async reload(): Promise { - - // We know that `window.location.reload` will trigger a shutdown - // so we update `shutdownReason` to reflect that - this.shutdownReason = HostShutdownReason.Api; - - window.location.reload(); + this.withExpectedShutdown(() => { + window.location.reload(); + }); } async close(): Promise { + this.withExpectedShutdown(() => { + window.close(); + }); + } + + private withExpectedShutdown(callback: () => void): void { - // We know that `window.close` will trigger a shutdown - // so we update `shutdownReason` to reflect that + // Update shutdown reason in a way that we do not show a dialog this.shutdownReason = HostShutdownReason.Api; - window.close(); + callback(); } //#endregion