diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 9a1b9ab092fd1..28d0480ab86a0 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -55,6 +55,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { labelFormat: 'default', splitSizing: 'auto', splitOnDragAndDrop: true, + allowDropIntoGroup: true, dragToOpenWindow: true, centeredLayoutFixedWidth: false, doubleClickTabToToggleEditorGroupSizes: 'expand', @@ -142,6 +143,7 @@ function validateEditorPartOptions(options: IEditorPartOptions): IEditorPartOpti 'mouseBackForwardToNavigate': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['mouseBackForwardToNavigate']), 'restoreViewState': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['restoreViewState']), 'splitOnDragAndDrop': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['splitOnDragAndDrop']), + 'allowDropIntoGroup': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['allowDropIntoGroup']), 'dragToOpenWindow': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['dragToOpenWindow']), 'centeredLayoutFixedWidth': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['centeredLayoutFixedWidth']), 'hasIcons': new BooleanVerifier(DEFAULT_EDITOR_PART_OPTIONS['hasIcons']), diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index f604df1d0fe58..f9c80ed1dd266 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -20,7 +20,7 @@ import { IThemeService, Themable } from '../../../../platform/theme/common/theme import { isTemporaryWorkspace, IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { CodeDataTransfers, containsDragType, Extensions as DragAndDropExtensions, IDragAndDropContributionRegistry, LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, extractTreeDropData, ResourcesDropHandler } from '../../dnd.js'; -import { IEditorGroupView, prepareMoveCopyEditors } from './editor.js'; +import { IEditorGroupsView, IEditorGroupView, prepareMoveCopyEditors } from './editor.js'; import { EditorInputCapabilities, IEditorIdentifier, IUntypedEditorInput } from '../../../common/editor.js'; import { EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BORDER, EDITOR_DROP_INTO_PROMPT_FOREGROUND } from '../../../common/theme.js'; import { GroupDirection, IEditorDropTargetDelegate, IEditorGroup, IEditorGroupsService, IMergeGroupOptions, MergeGroupMode } from '../../../services/editor/common/editorGroupsService.js'; @@ -181,7 +181,7 @@ class DropOverlay extends Themable { // Position overlay and conditionally enable or disable // editor group splitting support based on setting and // keymodifiers used. - let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop; + let splitOnDragAndDrop = !!this.groupView.groupsView.partOptions.splitOnDragAndDrop; if (this.isToggleSplitOperation(e)) { splitOnDragAndDrop = !splitOnDragAndDrop; } @@ -316,7 +316,7 @@ class DropOverlay extends Themable { // Optimization: if we move the last editor of an editor group // and we are configured to close empty editor groups, we can // rather move the entire editor group according to the direction - if (this.editorGroupService.partOptions.closeEmptyGroups && sourceGroup.count === 1 && typeof splitDirection === 'number' && !copyEditor) { + if (this.groupView.groupsView.partOptions.closeEmptyGroups && sourceGroup.count === 1 && typeof splitDirection === 'number' && !copyEditor) { targetGroup = this.editorGroupService.moveGroup(sourceGroup, this.groupView, splitDirection); } @@ -383,7 +383,7 @@ class DropOverlay extends Themable { } private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void { - const preferSplitVertically = this.editorGroupService.partOptions.openSideBySideDirection === 'right'; + const preferSplitVertically = this.groupView.groupsView.partOptions.openSideBySideDirection === 'right'; const editorControlWidth = this.groupView.element.clientWidth; const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight(); @@ -523,7 +523,7 @@ class DropOverlay extends Themable { private getOverlayOffsetHeight(): number { // With tabs and opened editors: use the area below tabs as drop target - if (!this.groupView.isEmpty && this.editorGroupService.partOptions.showTabs === 'multiple') { + if (!this.groupView.isEmpty && this.groupView.groupsView.partOptions.showTabs === 'multiple') { return this.groupView.titleHeight.offset; } @@ -571,6 +571,7 @@ export class EditorDropTarget extends Themable { private readonly groupTransfer = LocalSelectionTransfer.getInstance(); constructor( + private readonly groupsView: IEditorGroupsView, private readonly container: HTMLElement, private readonly delegate: IEditorDropTargetDelegate, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -620,6 +621,14 @@ export class EditorDropTarget extends Themable { } } + // Check if dropping into group is allowed + if (!this.groupsView.partOptions.allowDropIntoGroup) { + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'none'; + } + return; + } + // Signal DND start this.updateContainer(true); diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 4326662a9abaa..ca2d962ea0dd6 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -973,7 +973,7 @@ export class EditorPart extends Part implements IEditorPart, createEditorDropTarget(container: unknown, delegate: IEditorDropTargetDelegate): IDisposable { assertType(isHTMLElement(container)); - return this.scopedInstantiationService.createInstance(EditorDropTarget, container, delegate); + return this.scopedInstantiationService.createInstance(EditorDropTarget, this, container, delegate); } //#region Part diff --git a/src/vs/workbench/browser/parts/editor/modalEditorPart.ts b/src/vs/workbench/browser/parts/editor/modalEditorPart.ts index 643bbb4a9794d..d42669e46088d 100644 --- a/src/vs/workbench/browser/parts/editor/modalEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/modalEditorPart.ts @@ -181,9 +181,10 @@ export class ModalEditorPart { let height: number; if (editorPart.maximized) { - const padding = 16; // Keep a small margin around all edges - width = Math.max(containerDimension.width - padding, 0); - height = Math.max(availableHeight - padding, 0); + const horizontalPadding = 16; + const verticalPadding = Math.max(titleBarOffset /* keep away from title bar to prevent clipping issues with WCO */, 16); + width = Math.max(containerDimension.width - horizontalPadding, 0); + height = Math.max(availableHeight - verticalPadding, 0); } else { const maxWidth = 1200; const maxHeight = 800; @@ -273,7 +274,8 @@ class ModalEditorPartImpl extends EditorPart implements IModalEditorPart { tabActionCloseVisibility: editorCount > 1, editorActionsLocation: 'default', tabHeight: 'default', - wrapTabs: false + wrapTabs: false, + allowDropIntoGroup: false }); } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index eded82c117156..2e535074002fa 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1261,6 +1261,7 @@ interface IEditorPartConfiguration { splitInGroupLayout?: 'vertical' | 'horizontal'; splitSizing?: 'auto' | 'split' | 'distribute'; splitOnDragAndDrop?: boolean; + allowDropIntoGroup?: boolean; dragToOpenWindow?: boolean; centeredLayoutFixedWidth?: boolean; doubleClickTabToToggleEditorGroupSizes?: 'maximize' | 'expand' | 'off'; diff --git a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts index e47649ef3b4e6..b6e0bc187fc52 100644 --- a/src/vs/workbench/contrib/webview/browser/overlayWebview.ts +++ b/src/vs/workbench/contrib/webview/browser/overlayWebview.ts @@ -24,7 +24,7 @@ import { IOverlayWebview, IWebview, IWebviewElement, IWebviewService, KEYBINDING export class OverlayWebview extends Disposable implements IOverlayWebview { private _isFirstLoad = true; - private readonly _firstLoadPendingMessages = new Set<{ readonly message: any; readonly transfer?: readonly ArrayBuffer[]; readonly resolve: (value: boolean) => void }>(); + private readonly _firstLoadPendingMessages = new Set<{ readonly message: unknown; readonly transfer?: readonly ArrayBuffer[]; readonly resolve: (value: boolean) => void }>(); private readonly _webview = this._register(new MutableDisposable()); private readonly _webviewEvents = this._register(new DisposableStore()); @@ -37,7 +37,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { private _contentOptions: WebviewContentOptions; private _options: WebviewOptions; - private _owner: any = undefined; + private _owner: unknown = undefined; private _windowId: number | undefined = undefined; private get window() { return getWindowById(this._windowId, true).window; } @@ -123,7 +123,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { return this._container.domNode; } - public claim(owner: any, targetWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined) { + public claim(owner: unknown, targetWindow: CodeWindow, scopedContextKeyService: IContextKeyService | undefined) { if (this._isDisposed) { return; } @@ -165,7 +165,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { } } - public release(owner: any) { + public release(owner: unknown) { if (this._owner !== owner) { return; } @@ -370,7 +370,7 @@ export class OverlayWebview extends Disposable implements IOverlayWebview { public readonly intrinsicContentSize = observableValue<{ readonly width: number; readonly height: number } | undefined>('WebviewIntrinsicContentSize', undefined); - public async postMessage(message: any, transfer?: readonly ArrayBuffer[]): Promise { + public async postMessage(message: unknown, transfer?: readonly ArrayBuffer[]): Promise { if (this._webview.value) { return this._webview.value.postMessage(message, transfer); } diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 3a42f865c9630..22d994be10d0b 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -476,6 +476,11 @@ suite('EditorGroupsService', () => { assert.strictEqual(part.partOptions.showTabs, 'single'); assert.strictEqual(newOptions.showTabs, 'single'); assert.strictEqual(oldOptions, currentOptions); + + const enforced = part.enforcePartOptions({ allowDropIntoGroup: false }); + assert.strictEqual(part.partOptions.allowDropIntoGroup, false); + enforced.dispose(); + assert.strictEqual(part.partOptions.allowDropIntoGroup, true); }); test('editor basics', async function () {