mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-17 13:50:46 +01:00
Move shared webview focus implementation into base class
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { streamToBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
@@ -98,6 +99,8 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
||||
private readonly _tunnelService: ITunnelService;
|
||||
protected readonly _environmentService: IWorkbenchEnvironmentService;
|
||||
|
||||
private readonly _focusDelayer = this._register(new ThrottledDelayer(10));
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
private readonly options: WebviewOptions,
|
||||
@@ -528,4 +531,53 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
|
||||
location: redirect
|
||||
});
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.doFocus();
|
||||
|
||||
// Handle focus change programmatically (do not rely on event from <webview>)
|
||||
this.handleFocusChange(true);
|
||||
}
|
||||
|
||||
protected doFocus() {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the existing focus first if not already on the webview.
|
||||
// This is required because the next part where we set the focus is async.
|
||||
if (document.activeElement && document.activeElement instanceof HTMLElement && document.activeElement !== this.element) {
|
||||
// Don't blur if on the webview because this will also happen async and may unset the focus
|
||||
// after the focus trigger fires below.
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/microsoft/vscode/issues/75209
|
||||
// Electron's webview.focus is async so for a sequence of actions such as:
|
||||
//
|
||||
// 1. Open webview
|
||||
// 1. Show quick pick from command palette
|
||||
//
|
||||
// We end up focusing the webview after showing the quick pick, which causes
|
||||
// the quick pick to instantly dismiss.
|
||||
//
|
||||
// Workaround this by debouncing the focus and making sure we are not focused on an input
|
||||
// when we try to re-focus.
|
||||
this._focusDelayer.trigger(async () => {
|
||||
if (!this.isFocused || !this.element) {
|
||||
return;
|
||||
}
|
||||
if (document.activeElement && document.activeElement?.tagName !== 'BODY') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.elementFocusImpl();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
this._send('focus');
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract elementFocusImpl(): void;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
||||
|
||||
private _confirmBeforeClose: string;
|
||||
|
||||
private readonly _focusDelayer = this._register(new ThrottledDelayer(10));
|
||||
private _elementFocusImpl!: (options?: FocusOptions | undefined) => void;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
options: WebviewOptions,
|
||||
@@ -87,7 +84,6 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
||||
element.style.width = '100%';
|
||||
element.style.height = '100%';
|
||||
|
||||
this._elementFocusImpl = () => element.contentWindow?.focus();
|
||||
element.focus = () => {
|
||||
this.doFocus();
|
||||
};
|
||||
@@ -95,6 +91,10 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
||||
return element;
|
||||
}
|
||||
|
||||
protected elementFocusImpl() {
|
||||
this.element?.contentWindow?.focus();
|
||||
}
|
||||
|
||||
protected initElement(extension: WebviewExtensionDescription | undefined, options: WebviewOptions, extraParams?: object) {
|
||||
const params = {
|
||||
id: this.id,
|
||||
@@ -162,51 +162,4 @@ export class IFrameWebview extends BaseWebview<HTMLIFrameElement> implements Web
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.doFocus();
|
||||
|
||||
// Handle focus change programmatically (do not rely on event from <webview>)
|
||||
this.handleFocusChange(true);
|
||||
}
|
||||
|
||||
private doFocus() {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the existing focus first if not already on the webview.
|
||||
// This is required because the next part where we set the focus is async.
|
||||
if (document.activeElement && document.activeElement instanceof HTMLElement && document.activeElement !== this.element) {
|
||||
// Don't blur if on the webview because this will also happen async and may unset the focus
|
||||
// after the focus trigger fires below.
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/microsoft/vscode/issues/75209
|
||||
// Electron's webview.focus is async so for a sequence of actions such as:
|
||||
//
|
||||
// 1. Open webview
|
||||
// 1. Show quick pick from command palette
|
||||
//
|
||||
// We end up focusing the webview after showing the quick pick, which causes
|
||||
// the quick pick to instantly dismiss.
|
||||
//
|
||||
// Workaround this by debouncing the focus and making sure we are not focused on an input
|
||||
// when we try to re-focus.
|
||||
this._focusDelayer.trigger(async () => {
|
||||
if (!this.isFocused || !this.element) {
|
||||
return;
|
||||
}
|
||||
if (document.activeElement && document.activeElement?.tagName !== 'BODY') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._elementFocusImpl();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
this._send('focus');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { FindInPageOptions, WebviewTag } from 'electron';
|
||||
import { addDisposableListener } from 'vs/base/browser/dom';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -45,9 +44,6 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
||||
private _webviewFindWidget: WebviewFindWidget | undefined;
|
||||
private _findStarted: boolean = false;
|
||||
|
||||
private readonly _focusDelayer = this._register(new ThrottledDelayer(10));
|
||||
private _elementFocusImpl!: (options?: FocusOptions | undefined) => void;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
options: WebviewOptions,
|
||||
@@ -168,7 +164,6 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
||||
// Wait the end of the ctor when all listeners have been hooked up.
|
||||
const element = document.createElement('webview');
|
||||
|
||||
this._elementFocusImpl = element.focus.bind(element);
|
||||
element.focus = () => {
|
||||
this.doFocus();
|
||||
};
|
||||
@@ -185,6 +180,10 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
||||
return element;
|
||||
}
|
||||
|
||||
protected elementFocusImpl() {
|
||||
this.element?.focus();
|
||||
}
|
||||
|
||||
public override set contentOptions(options: WebviewContentOptions) {
|
||||
this._myLogService.debug(`Webview(${this.id}): will set content options`);
|
||||
super.contentOptions = options;
|
||||
@@ -212,53 +211,6 @@ export class ElectronWebviewBasedWebview extends BaseWebview<WebviewTag> impleme
|
||||
this.element?.send(channel, data);
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.doFocus();
|
||||
|
||||
// Handle focus change programmatically (do not rely on event from <webview>)
|
||||
this.handleFocusChange(true);
|
||||
}
|
||||
|
||||
private doFocus() {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the existing focus first if not already on the webview.
|
||||
// This is required because the next part where we set the focus is async.
|
||||
if (document.activeElement && document.activeElement instanceof HTMLElement && document.activeElement !== this.element) {
|
||||
// Don't blur if on the webview because this will also happen async and may unset the focus
|
||||
// after the focus trigger fires below.
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
// Workaround for https://github.com/microsoft/vscode/issues/75209
|
||||
// Electron's webview.focus is async so for a sequence of actions such as:
|
||||
//
|
||||
// 1. Open webview
|
||||
// 1. Show quick pick from command palette
|
||||
//
|
||||
// We end up focusing the webview after showing the quick pick, which causes
|
||||
// the quick pick to instantly dismiss.
|
||||
//
|
||||
// Workaround this by debouncing the focus and making sure we are not focused on an input
|
||||
// when we try to re-focus.
|
||||
this._focusDelayer.trigger(async () => {
|
||||
if (!this.isFocused || !this.element) {
|
||||
return;
|
||||
}
|
||||
if (document.activeElement && document.activeElement?.tagName !== 'BODY') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this._elementFocusImpl();
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
this._send('focus');
|
||||
});
|
||||
}
|
||||
|
||||
protected override style(): void {
|
||||
super.style();
|
||||
this.styledFindWidget();
|
||||
|
||||
Reference in New Issue
Block a user