Fixes navigation commands for webviews

Fixes #100536

These commands currently do not work because:

- The use the `hasFocus` check in layout.ts
- This looks at the active element and checks if the active element has a parent in the editor dom
- However webviews are outside of the normal dom flow (since they cannot be reparented without being destroyred)

To fix this, this PR adds allows dom node to point to their explicit parent using `setParentFlowTo`. Instead of a normal ancestor check, we then check ancestors while observing the flow to parents of node

The webview element is then update to have a parent flow to that points at its editor node
This commit is contained in:
Matt Bierner 2020-11-06 13:35:42 -08:00
parent 6e2aa0bfb7
commit 3338ff4e18
3 changed files with 44 additions and 4 deletions

View file

@ -661,6 +661,43 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b
return false;
}
const parentFlowToDataKey = 'parentFlowToElementId';
/**
* Set an explicit parent to use for nodes that are not part of the
* regular dom structure.
*/
export function setParentFlowTo(fromChildElement: HTMLElement, toParentElement: Element): void {
fromChildElement.dataset[parentFlowToDataKey] = toParentElement.id;
}
/**
* Check if `testAncestor` is an ancessor of `testChild`, observing the explicit
* parents set by `setParentFlowTo`.
*/
export function isAncestorUsingFlowTo(testChild: Node, testAncestor: Node): boolean {
let node: Node | null = testChild;
while (node) {
if (node === testAncestor) {
return true;
}
if (node instanceof HTMLElement) {
const flowToParentId = node.dataset[parentFlowToDataKey];
if (typeof flowToParentId === 'string') {
const flowToParentElement = document.getElementById(flowToParentId);
if (flowToParentElement) {
node = flowToParentElement;
continue;
}
}
}
node = node.parentNode;
}
return false;
}
export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null {
while (node && node.nodeType === node.ELEMENT_NODE) {
if (node.classList.contains(clazz)) {

View file

@ -5,7 +5,7 @@
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { EventType, addDisposableListener, isAncestor, getClientArea, Dimension, position, size, IDimension } from 'vs/base/browser/dom';
import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo } from 'vs/base/browser/dom';
import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { Registry } from 'vs/platform/registry/common/platform';
@ -999,7 +999,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
const container = this.getContainer(part);
return !!container && isAncestor(activeElement, container);
return !!container && isAncestorUsingFlowTo(activeElement, container);
}
focusPart(part: Parts): void {

View file

@ -8,18 +8,19 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { generateUuid } from 'vs/base/common/uuid';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
import { EditorInput, EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
export class WebviewEditor extends EditorPane {
@ -54,6 +55,7 @@ export class WebviewEditor extends EditorPane {
protected createEditor(parent: HTMLElement): void {
const element = document.createElement('div');
this._element = element;
this._element.id = `webview-editor-element-${generateUuid()}`;
parent.appendChild(element);
}
@ -143,6 +145,7 @@ export class WebviewEditor extends EditorPane {
if (this._element) {
this._element.setAttribute('aria-flowto', input.webview.container.id);
DOM.setParentFlowTo(input.webview.container, this._element);
}
this._webviewVisibleDisposables.clear();