diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index f002e9af6..1ec04ab90 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -8,7 +8,7 @@ import { PanelUpdateEvent } from '../../panel/types'; import { Orientation } from '../../splitview/splitview'; import { CompositeDisposable } from '../../lifecycle'; import { Emitter } from '../../events'; -import { IDockviewPanel } from '../../dockview/dockviewPanel'; +import { DockviewPanel, IDockviewPanel } from '../../dockview/dockviewPanel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; import { fireEvent } from '@testing-library/dom'; import { getPanelData } from '../../dnd/dataTransfer'; @@ -4598,6 +4598,64 @@ describe('dockviewComponent', () => { }); }); + describe('that emits onDidLayoutChange', () => { + let dockview: DockviewComponent; + let panel1: DockviewPanel; + + beforeEach(() => { + jest.useFakeTimers(); + + dockview = new DockviewComponent({ + parentElement: container, + components: { + default: PanelContentPartTest, + }, + tabComponents: { + test_tab_id: PanelTabPartTest, + }, + orientation: Orientation.HORIZONTAL, + }); + + panel1 = dockview.addPanel({ + id: 'panel_1', + component: 'default', + }); + }); + + afterEach(() => { + jest.runAllTimers(); + jest.useRealTimers(); + }); + + test('that emits onDidPanelTitleChange and onDidLayoutChange when the panel set a title', () => { + const didLayoutChangeHandler = jest.fn(); + const { dispose: disposeDidLayoutChangeHandler } = + dockview.onDidLayoutChange(didLayoutChangeHandler); + + panel1.setTitle('new title'); + + jest.runAllTimers(); + + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(1); + + disposeDidLayoutChangeHandler(); + }); + + test('that emits onDidPanelParametersChange and onDidLayoutChange when the panel updates parameters', () => { + const didLayoutChangeHandler = jest.fn(); + const { dispose: disposeDidLayoutChangeHandler } = + dockview.onDidLayoutChange(didLayoutChangeHandler); + + panel1.api.updateParameters({ keyA: 'valueA' }); + + jest.runAllTimers(); + + expect(didLayoutChangeHandler).toHaveBeenCalledTimes(1); + + disposeDidLayoutChangeHandler(); + }); + }); + test('that setVisible toggles visiblity', () => { const container = document.createElement('div'); diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts index b1963dbe5..939380ec6 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewGroupPanelModel.spec.ts @@ -9,19 +9,40 @@ import { } from '../../dockview/types'; import { PanelUpdateEvent, Parameters } from '../../panel/types'; import { + DockviewGroupLocation, DockviewGroupPanelModel, GroupOptions, } from '../../dockview/dockviewGroupPanelModel'; import { fireEvent } from '@testing-library/dom'; import { LocalSelectionTransfer, PanelTransfer } from '../../dnd/dataTransfer'; import { CompositeDisposable } from '../../lifecycle'; -import { DockviewPanelApi } from '../../api/dockviewPanelApi'; +import { + ActiveGroupEvent, + DockviewPanelApi, + GroupChangedEvent, + RendererChangedEvent, +} from '../../api/dockviewPanelApi'; import { IDockviewPanel } from '../../dockview/dockviewPanel'; import { IDockviewPanelModel } from '../../dockview/dockviewPanelModel'; import { DockviewGroupPanel } from '../../dockview/dockviewGroupPanel'; import { WatermarkRendererInitParameters } from '../../dockview/types'; import { createOffsetDragOverEvent } from '../__test_utils__/utils'; -import { OverlayRenderContainer } from '../../overlayRenderContainer'; +import { + DockviewPanelRenderer, + OverlayRenderContainer, +} from '../../overlayRenderContainer'; +import { DockviewGroupPanelFloatingChangeEvent } from '../../api/dockviewGroupPanelApi'; +import { SizeEvent } from '../../api/gridviewPanelApi'; +import { + PanelDimensionChangeEvent, + FocusEvent, + VisibilityEvent, + ActiveEvent, + WillFocusEvent, +} from '../../api/panelApi'; +import { Position } from '../../dnd/droptarget'; +import { Emitter, Event } from '../../events'; +import { fromPartial } from '@total-typescript/shoehorn'; enum GroupChangeKind2 { ADD_PANEL, @@ -240,12 +261,20 @@ describe('dockviewGroupPanelModel', () => { let removePanelMock: jest.Mock; let removeGroupMock: jest.Mock; + let panelApi: DockviewPanelApi; + beforeEach(() => { removePanelMock = jest.fn(); removeGroupMock = jest.fn(); options = {}; + panelApi = fromPartial({ + renderer: 'onlyWhenVisibile', + onDidTitleChange: new Emitter().event, + onDidParametersChange: new Emitter().event, + }); + dockview = (>{ options: { parentElement: document.createElement('div') }, createWatermarkComponent: () => new Watermark(), @@ -265,15 +294,9 @@ describe('dockviewGroupPanelModel', () => { }); test('panel events are captured during de-serialization', () => { - const panel1 = new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any); - const panel2 = new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any); - const panel3 = new TestPanel('panel3', { - renderer: 'onlyWhenVisibile', - } as any); + const panel1 = new TestPanel('panel1', panelApi); + const panel2 = new TestPanel('panel2', panelApi); + const panel3 = new TestPanel('panel3', panelApi); const groupview2 = new DockviewGroupPanel(dockview, 'groupview-2', { panels: [panel1, panel2, panel3], @@ -357,15 +380,9 @@ describe('dockviewGroupPanelModel', () => { }) ); - const panel1 = new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any); - const panel2 = new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any); - const panel3 = new TestPanel('panel3', { - renderer: 'onlyWhenVisibile', - } as any); + const panel1 = new TestPanel('panel1', panelApi); + const panel2 = new TestPanel('panel2', panelApi); + const panel3 = new TestPanel('panel3', panelApi); expect(events.length).toBe(0); @@ -443,15 +460,9 @@ describe('dockviewGroupPanelModel', () => { }); test('moveToPrevious and moveToNext', () => { - const panel1 = new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any); - const panel2 = new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any); - const panel3 = new TestPanel('panel3', { - renderer: 'onlyWhenVisibile', - } as any); + const panel1 = new TestPanel('panel1', panelApi); + const panel2 = new TestPanel('panel2', panelApi); + const panel3 = new TestPanel('panel3', panelApi); groupview.model.openPanel(panel1); groupview.model.openPanel(panel2); @@ -495,15 +506,9 @@ describe('dockviewGroupPanelModel', () => { }); test('closeAllPanels with panels', () => { - const panel1 = new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any); - const panel2 = new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any); - const panel3 = new TestPanel('panel3', { - renderer: 'onlyWhenVisibile', - } as any); + const panel1 = new TestPanel('panel1', panelApi); + const panel2 = new TestPanel('panel2', panelApi); + const panel3 = new TestPanel('panel3', panelApi); groupview.model.openPanel(panel1); groupview.model.openPanel(panel2); @@ -608,25 +613,19 @@ describe('dockviewGroupPanelModel', () => { .getElementsByClassName('content-container') .item(0)!.childNodes; - const panel1 = new TestPanel('id_1', { - renderer: 'onlyWhenVisibile', - } as any); + const panel1 = new TestPanel('id_1', panelApi); cut.openPanel(panel1); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel1.view.content.element); - const panel2 = new TestPanel('id_2', { - renderer: 'onlyWhenVisibile', - } as any); + const panel2 = new TestPanel('id_2', panelApi); cut.openPanel(panel2); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel2.view.content.element); - const panel3 = new TestPanel('id_2', { - renderer: 'onlyWhenVisibile', - } as any); + const panel3 = new TestPanel('id_2', panelApi); cut.openPanel(panel3, { skipSetActive: true }); expect(contentContainer.length).toBe(1); @@ -834,11 +833,7 @@ describe('dockviewGroupPanelModel', () => { new groupPanelMock() as DockviewGroupPanel ); - cut.openPanel( - new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel1', panelApi)); const element = container .getElementsByClassName('content-container') @@ -908,16 +903,8 @@ describe('dockviewGroupPanelModel', () => { new groupPanelMock() as DockviewGroupPanel ); - cut.openPanel( - new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any) - ); - cut.openPanel( - new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel1', panelApi)); + cut.openPanel(new TestPanel('panel2', panelApi)); const element = container .getElementsByClassName('content-container') @@ -987,16 +974,8 @@ describe('dockviewGroupPanelModel', () => { new groupPanelMock() as DockviewGroupPanel ); - cut.openPanel( - new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any) - ); - cut.openPanel( - new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel1', panelApi)); + cut.openPanel(new TestPanel('panel2', panelApi)); const element = container .getElementsByClassName('content-container') @@ -1097,11 +1076,7 @@ describe('dockviewGroupPanelModel', () => { container.getElementsByClassName('watermark-test-container').length ).toBe(1); - cut.openPanel( - new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel1', panelApi)); expect( container.getElementsByClassName('watermark-test-container').length @@ -1111,11 +1086,7 @@ describe('dockviewGroupPanelModel', () => { .length ).toBe(1); - cut.openPanel( - new TestPanel('panel2', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel2', panelApi)); expect( container.getElementsByClassName('watermark-test-container').length @@ -1133,11 +1104,7 @@ describe('dockviewGroupPanelModel', () => { container.getElementsByClassName('watermark-test-container').length ).toBe(1); - cut.openPanel( - new TestPanel('panel1', { - renderer: 'onlyWhenVisibile', - } as any) - ); + cut.openPanel(new TestPanel('panel1', panelApi)); expect( container.getElementsByClassName('watermark-test-container').length diff --git a/packages/dockview-core/src/api/dockviewPanelApi.ts b/packages/dockview-core/src/api/dockviewPanelApi.ts index 06a4ebc22..420657ec2 100644 --- a/packages/dockview-core/src/api/dockviewPanelApi.ts +++ b/packages/dockview-core/src/api/dockviewPanelApi.ts @@ -37,6 +37,7 @@ export interface DockviewPanelApi readonly title: string | undefined; readonly onDidActiveGroupChange: Event; readonly onDidGroupChange: Event; + readonly onDidTitleChange: Event; readonly onDidRendererChange: Event; readonly location: DockviewGroupLocation; readonly onDidLocationChange: Event; diff --git a/packages/dockview-core/src/api/panelApi.ts b/packages/dockview-core/src/api/panelApi.ts index 698c480e9..ad332613a 100644 --- a/packages/dockview-core/src/api/panelApi.ts +++ b/packages/dockview-core/src/api/panelApi.ts @@ -24,6 +24,7 @@ export interface PanelApi { readonly onDidFocusChange: Event; readonly onDidVisibilityChange: Event; readonly onDidActiveChange: Event; + readonly onDidParametersChange: Event; setActive(): void; setVisible(isVisible: boolean): void; updateParameters(parameters: Parameters): void; @@ -53,6 +54,8 @@ export interface PanelApi { readonly height: number; readonly onWillFocus: Event; + + getParameters(): T; } export class WillFocusEvent extends DockviewEvent { @@ -70,6 +73,7 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { private _isVisible = true; private _width = 0; private _height = 0; + private _parameters: Parameters = {}; private readonly panelUpdatesDisposable = new MutableDisposable(); @@ -97,9 +101,9 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { readonly _onActiveChange = new Emitter(); readonly onActiveChange: Event = this._onActiveChange.event; - readonly _onUpdateParameters = new Emitter(); - readonly onUpdateParameters: Event = - this._onUpdateParameters.event; + readonly _onDidParametersChange = new Emitter(); + readonly onDidParametersChange: Event = + this._onDidParametersChange.event; get isFocused(): boolean { return this._isFocused; @@ -145,16 +149,20 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { this._onDidActiveChange, this._onWillFocus, this._onActiveChange, - this._onUpdateParameters, this._onWillFocus, this._onWillVisibilityChange, - this._onUpdateParameters + this._onDidParametersChange ); } + getParameters(): T { + return this._parameters as T; + } + public initialize(panel: IPanel): void { - this.panelUpdatesDisposable.value = this._onUpdateParameters.event( + this.panelUpdatesDisposable.value = this._onDidParametersChange.event( (parameters) => { + this._parameters = parameters; panel.update({ params: parameters, }); @@ -171,6 +179,6 @@ export class PanelApiImpl extends CompositeDisposable implements PanelApi { } updateParameters(parameters: Parameters): void { - this._onUpdateParameters.fire(parameters); + this._onDidParametersChange.fire(parameters); } } diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index a5fdacf39..220a5ad1c 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -65,6 +65,7 @@ import { OverlayRenderContainer, } from '../overlayRenderContainer'; import { PopoutWindow } from '../popoutWindow'; +import { TitleEvent } from '../api/dockviewPanelApi'; const DEFAULT_ROOT_OVERLAY_MODEL: DroptargetOverlayModel = { activationSize: { type: 'pixels', value: 10 }, @@ -2175,6 +2176,12 @@ export class DockviewComponent if (this._onDidActivePanelChange.value !== event.panel) { this._onDidActivePanelChange.fire(event.panel); } + }), + Event.any( + view.model.onDidPanelTitleChange, + view.model.onDidPanelParametersChange + )(() => { + this._bufferOnDidLayoutChange.fire(); }) ); diff --git a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts index d690d0f4c..e1989dd68 100644 --- a/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts +++ b/packages/dockview-core/src/dockview/dockviewGroupPanelModel.ts @@ -11,8 +11,13 @@ import { IDockviewEvent, } from '../events'; import { IViewSize } from '../gridview/gridview'; -import { CompositeDisposable } from '../lifecycle'; -import { IPanel, PanelInitParameters, PanelUpdateEvent } from '../panel/types'; +import { CompositeDisposable, IDisposable } from '../lifecycle'; +import { + IPanel, + PanelInitParameters, + PanelUpdateEvent, + Parameters, +} from '../panel/types'; import { ContentContainer, IContentContainer, @@ -28,6 +33,7 @@ import { DockviewGroupPanel } from './dockviewGroupPanel'; import { IDockviewPanel } from './dockviewPanel'; import { IHeaderActionsRenderer } from './options'; import { OverlayRenderContainer } from '../overlayRenderContainer'; +import { TitleEvent } from '../api/dockviewPanelApi'; interface GroupMoveEvent { groupId: string; @@ -245,6 +251,7 @@ export class DockviewGroupPanelModel private _height = 0; private _panels: IDockviewPanel[] = []; + private readonly _panelDisposables = new Map(); private readonly _onMove = new Emitter(); readonly onMove: Event = this._onMove.event; @@ -271,6 +278,14 @@ export class DockviewGroupPanelModel readonly onDidAddPanel: Event = this._onDidAddPanel.event; + private readonly _onDidPanelTitleChange = new Emitter(); + readonly onDidPanelTitleChange: Event = + this._onDidPanelTitleChange.event; + + private readonly _onDidPanelParametersChange = new Emitter(); + readonly onDidPanelParametersChange: Event = + this._onDidPanelParametersChange.event; + private readonly _onDidRemovePanel = new Emitter(); readonly onDidRemovePanel: Event = @@ -826,6 +841,12 @@ export class DockviewGroupPanelModel ); } + const disposable = this._panelDisposables.get(panel.id); + if (disposable) { + disposable.dispose(); + this._panelDisposables.delete(panel.id); + } + this._onDidRemovePanel.fire({ panel }); } @@ -856,6 +877,18 @@ export class DockviewGroupPanelModel this.updateMru(panel); this.panels.splice(index, 0, panel); + this._panelDisposables.set( + panel.id, + new CompositeDisposable( + panel.api.onDidTitleChange((event) => + this._onDidPanelTitleChange.fire(event) + ), + panel.api.onDidParametersChange((event) => + this._onDidPanelParametersChange.fire(event) + ) + ) + ); + this._onDidAddPanel.fire({ panel }); }