add drag intercept events

This commit is contained in:
mathuo 2023-08-19 15:43:46 +01:00
parent 2aa700bca0
commit 821c2f5c44
No known key found for this signature in database
GPG Key ID: C6EEDEFD6CA07281
9 changed files with 135 additions and 38 deletions

View File

@ -2,6 +2,7 @@ import { fireEvent } from '@testing-library/dom';
import { GroupDragHandler } from '../../dnd/groupDragHandler'; import { GroupDragHandler } from '../../dnd/groupDragHandler';
import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel';
import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer';
import { DockviewComponent } from '../../dockview/dockviewComponent';
describe('groupDragHandler', () => { describe('groupDragHandler', () => {
test('that the dnd transfer object is setup and torndown', () => { test('that the dnd transfer object is setup and torndown', () => {
@ -16,7 +17,11 @@ describe('groupDragHandler', () => {
}); });
const group = new groupMock(); const group = new groupMock();
const cut = new GroupDragHandler(element, 'test_accessor_id', group); const cut = new GroupDragHandler(
element,
{ id: 'test_accessor_id' } as DockviewComponent,
group
);
fireEvent.dragStart(element, new Event('dragstart')); fireEvent.dragStart(element, new Event('dragstart'));
@ -54,7 +59,11 @@ describe('groupDragHandler', () => {
}); });
const group = new groupMock(); const group = new groupMock();
const cut = new GroupDragHandler(element, 'accessor_id', group); const cut = new GroupDragHandler(
element,
{ id: 'accessor_id' } as DockviewComponent,
group
);
const event = new KeyboardEvent('dragstart', { shiftKey: false }); const event = new KeyboardEvent('dragstart', { shiftKey: false });
@ -82,7 +91,11 @@ describe('groupDragHandler', () => {
}); });
const group = new groupMock(); const group = new groupMock();
const cut = new GroupDragHandler(element, 'accessor_id', group); const cut = new GroupDragHandler(
element,
{ id: 'accessor_id' } as DockviewComponent,
group
);
const event = new KeyboardEvent('dragstart', { shiftKey: false }); const event = new KeyboardEvent('dragstart', { shiftKey: false });

View File

@ -1,16 +1,24 @@
import { fireEvent } from '@testing-library/dom'; import { fireEvent } from '@testing-library/dom';
import { LocalSelectionTransfer, PanelTransfer } from '../../../dnd/dataTransfer'; import {
LocalSelectionTransfer,
PanelTransfer,
} from '../../../dnd/dataTransfer';
import { DockviewComponent } from '../../../dockview/dockviewComponent'; import { DockviewComponent } from '../../../dockview/dockviewComponent';
import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../../../dockview/dockviewGroupPanel';
import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel'; import { DockviewGroupPanelModel } from '../../../dockview/dockviewGroupPanelModel';
import { Tab } from '../../../dockview/components/tab/tab'; import { Tab } from '../../../dockview/components/tab/tab';
import { IDockviewPanel } from '../../../dockview/dockviewPanel';
describe('tab', () => { describe('tab', () => {
test('that empty tab has inactive-tab class', () => { test('that empty tab has inactive-tab class', () => {
const accessorMock = jest.fn(); const accessorMock = jest.fn();
const groupMock = jest.fn(); const groupMock = jest.fn();
const cut = new Tab('panelId', new accessorMock(), new groupMock()); const cut = new Tab(
{ id: 'panelId' } as IDockviewPanel,
new accessorMock(),
new groupMock()
);
expect(cut.element.className).toBe('tab inactive-tab'); expect(cut.element.className).toBe('tab inactive-tab');
}); });
@ -19,7 +27,11 @@ describe('tab', () => {
const accessorMock = jest.fn(); const accessorMock = jest.fn();
const groupMock = jest.fn(); const groupMock = jest.fn();
const cut = new Tab('panelId', new accessorMock(), new groupMock()); const cut = new Tab(
{ id: 'panelId' } as IDockviewPanel,
new accessorMock(),
new groupMock()
);
cut.setActive(true); cut.setActive(true);
expect(cut.element.className).toBe('tab active-tab'); expect(cut.element.className).toBe('tab active-tab');
@ -54,7 +66,11 @@ describe('tab', () => {
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupPanel = new groupPanelMock() as DockviewGroupPanel; const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new Tab('panelId', accessor, groupPanel); const cut = new Tab(
{ id: 'panelId' } as IDockviewPanel,
accessor,
groupPanel
);
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation( jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
() => 100 () => 100
@ -99,7 +115,11 @@ describe('tab', () => {
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupPanel = new groupPanelMock() as DockviewGroupPanel; const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new Tab('panel1', accessor, groupPanel); const cut = new Tab(
{ id: 'panel1' } as IDockviewPanel,
accessor,
groupPanel
);
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation( jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
() => 100 () => 100
@ -149,7 +169,11 @@ describe('tab', () => {
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupPanel = new groupPanelMock() as DockviewGroupPanel; const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new Tab('panel1', accessor, groupPanel); const cut = new Tab(
{ id: 'panel1' } as IDockviewPanel,
accessor,
groupPanel
);
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation( jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
() => 100 () => 100
@ -199,7 +223,11 @@ describe('tab', () => {
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupPanel = new groupPanelMock() as DockviewGroupPanel; const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new Tab('panel1', accessor, groupPanel); const cut = new Tab(
{ id: 'panel1' } as IDockviewPanel,
accessor,
groupPanel
);
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation( jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
() => 100 () => 100
@ -255,7 +283,11 @@ describe('tab', () => {
const accessor = new accessorMock() as DockviewComponent; const accessor = new accessorMock() as DockviewComponent;
const groupPanel = new groupPanelMock() as DockviewGroupPanel; const groupPanel = new groupPanelMock() as DockviewGroupPanel;
const cut = new Tab('panel1', accessor, groupPanel); const cut = new Tab(
{ id: 'panel1' } as IDockviewPanel,
accessor,
groupPanel
);
jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation( jest.spyOn(cut.element, 'clientHeight', 'get').mockImplementation(
() => 100 () => 100

View File

@ -35,8 +35,6 @@ export abstract class DragHandler extends CompositeDisposable {
this.addDisposables( this.addDisposables(
this._onDragStart, this._onDragStart,
addDisposableListener(this.el, 'dragstart', (event) => { addDisposableListener(this.el, 'dragstart', (event) => {
this._onDragStart.fire(event);
if (event.defaultPrevented || this.isCancelled(event)) { if (event.defaultPrevented || this.isCancelled(event)) {
event.preventDefault(); event.preventDefault();
return; return;
@ -63,6 +61,7 @@ export abstract class DragHandler extends CompositeDisposable {
setTimeout(() => this.el.classList.remove('dv-dragged'), 0); setTimeout(() => this.el.classList.remove('dv-dragged'), 0);
this.dataDisposable.value = this.getData(event); this.dataDisposable.value = this.getData(event);
this._onDragStart.fire(event);
if (event.dataTransfer) { if (event.dataTransfer) {
event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.effectAllowed = 'move';

View File

@ -1,3 +1,4 @@
import { DockviewComponent } from '../dockview/dockviewComponent';
import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel'; import { DockviewGroupPanel } from '../dockview/dockviewGroupPanel';
import { quasiPreventDefault } from '../dom'; import { quasiPreventDefault } from '../dom';
import { addDisposableListener } from '../events'; import { addDisposableListener } from '../events';
@ -12,7 +13,7 @@ export class GroupDragHandler extends DragHandler {
constructor( constructor(
element: HTMLElement, element: HTMLElement,
private readonly accessorId: string, private readonly accessor: DockviewComponent,
private readonly group: DockviewGroupPanel private readonly group: DockviewGroupPanel
) { ) {
super(element); super(element);
@ -47,7 +48,7 @@ export class GroupDragHandler extends DragHandler {
const dataTransfer = dragEvent.dataTransfer; const dataTransfer = dragEvent.dataTransfer;
this.panelTransfer.setData( this.panelTransfer.setData(
[new PanelTransfer(this.accessorId, this.group.id, null)], [new PanelTransfer(this.accessor.id, this.group.id, null)],
PanelTransfer.prototype PanelTransfer.prototype
); );

View File

@ -26,7 +26,7 @@ class TabDragHandler extends DragHandler {
super(element); super(element);
} }
getData(): IDisposable { getData(event: DragEvent): IDisposable {
this.panelTransfer.setData( this.panelTransfer.setData(
[new PanelTransfer(this.accessor.id, this.group.id, this.panel.id)], [new PanelTransfer(this.accessor.id, this.group.id, this.panel.id)],
PanelTransfer.prototype PanelTransfer.prototype

View File

@ -18,7 +18,12 @@ export interface TabDropIndexEvent {
export interface TabDragEvent { export interface TabDragEvent {
readonly nativeEvent: DragEvent; readonly nativeEvent: DragEvent;
readonly panel?: IDockviewPanel; readonly panel: IDockviewPanel;
}
export interface GroupDragEvent {
readonly nativeEvent: DragEvent;
readonly group: DockviewGroupPanel;
} }
export interface ITabsContainer extends IDisposable { export interface ITabsContainer extends IDisposable {
@ -29,7 +34,8 @@ export interface ITabsContainer extends IDisposable {
delete: (id: string) => void; delete: (id: string) => void;
indexOf: (id: string) => number; indexOf: (id: string) => number;
onDrop: Event<TabDropIndexEvent>; onDrop: Event<TabDropIndexEvent>;
onDragStart: Event<TabDragEvent>; onTabDragStart: Event<TabDragEvent>;
onGroupDragStart: Event<GroupDragEvent>;
setActive: (isGroupActive: boolean) => void; setActive: (isGroupActive: boolean) => void;
setActivePanel: (panel: IDockviewPanel) => void; setActivePanel: (panel: IDockviewPanel) => void;
isActive: (tab: ITab) => boolean; isActive: (tab: ITab) => boolean;
@ -61,8 +67,12 @@ export class TabsContainer
private readonly _onDrop = new Emitter<TabDropIndexEvent>(); private readonly _onDrop = new Emitter<TabDropIndexEvent>();
readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event; readonly onDrop: Event<TabDropIndexEvent> = this._onDrop.event;
private readonly _onDragStart = new Emitter<TabDragEvent>(); private readonly _onTabDragStart = new Emitter<TabDragEvent>();
readonly onDragStart: Event<TabDragEvent> = this._onDragStart.event; readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
readonly onGroupDragStart: Event<GroupDragEvent> =
this._onGroupDragStart.event;
get panels(): string[] { get panels(): string[] {
return this.tabs.map((_) => _.value.panel.id); return this.tabs.map((_) => _.value.panel.id);
@ -140,7 +150,11 @@ export class TabsContainer
) { ) {
super(); super();
this.addDisposables(this._onDrop, this._onDragStart); this.addDisposables(
this._onDrop,
this._onTabDragStart,
this._onGroupDragStart
);
this._element = document.createElement('div'); this._element = document.createElement('div');
this._element.className = 'tabs-and-actions-container'; this._element.className = 'tabs-and-actions-container';
@ -190,6 +204,12 @@ export class TabsContainer
this.addDisposables( this.addDisposables(
this.voidContainer, this.voidContainer,
this.voidContainer.onDragStart((event) => {
this._onGroupDragStart.fire({
nativeEvent: event,
group: this.group,
});
}),
this.voidContainer.onDrop((event) => { this.voidContainer.onDrop((event) => {
this._onDrop.fire({ this._onDrop.fire({
event: event.nativeEvent, event: event.nativeEvent,
@ -302,7 +322,7 @@ export class TabsContainer
const disposable = new CompositeDisposable( const disposable = new CompositeDisposable(
tab.onDragStart((event) => { tab.onDragStart((event) => {
this._onDragStart.fire({ nativeEvent: event, panel }); this._onTabDragStart.fire({ nativeEvent: event, panel });
}), }),
tab.onChanged((event) => { tab.onChanged((event) => {
const isFloatingGroupsEnabled = const isFloatingGroupsEnabled =

View File

@ -15,6 +15,9 @@ export class VoidContainer extends CompositeDisposable {
private readonly _onDrop = new Emitter<DroptargetEvent>(); private readonly _onDrop = new Emitter<DroptargetEvent>();
readonly onDrop: Event<DroptargetEvent> = this._onDrop.event; readonly onDrop: Event<DroptargetEvent> = this._onDrop.event;
private readonly _onDragStart = new Emitter<DragEvent>();
readonly onDragStart = this._onDragStart.event;
get element(): HTMLElement { get element(): HTMLElement {
return this._element; return this._element;
} }
@ -33,12 +36,13 @@ export class VoidContainer extends CompositeDisposable {
this.addDisposables( this.addDisposables(
this._onDrop, this._onDrop,
this._onDragStart,
addDisposableListener(this._element, 'click', () => { addDisposableListener(this._element, 'click', () => {
this.accessor.doSetGroupActive(this.group); this.accessor.doSetGroupActive(this.group);
}) })
); );
const handler = new GroupDragHandler(this._element, accessor.id, group); const handler = new GroupDragHandler(this._element, accessor, group);
this.voidDropTarget = new Droptarget(this._element, { this.voidDropTarget = new Droptarget(this._element, {
acceptedTargetZones: ['center'], acceptedTargetZones: ['center'],
@ -68,6 +72,9 @@ export class VoidContainer extends CompositeDisposable {
this.addDisposables( this.addDisposables(
handler, handler,
handler.onDragStart((event) => {
this._onDragStart.fire(event);
}),
this.voidDropTarget.onDrop((event) => { this.voidDropTarget.onDrop((event) => {
this._onDrop.fire(event); this._onDrop.fire(event);
}), }),

View File

@ -50,6 +50,10 @@ import {
DockviewFloatingGroupPanel, DockviewFloatingGroupPanel,
IDockviewFloatingGroupPanel, IDockviewFloatingGroupPanel,
} from './dockviewFloatingGroupPanel'; } from './dockviewFloatingGroupPanel';
import {
GroupDragEvent,
TabDragEvent,
} from './components/titlebar/tabsContainer';
export interface PanelReference { export interface PanelReference {
update: (event: { params: { [key: string]: any } }) => void; update: (event: { params: { [key: string]: any } }) => void;
@ -130,6 +134,8 @@ export interface IDockviewComponent extends IBaseGrid<DockviewGroupPanel> {
readonly onDidAddPanel: Event<IDockviewPanel>; readonly onDidAddPanel: Event<IDockviewPanel>;
readonly onDidLayoutFromJSON: Event<void>; readonly onDidLayoutFromJSON: Event<void>;
readonly onDidActivePanelChange: Event<IDockviewPanel | undefined>; readonly onDidActivePanelChange: Event<IDockviewPanel | undefined>;
readonly onWillDragPanel: Event<TabDragEvent>;
readonly onWillDragGroup: Event<GroupDragEvent>;
addFloatingGroup( addFloatingGroup(
item: IDockviewPanel | DockviewGroupPanel, item: IDockviewPanel | DockviewGroupPanel,
coord?: { x: number; y: number } coord?: { x: number; y: number }
@ -146,6 +152,13 @@ export class DockviewComponent
private _options: Exclude<DockviewComponentOptions, 'orientation'>; private _options: Exclude<DockviewComponentOptions, 'orientation'>;
private watermark: IWatermarkRenderer | null = null; private watermark: IWatermarkRenderer | null = null;
readonly _onWillDragPanel = new Emitter<TabDragEvent>();
readonly onWillDragPanel: Event<TabDragEvent> = this._onWillDragPanel.event;
readonly _onWillDragGroup = new Emitter<GroupDragEvent>();
readonly onWillDragGroup: Event<GroupDragEvent> =
this._onWillDragGroup.event;
private readonly _onDidDrop = new Emitter<DockviewDropEvent>(); private readonly _onDidDrop = new Emitter<DockviewDropEvent>();
readonly onDidDrop: Event<DockviewDropEvent> = this._onDidDrop.event; readonly onDidDrop: Event<DockviewDropEvent> = this._onDidDrop.event;
@ -204,6 +217,12 @@ export class DockviewComponent
toggleClass(this.gridview.element, 'dv-dockview', true); toggleClass(this.gridview.element, 'dv-dockview', true);
this.addDisposables( this.addDisposables(
this._onWillDragPanel,
this._onWillDragGroup,
this._onDidActivePanelChange,
this._onDidAddPanel,
this._onDidRemovePanel,
this._onDidLayoutFromJSON,
this._onDidDrop, this._onDidDrop,
Event.any( Event.any(
this.onDidAddGroup, this.onDidAddGroup,
@ -1157,8 +1176,11 @@ export class DockviewComponent
if (!this._groups.has(view.id)) { if (!this._groups.has(view.id)) {
const disposable = new CompositeDisposable( const disposable = new CompositeDisposable(
view.model.onDragStart((event) => { view.model.onTabDragStart((event) => {
this.onDragStart(event); this._onWillDragPanel.fire(event);
}),
view.model.onGroupDragStart((event) => {
this._onWillDragGroup.fire(event);
}), }),
view.model.onMove((event) => { view.model.onMove((event) => {
const { groupId, itemId, target, index } = event; const { groupId, itemId, target, index } = event;
@ -1235,13 +1257,4 @@ export class DockviewComponent
group.value.model.containsPanel(panel) group.value.model.containsPanel(panel)
)?.value; )?.value;
} }
public dispose(): void {
this._onDidActivePanelChange.dispose();
this._onDidAddPanel.dispose();
this._onDidRemovePanel.dispose();
this._onDidLayoutFromJSON.dispose();
super.dispose();
}
} }

View File

@ -12,6 +12,7 @@ import {
IContentContainer, IContentContainer,
} from './components/panel/content'; } from './components/panel/content';
import { import {
GroupDragEvent,
ITabsContainer, ITabsContainer,
TabDragEvent, TabDragEvent,
TabsContainer, TabsContainer,
@ -92,6 +93,8 @@ export interface IDockviewGroupPanelModel extends IPanel {
readonly onDidRemovePanel: Event<GroupviewChangeEvent>; readonly onDidRemovePanel: Event<GroupviewChangeEvent>;
readonly onDidActivePanelChange: Event<GroupviewChangeEvent>; readonly onDidActivePanelChange: Event<GroupviewChangeEvent>;
readonly onMove: Event<GroupMoveEvent>; readonly onMove: Event<GroupMoveEvent>;
readonly onTabDragStart: Event<TabDragEvent>;
readonly onGroupDragStart: Event<GroupDragEvent>;
locked: boolean; locked: boolean;
setActive(isActive: boolean): void; setActive(isActive: boolean): void;
initialize(): void; initialize(): void;
@ -159,8 +162,12 @@ export class DockviewGroupPanelModel
private readonly _onDidDrop = new Emitter<GroupviewDropEvent>(); private readonly _onDidDrop = new Emitter<GroupviewDropEvent>();
readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event; readonly onDidDrop: Event<GroupviewDropEvent> = this._onDidDrop.event;
private readonly _onDragStart = new Emitter<TabDragEvent>(); private readonly _onTabDragStart = new Emitter<TabDragEvent>();
readonly onDragStart: Event<TabDragEvent> = this._onDragStart.event; readonly onTabDragStart: Event<TabDragEvent> = this._onTabDragStart.event;
private readonly _onGroupDragStart = new Emitter<GroupDragEvent>();
readonly onGroupDragStart: Event<GroupDragEvent> =
this._onGroupDragStart.event;
private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>(); private readonly _onDidAddPanel = new Emitter<GroupviewChangeEvent>();
readonly onDidAddPanel: Event<GroupviewChangeEvent> = readonly onDidAddPanel: Event<GroupviewChangeEvent> =
@ -310,8 +317,13 @@ export class DockviewGroupPanelModel
this.locked = !!options.locked; this.locked = !!options.locked;
this.addDisposables( this.addDisposables(
this.tabsContainer.onDragStart((event) => { this._onTabDragStart,
this._onDragStart.fire(event); this._onGroupDragStart,
this.tabsContainer.onTabDragStart((event) => {
this._onTabDragStart.fire(event);
}),
this.tabsContainer.onGroupDragStart((event) => {
this._onGroupDragStart.fire(event);
}), }),
this.tabsContainer.onDrop((event) => { this.tabsContainer.onDrop((event) => {
this.handleDropEvent(event.event, 'center', event.index); this.handleDropEvent(event.event, 'center', event.index);