diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index b5089f88f4f40..2c5ca4108311f 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -460,6 +460,10 @@ class DropOverlay extends Themable { } } +export interface EditorDropTargetDelegate { + groupContainsPredicate?(groupView: IEditorGroupView): boolean; +} + export class EditorDropTarget extends Themable { private _overlay?: DropOverlay; @@ -472,6 +476,7 @@ export class EditorDropTarget extends Themable { constructor( private accessor: IEditorGroupsAccessor, private container: HTMLElement, + private readonly delegate: EditorDropTargetDelegate, @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { @@ -545,7 +550,7 @@ export class EditorDropTarget extends Themable { private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined { const groups = this.accessor.groups; - return find(groups, groupView => isAncestor(child, groupView.element)); + return find(groups, groupView => isAncestor(child, groupView.element) || this.delegate.groupContainsPredicate?.(groupView)); } private updateContainer(isDraggedOver: boolean): void { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 75c49c31c867a..81f5b00aac33d 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -23,7 +23,7 @@ import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/com import { assign } from 'vs/base/common/objects'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { EditorDropTarget } from 'vs/workbench/browser/parts/editor/editorDropTarget'; +import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { Color } from 'vs/base/common/color'; import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -780,6 +780,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro return groupView; } + createEditorDropTarget(container: HTMLElement, delegate: EditorDropTargetDelegate): IDisposable { + return this.instantiationService.createInstance(EditorDropTarget, this, container, delegate); + } + //#endregion //#region Part @@ -822,7 +826,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY])); // Drop support - this._register(this.instantiationService.createInstance(EditorDropTarget, this, this.container)); + this._register(this.createEditorDropTarget(this.container, {})); return this.container; } diff --git a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts index ed65ede628660..553732b0b437d 100644 --- a/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/baseWebviewElement.ts @@ -272,4 +272,19 @@ export abstract class BaseWebview extends Disposable { // And re-dispatch window.dispatchEvent(emulatedKeyboardEvent); } + + windowDidDragStart(): void { + // Webview break drag and droping around the main window (no events are generated when you are over them) + // Work around this by disabling pointer events during the drag. + // https://github.com/electron/electron/issues/18226 + if (this.element) { + this.element.style.pointerEvents = 'none'; + } + } + + windowDidDragEnd(): void { + if (this.element) { + this.element.style.pointerEvents = ''; + } + } } diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 299ecab6654e2..d5365f700f3b9 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -198,4 +198,12 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd f(this._webview.value); } } + + windowDidDragStart() { + this.withWebview(webview => webview.windowDidDragStart()); + } + + windowDidDragEnd() { + this.withWebview(webview => webview.windowDidDragEnd()); + } } diff --git a/src/vs/workbench/contrib/webview/browser/webview.ts b/src/vs/workbench/contrib/webview/browser/webview.ts index a26adfb756dac..cf633fbc463db 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.ts @@ -82,6 +82,9 @@ export interface Webview extends IDisposable { showFind(): void; hideFind(): void; runFindAction(previous: boolean): void; + + windowDidDragStart(): void; + windowDidDragEnd(): void; } export interface WebviewElement extends Webview { diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 38c4d822c2d46..a0e741c29170b 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -6,19 +6,20 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { isWeb } from 'vs/base/common/platform'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; 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 { IHostService } from 'vs/workbench/services/host/browser/host'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; -import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; +import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; -import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { isWeb } from 'vs/base/common/platform'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; export class WebviewEditor extends BaseEditor { @@ -30,7 +31,7 @@ export class WebviewEditor extends BaseEditor { private _content?: HTMLElement; private _dimension?: DOM.Dimension; - private readonly _webviewFocusTrackerDisposables = this._register(new DisposableStore()); + private readonly _webviewVisibleDisposables = this._register(new DisposableStore()); private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); private readonly _onDidFocusWebview = this._register(new Emitter()); @@ -39,10 +40,11 @@ export class WebviewEditor extends BaseEditor { constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IEditorService private readonly _editorService: IEditorService, + @IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService, @IHostService private readonly _hostService: IHostService, - @IStorageService storageService: IStorageService ) { super(WebviewEditor.ID, telemetryService, themeService, storageService); @@ -117,23 +119,22 @@ export class WebviewEditor extends BaseEditor { } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - const webview = this.input && (this.input as WebviewInput).webview; - if (webview) { + if (this.input instanceof WebviewInput) { + const webview = this.input.webview; if (visible) { webview.claim(this); } else { webview.release(this); } - this.claimWebview(this.input as WebviewInput); + this.claimWebview(this.input); } - super.setEditorVisible(visible, group); } public clearInput() { if (this.input && this.input instanceof WebviewInput) { this.input.webview.release(this); - this._webviewFocusTrackerDisposables.clear(); + this._webviewVisibleDisposables.clear(); } super.clearInput(); @@ -178,8 +179,35 @@ export class WebviewEditor extends BaseEditor { this._content.setAttribute('aria-flowto', input.webview.container.id); } + this._webviewVisibleDisposables.clear(); + + // Webviews are not part of the normal editor dom, so we have to register our own drag and drop handler on them. + if (this._editorGroupsService instanceof EditorPart) { + this._webviewVisibleDisposables.add(this._editorGroupsService.createEditorDropTarget(input.webview.container, { + groupContainsPredicate: (group) => this.group?.id === group.group.id + })); + } + + this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_START, () => { + if (this.input instanceof WebviewInput) { + this.input.webview.windowDidDragStart(); + } + })); + + const onDragEnd = () => { + if (this.input instanceof WebviewInput) { + this.input.webview.windowDidDragEnd(); + } + }; + this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.DRAG_END, onDragEnd)); + this._webviewVisibleDisposables.add(DOM.addDisposableListener(window, DOM.EventType.MOUSE_MOVE, currentEvent => { + if (currentEvent.buttons === 0) { + onDragEnd(); + } + })); + this.synchronizeWebviewContainerDimensions(input.webview); - this.trackFocus(input.webview); + this._webviewVisibleDisposables.add(this.trackFocus(input.webview)); } private synchronizeWebviewContainerDimensions(webview: WebviewEditorOverlay, dimension?: DOM.Dimension) { @@ -188,15 +216,17 @@ export class WebviewEditor extends BaseEditor { } } - private trackFocus(webview: WebviewEditorOverlay): void { - this._webviewFocusTrackerDisposables.clear(); + private trackFocus(webview: WebviewEditorOverlay): IDisposable { + const store = new DisposableStore(); // Track focus in webview content const webviewContentFocusTracker = DOM.trackFocus(webview.container); - this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker); - this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); + store.add(webviewContentFocusTracker); + store.add(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); // Track focus in webview element - this._webviewFocusTrackerDisposables.add(webview.onDidFocus(() => this._onDidFocusWebview.fire())); + store.add(webview.onDidFocus(() => this._onDidFocusWebview.fire())); + + return store; } }