From 1f022635cf7f0140c52f2c2637cdd9ae1620843d Mon Sep 17 00:00:00 2001 From: mathuo <6710312+mathuo@users.noreply.github.com> Date: Sat, 21 May 2022 21:48:10 +0100 Subject: [PATCH] feat: improve events --- .../__tests__/dockview/deserializer.spec.ts | 95 -- .../dockview/dockviewComponent.spec.ts | 187 ++- .../gridview/gridviewComponent.spec.ts | 122 ++ .../src/__tests__/groupview/groupview.spec.ts | 2 +- .../dockview/src/dockview/deserializer.ts | 46 - .../src/dockview/dockviewComponent.ts | 1169 +++++++++-------- .../src/gridview/baseComponentGridview.ts | 4 +- .../src/gridview/gridviewComponent.ts | 8 + packages/dockview/src/groupview/groupview.ts | 19 +- 9 files changed, 927 insertions(+), 725 deletions(-) delete mode 100644 packages/dockview/src/__tests__/dockview/deserializer.spec.ts diff --git a/packages/dockview/src/__tests__/dockview/deserializer.spec.ts b/packages/dockview/src/__tests__/dockview/deserializer.spec.ts deleted file mode 100644 index e4bf4cc24..000000000 --- a/packages/dockview/src/__tests__/dockview/deserializer.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { - DefaultDeserializer, - PanelDeserializerOptions, -} from '../../dockview/deserializer'; -import { DockviewComponent } from '../../dockview/dockviewComponent'; -import { Groupview } from '../../groupview/groupview'; -import { GroupPanel } from '../../groupview/groupviewPanel'; - -describe('deserializer', () => { - test('fromJSON', () => { - const openPanel = jest.fn(); - - const model = jest.fn(() => { - const result: Partial = { - openPanel, - }; - - return result as Groupview; - }); - - const panel1 = jest.fn(); - const panel2 = jest.fn(); - - const groupMock = jest.fn(() => { - const result: Partial = { - model: new model(), - panels: [panel1, panel2], - activePanel: null, - }; - - return result as GroupPanel; - }); - const group = new groupMock(); - const createGroup = jest.fn().mockReturnValue(new groupMock()); - - const dockviewComponentMock = jest.fn(() => { - const value: Partial = { - createGroup, - }; - - return value; - }); - - const createPanel = jest - .fn() - .mockImplementation((child) => ({ id: child })); - - const panelDeserializer: PanelDeserializerOptions = { - createPanel, - }; - - const dockviewComponent = new dockviewComponentMock(); - - const cut = new DefaultDeserializer( - dockviewComponent, - panelDeserializer - ); - - cut.fromJSON({ - type: 'leaf', - size: 100, - visible: true, - data: { - hideHeader: true, - locked: true, - id: 'id', - views: ['view1', 'view2'], - activeView: 'view2', - }, - }); - - expect(createGroup).toBeCalledWith({ - id: 'id', - locked: true, - hideHeader: true, - }); - expect(createGroup).toBeCalledTimes(1); - - expect(createPanel).toBeCalledWith('view1', group); - expect(createPanel).toBeCalledWith('view2', group); - expect(createPanel).toBeCalledTimes(2); - - expect(openPanel).toBeCalledWith( - { id: 'view1' }, - { skipSetActive: true } - ); - expect(openPanel).toBeCalledWith( - { id: 'view2' }, - { skipSetActive: false } - ); - - expect(openPanel).toBeCalledWith(panel2); - expect(openPanel).toBeCalledTimes(3); - }); -}); diff --git a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts index e2bf9fd5d..3839d56c7 100644 --- a/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/dockview/dockviewComponent.spec.ts @@ -254,34 +254,34 @@ describe('dockviewComponent', () => { const panel3 = dockview.getGroupPanel('panel3'); const panel4 = dockview.getGroupPanel('panel4'); - const group1 = panel1.group; + const group1 = panel1!.group; dockview.moveGroupOrPanel(group1, group1.id, 'panel1', Position.Right); - const group2 = panel1.group; + const group2 = panel1!.group; dockview.moveGroupOrPanel(group2, group1.id, 'panel3', Position.Center); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.model.activePanel).toBe(panel3); - expect(dockview.activeGroup.model.indexOf(panel3)).toBe(1); + expect(dockview.activeGroup!.model.activePanel).toBe(panel3); + expect(dockview.activeGroup!.model.indexOf(panel3!)).toBe(1); dockview.moveToPrevious({ includePanel: true }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.model.activePanel).toBe(panel1); + expect(dockview.activeGroup!.model.activePanel).toBe(panel1); dockview.moveToNext({ includePanel: true }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.model.activePanel).toBe(panel3); + expect(dockview.activeGroup!.model.activePanel).toBe(panel3); dockview.moveToPrevious({ includePanel: false }); expect(dockview.activeGroup).toBe(group1); - expect(dockview.activeGroup.model.activePanel).toBe(panel4); + expect(dockview.activeGroup!.model.activePanel).toBe(panel4); dockview.moveToPrevious({ includePanel: true }); expect(dockview.activeGroup).toBe(group1); - expect(dockview.activeGroup.model.activePanel).toBe(panel2); + expect(dockview.activeGroup!.model.activePanel).toBe(panel2); dockview.moveToNext({ includePanel: false }); expect(dockview.activeGroup).toBe(group2); - expect(dockview.activeGroup.model.activePanel).toBe(panel3); + expect(dockview.activeGroup!.model.activePanel).toBe(panel3); }); test('remove group', () => { @@ -1517,6 +1517,175 @@ describe('dockviewComponent', () => { expect(panel2Spy).toBeCalledTimes(1); }); + test('fromJSON events should still fire', () => { + jest.useFakeTimers(); + + dockview.layout(1000, 1000); + + let addGroup: GroupPanel[] = []; + let removeGroup: GroupPanel[] = []; + let activeGroup: (GroupPanel | undefined)[] = []; + let addPanel: IDockviewPanel[] = []; + let removePanel: IDockviewPanel[] = []; + let activePanel: (IDockviewPanel | undefined)[] = []; + let layoutChange = 0; + let layoutChangeFromJson = 0; + + const disposable = new CompositeDisposable( + dockview.onDidAddGroup((panel) => { + addGroup.push(panel); + }), + dockview.onDidRemoveGroup((panel) => { + removeGroup.push(panel); + }), + dockview.onDidActiveGroupChange((event) => { + activeGroup.push(event); + }), + dockview.onDidAddPanel((panel) => { + addPanel.push(panel); + }), + dockview.onDidRemovePanel((panel) => { + removePanel.push(panel); + }), + dockview.onDidActivePanelChange((event) => { + activePanel.push(event); + }), + dockview.onDidLayoutChange(() => { + layoutChange++; + }), + dockview.onDidLayoutFromJSON(() => { + layoutChangeFromJson++; + }) + ); + + dockview.deserializer = new ReactPanelDeserialzier(dockview); + dockview.fromJSON({ + activeGroup: 'group-1', + grid: { + root: { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel1'], + id: 'group-1', + activeView: 'panel1', + }, + size: 500, + }, + { + type: 'branch', + data: [ + { + type: 'leaf', + data: { + views: ['panel2', 'panel3'], + id: 'group-2', + }, + size: 500, + }, + { + type: 'leaf', + data: { views: ['panel4'], id: 'group-3' }, + size: 500, + }, + ], + size: 250, + }, + { + type: 'leaf', + data: { views: ['panel5'], id: 'group-4' }, + size: 250, + }, + ], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: { + panel1: { + id: 'panel1', + view: { content: { id: 'default' } }, + title: 'panel1', + }, + panel2: { + id: 'panel2', + view: { content: { id: 'default' } }, + title: 'panel2', + }, + panel3: { + id: 'panel3', + view: { content: { id: 'default' } }, + title: 'panel3', + }, + panel4: { + id: 'panel4', + view: { content: { id: 'default' } }, + title: 'panel4', + }, + panel5: { + id: 'panel5', + view: { content: { id: 'default' } }, + title: 'panel5', + }, + }, + options: { tabHeight: 25 }, + }); + + jest.runAllTimers(); + + console.log(activePanel.map((_) => _?.id).join(' ')); + + expect(addGroup.length).toBe(4); + expect(removeGroup.length).toBe(0); + expect(activeGroup.length).toBe(1); + expect(addPanel.length).toBe(5); + expect(removePanel.length).toBe(0); + expect(activePanel.length).toBe(5); + expect(layoutChange).toBe(1); + expect(layoutChangeFromJson).toBe(1); + + addGroup = []; + removeGroup = []; + activeGroup = []; + addPanel = []; + removePanel = []; + activePanel = []; + layoutChange = 0; + layoutChangeFromJson = 0; + + dockview.fromJSON({ + grid: { + root: { + type: 'branch', + data: [], + size: 1000, + }, + height: 1000, + width: 1000, + orientation: Orientation.VERTICAL, + }, + panels: {}, + options: { tabHeight: 25 }, + }); + + jest.runAllTimers(); + + expect(addGroup.length).toBe(0); + expect(removeGroup.length).toBe(4); + expect(activeGroup.length).toBe(1); + expect(addPanel.length).toBe(0); + expect(removePanel.length).toBe(5); + expect(activePanel.length).toBe(1); + expect(layoutChange).toBe(1); + expect(layoutChangeFromJson).toBe(1); + + return disposable.dispose(); + }); + // group is disposed of when dockview is disposed // watermark is disposed of when removed // watermark is disposed of when dockview is disposed diff --git a/packages/dockview/src/__tests__/gridview/gridviewComponent.spec.ts b/packages/dockview/src/__tests__/gridview/gridviewComponent.spec.ts index 77a748333..19184a993 100644 --- a/packages/dockview/src/__tests__/gridview/gridviewComponent.spec.ts +++ b/packages/dockview/src/__tests__/gridview/gridviewComponent.spec.ts @@ -1761,4 +1761,126 @@ describe('gridview', () => { expect(panel1Spy).toHaveBeenCalledTimes(1); expect(panel2Spy).toHaveBeenCalledTimes(1); }); + + test('fromJSON events should still fire', () => { + jest.useFakeTimers(); + + const gridview = new GridviewComponent(container, { + proportionalLayout: true, + orientation: Orientation.HORIZONTAL, + components: { default: TestGridview }, + }); + + let addGroup: GridviewPanel[] = []; + let removeGroup: GridviewPanel[] = []; + let activeGroup: (GridviewPanel | undefined)[] = []; + let layoutChange = 0; + let layoutChangeFromJson = 0; + + const disposable = new CompositeDisposable( + gridview.onDidAddGroup((panel) => { + addGroup.push(panel); + }), + gridview.onDidRemoveGroup((panel) => { + removeGroup.push(panel); + }), + gridview.onDidActiveGroupChange((event) => { + activeGroup.push(event); + }), + gridview.onDidLayoutChange(() => { + layoutChange++; + }), + gridview.onDidLayoutFromJSON(() => { + layoutChangeFromJson++; + }) + ); + + gridview.fromJSON({ + grid: { + height: 400, + width: 800, + orientation: Orientation.HORIZONTAL, + root: { + type: 'branch', + size: 400, + data: [ + { + type: 'leaf', + size: 200, + data: { + id: 'panel_1', + component: 'default', + snap: false, + }, + }, + { + type: 'branch', + size: 400, + data: [ + { + type: 'leaf', + size: 250, + data: { + id: 'panel_2', + component: 'default', + snap: false, + }, + }, + { + type: 'leaf', + size: 150, + data: { + id: 'panel_3', + component: 'default', + snap: false, + }, + }, + ], + }, + { + type: 'leaf', + size: 200, + data: { + id: 'panel_4', + component: 'default', + snap: false, + }, + }, + ], + }, + }, + activePanel: 'panel_1', + }); + + jest.runAllTimers(); + + expect(addGroup.length).toBe(4); + expect(removeGroup.length).toBe(0); + expect(activeGroup.length).toBe(1); + expect(activeGroup[0]).toEqual(gridview.getPanel('panel_1')); + expect(layoutChange).toBe(1); + expect(layoutChangeFromJson).toBe(1); + + addGroup = []; + activeGroup = []; + + gridview.fromJSON({ + grid: { + height: 0, + width: 0, + root: { type: 'branch', data: [] }, + orientation: Orientation.HORIZONTAL, + }, + }); + + jest.runAllTimers(); + + expect(addGroup.length).toBe(0); + expect(removeGroup.length).toBe(4); + expect(activeGroup.length).toBe(1); + expect(layoutChange).toBe(2); + expect(layoutChangeFromJson).toBe(2); + + return disposable.dispose(); + }); }); diff --git a/packages/dockview/src/__tests__/groupview/groupview.spec.ts b/packages/dockview/src/__tests__/groupview/groupview.spec.ts index b67c30766..838d7affa 100644 --- a/packages/dockview/src/__tests__/groupview/groupview.spec.ts +++ b/packages/dockview/src/__tests__/groupview/groupview.spec.ts @@ -560,7 +560,7 @@ describe('groupview', () => { const panel3 = new TestPanel('id_2', null); - cut.openPanel(panel3, { skipSetActive: true }); + cut.openPanel(panel3, { skipSetPanelActive: true }); expect(contentContainer.length).toBe(1); expect(contentContainer.item(0)).toBe(panel2.view.content.element); diff --git a/packages/dockview/src/dockview/deserializer.ts b/packages/dockview/src/dockview/deserializer.ts index 490a32ed8..afb5adde6 100644 --- a/packages/dockview/src/dockview/deserializer.ts +++ b/packages/dockview/src/dockview/deserializer.ts @@ -1,52 +1,6 @@ -import { - IGridView, - ISerializedLeafNode, - IViewDeserializer, -} from '../gridview/gridview'; import { GroupviewPanelState, IDockviewPanel } from '../groupview/groupPanel'; -import { GroupPanelViewState } from '../groupview/groupview'; import { GroupPanel } from '../groupview/groupviewPanel'; -import { DockviewComponent } from './dockviewComponent'; export interface IPanelDeserializer { fromJSON(panelData: GroupviewPanelState, group: GroupPanel): IDockviewPanel; } - -export interface PanelDeserializerOptions { - createPanel: (id: string, group: GroupPanel) => IDockviewPanel; -} - -export class DefaultDeserializer implements IViewDeserializer { - constructor( - private readonly layout: DockviewComponent, - private panelDeserializer: PanelDeserializerOptions - ) {} - - public fromJSON(node: ISerializedLeafNode): IGridView { - const data = node.data; - const children = data.views; - const active = data.activeView; - - const group = this.layout.createGroup({ - id: data.id, - locked: !!data.locked, - hideHeader: !!data.hideHeader, - }); - - for (const child of children) { - const panel = this.panelDeserializer.createPanel(child, group); - - const isActive = typeof active === 'string' && active === panel.id; - - group.model.openPanel(panel, { - skipSetActive: !isActive, - }); - } - - if (!group.activePanel && group.panels.length > 0) { - group.model.openPanel(group.panels[group.panels.length - 1]); - } - - return group; - } -} diff --git a/packages/dockview/src/dockview/dockviewComponent.ts b/packages/dockview/src/dockview/dockviewComponent.ts index 8a0559988..819a0af7b 100644 --- a/packages/dockview/src/dockview/dockviewComponent.ts +++ b/packages/dockview/src/dockview/dockviewComponent.ts @@ -1,7 +1,8 @@ import { - getRelativeLocation, - SerializedGridObject, - getGridLocation, + getRelativeLocation, + SerializedGridObject, + getGridLocation, + ISerializedLeafNode, } from '../gridview/gridview'; import { Position } from '../dnd/droptarget'; import { tail, sequenceEquals } from '../array'; @@ -11,34 +12,34 @@ import { CompositeDisposable } from '../lifecycle'; import { Event, Emitter } from '../events'; import { Watermark } from './components/watermark/watermark'; import { - IContentRenderer, - ITabRenderer, - IWatermarkRenderer, + IContentRenderer, + ITabRenderer, + IWatermarkRenderer, } from '../groupview/types'; import { sequentialNumberGenerator } from '../math'; -import { DefaultDeserializer, IPanelDeserializer } from './deserializer'; +import { IPanelDeserializer } from './deserializer'; import { createComponent } from '../panel/componentFactory'; import { - AddGroupOptions, - AddPanelOptions, - DockviewComponentOptions, - MovementOptions, - TabContextMenuEvent, + AddGroupOptions, + AddPanelOptions, + DockviewComponentOptions, + MovementOptions, + TabContextMenuEvent, } from './options'; import { - BaseGrid, - IBaseGrid, - toTarget, + BaseGrid, + IBaseGrid, + toTarget, } from '../gridview/baseComponentGridview'; import { DockviewApi } from '../api/component.api'; import { LayoutMouseEvent, MouseEventKind } from '../groupview/tab'; import { Orientation } from '../splitview/core/splitview'; import { DefaultTab } from './components/tab/defaultTab'; import { - GroupChangeKind2, - GroupOptions, - GroupPanelViewState, - GroupviewDropEvent, + GroupChangeKind2, + GroupOptions, + GroupPanelViewState, + GroupviewDropEvent, } from '../groupview/groupview'; import { GroupPanel } from '../groupview/groupviewPanel'; import { DefaultGroupPanelView } from './defaultGroupPanelView'; @@ -46,20 +47,20 @@ import { DefaultGroupPanelView } from './defaultGroupPanelView'; const nextGroupId = sequentialNumberGenerator(); export interface PanelReference { - update: (event: { params: { [key: string]: any } }) => void; - remove: () => void; + update: (event: { params: { [key: string]: any } }) => void; + remove: () => void; } export interface SerializedDockview { - grid: { - root: SerializedGridObject; - height: number; - width: number; - orientation: Orientation; - }; - panels: { [key: string]: GroupviewPanelState }; - activeGroup?: string; - options?: { tabHeight?: number }; + grid: { + root: SerializedGridObject; + height: number; + width: number; + orientation: Orientation; + }; + panels: { [key: string]: GroupviewPanelState }; + activeGroup?: string; + options?: { tabHeight?: number }; } export type DockviewComponentUpdateOptions = Pick< @@ -75,7 +76,7 @@ export type DockviewComponentUpdateOptions = Pick< export interface DockviewDropEvent extends GroupviewDropEvent { api: DockviewApi; - group: GroupPanel + group: GroupPanel; } export interface IDockviewComponent extends IBaseGrid { @@ -119,506 +120,548 @@ export interface IDockviewComponent extends IBaseGrid { } export class DockviewComponent - extends BaseGrid - implements IDockviewComponent + extends BaseGrid + implements IDockviewComponent { - private _deserializer: IPanelDeserializer | undefined; - private _api: DockviewApi; - private _options: DockviewComponentOptions; + private _deserializer: IPanelDeserializer | undefined; + private _api: DockviewApi; + private _options: DockviewComponentOptions; - private readonly _onTabContextMenu = new Emitter(); - readonly onTabContextMenu: Event = - this._onTabContextMenu.event; + private readonly _onTabContextMenu = new Emitter(); + readonly onTabContextMenu: Event = + this._onTabContextMenu.event; - private readonly _onDidDrop = new Emitter(); - readonly onDidDrop: Event = this._onDidDrop.event; + private readonly _onDidDrop = new Emitter(); + readonly onDidDrop: Event = this._onDidDrop.event; - private readonly _onDidRemovePanel = new Emitter(); - readonly onDidRemovePanel: Event = - this._onDidRemovePanel.event; + private readonly _onDidRemovePanel = new Emitter(); + readonly onDidRemovePanel: Event = + this._onDidRemovePanel.event; - private readonly _onDidAddPanel = new Emitter(); - readonly onDidAddPanel: Event = this._onDidAddPanel.event; + private readonly _onDidAddPanel = new Emitter(); + readonly onDidAddPanel: Event = this._onDidAddPanel.event; - private readonly _onDidLayoutfromJSON = new Emitter(); - readonly onDidLayoutFromJSON: Event = this._onDidLayoutfromJSON.event; + private readonly _onDidLayoutFromJSON = new Emitter(); + readonly onDidLayoutFromJSON: Event = this._onDidLayoutFromJSON.event; - private readonly _onDidActivePanelChange = new Emitter< - IDockviewPanel | undefined - >(); - readonly onDidActivePanelChange: Event = - this._onDidActivePanelChange.event; + private readonly _onDidActivePanelChange = new Emitter< + IDockviewPanel | undefined + >(); + readonly onDidActivePanelChange: Event = + this._onDidActivePanelChange.event; - get totalPanels(): number { - return this.panels.length; - } + get totalPanels(): number { + return this.panels.length; + } - get panels(): IDockviewPanel[] { - return this.groups.flatMap((group) => group.panels); - } + get panels(): IDockviewPanel[] { + return this.groups.flatMap((group) => group.panels); + } - get deserializer(): IPanelDeserializer | undefined { - return this._deserializer; - } + get deserializer(): IPanelDeserializer | undefined { + return this._deserializer; + } - set deserializer(value: IPanelDeserializer | undefined) { - this._deserializer = value; - } + set deserializer(value: IPanelDeserializer | undefined) { + this._deserializer = value; + } - get options(): DockviewComponentOptions { - return this._options; - } + get options(): DockviewComponentOptions { + return this._options; + } - get activePanel(): IDockviewPanel | undefined { - const activeGroup = this.activeGroup; + get activePanel(): IDockviewPanel | undefined { + const activeGroup = this.activeGroup; - if (!activeGroup) { - return undefined; - } + if (!activeGroup) { + return undefined; + } - return activeGroup.activePanel; - } + return activeGroup.activePanel; + } - set tabHeight(height: number | undefined) { - this.options.tabHeight = height; - this._groups.forEach((value) => { - value.value.model.header.height = height; - }); - } + set tabHeight(height: number | undefined) { + this.options.tabHeight = height; + this._groups.forEach((value) => { + value.value.model.header.height = height; + }); + } - get tabHeight(): number | undefined { - return this.options.tabHeight; - } + get tabHeight(): number | undefined { + return this.options.tabHeight; + } - constructor(element: HTMLElement, options: DockviewComponentOptions) { - super(element, { - proportionalLayout: true, - orientation: options.orientation || Orientation.HORIZONTAL, - styles: options.styles, - }); + constructor(element: HTMLElement, options: DockviewComponentOptions) { + super(element, { + proportionalLayout: true, + orientation: options.orientation || Orientation.HORIZONTAL, + styles: options.styles, + }); - this.addDisposables( - this._onTabContextMenu, - this._onDidDrop, - Event.any( - this.onDidAddPanel, - this.onDidRemovePanel, - this.onDidActivePanelChange - )(() => { - this._bufferOnDidLayoutChange.fire(); - }) - ); - - this._options = options; - - if (!this.options.components) { - this.options.components = {}; - } - if (!this.options.frameworkComponents) { - this.options.frameworkComponents = {}; - } - if (!this.options.frameworkTabComponents) { - this.options.frameworkTabComponents = {}; - } - if (!this.options.tabComponents) { - this.options.tabComponents = {}; - } - if ( - !this.options.watermarkComponent && - !this.options.watermarkFrameworkComponent - ) { - this.options.watermarkComponent = Watermark; - } - - this._api = new DockviewApi(this); - } - - updateOptions(options: DockviewComponentUpdateOptions): void { - const hasOrientationChanged = - typeof options.orientation === 'string' && - this.options.orientation !== options.orientation; - - this._options = { ...this.options, ...options }; - - if (hasOrientationChanged) { - this.gridview.orientation = options.orientation!; - } - - this.layout(this.gridview.width, this.gridview.height, true); - } - - focus(): void { - this.activeGroup?.focus(); - } - - getGroupPanel(id: string): IDockviewPanel | undefined { - return this.panels.find((panel) => panel.id === id); - } - - setActivePanel(panel: IDockviewPanel): void { - this.doSetGroupActive(panel.group); - panel.group.model.openPanel(panel); - } - - moveToNext(options: MovementOptions = {}): void { - if (!options.group) { - if (!this.activeGroup) { - return; - } - options.group = this.activeGroup; - } - - if (options.includePanel && options.group) { - if ( - options.group.activePanel !== - options.group.panels[options.group.panels.length - 1] - ) { - options.group.model.moveToNext({ suppressRoll: true }); - return; - } - } - - const location = getGridLocation(options.group.element); - const next = this.gridview.next(location)?.view; - this.doSetGroupActive(next); - } - - moveToPrevious(options: MovementOptions = {}): void { - if (!options.group) { - if (!this.activeGroup) { - return; - } - options.group = this.activeGroup; - } - - if (options.includePanel && options.group) { - if (options.group.activePanel !== options.group.panels[0]) { - options.group.model.moveToPrevious({ suppressRoll: true }); - return; - } - } - - const location = getGridLocation(options.group.element); - const next = this.gridview.previous(location)?.view; - if (next) { - this.doSetGroupActive(next as GroupPanel); - } - } - - /** - * Serialize the current state of the layout - * - * @returns A JSON respresentation of the layout - */ - toJSON(): SerializedDockview { - const data = this.gridview.serialize(); - - const panels = this.panels.reduce((collection, panel) => { - collection[panel.id] = panel.toJSON(); - return collection; - }, {} as { [key: string]: GroupviewPanelState }); - - return { - grid: data, - panels, - activeGroup: this.activeGroup?.id, - options: { tabHeight: this.tabHeight }, - }; - } - - fromJSON(data: SerializedDockview): void { - const groups = Array.from(this._groups.values()).map((_) => _.value); - - for (const group of groups) { - // remove the group will automatically remove the panels - this.removeGroup(group, true); - } - - this.gridview.clear(); - - if (!this.deserializer) { - throw new Error('invalid deserializer'); - } - const { grid, panels, options, activeGroup } = data; - - if (typeof options?.tabHeight === 'number') { - this.tabHeight = options.tabHeight; - } - - if (!this.deserializer) { - throw new Error('no deserializer provided'); - } - - this.gridview.deserialize( - grid, - new DefaultDeserializer(this, { - createPanel: (id, group) => { - const panelData = panels[id]; - return this.deserializer!.fromJSON(panelData, group); - }, + this.addDisposables( + this._onTabContextMenu, + this._onDidDrop, + Event.any( + this.onDidAddPanel, + this.onDidRemovePanel, + this.onDidActivePanelChange + )(() => { + this._bufferOnDidLayoutChange.fire(); }) ); - if (typeof activeGroup === 'string') { - const panel = this.getPanel(activeGroup); - if (panel) { - this.doSetGroupActive(panel); - } - } + this._options = options; - this.gridview.layout(this.width, this.height); + if (!this.options.components) { + this.options.components = {}; + } + if (!this.options.frameworkComponents) { + this.options.frameworkComponents = {}; + } + if (!this.options.frameworkTabComponents) { + this.options.frameworkTabComponents = {}; + } + if (!this.options.tabComponents) { + this.options.tabComponents = {}; + } + if ( + !this.options.watermarkComponent && + !this.options.watermarkFrameworkComponent + ) { + this.options.watermarkComponent = Watermark; + } - this._onDidLayoutfromJSON.fire(); - } + this._api = new DockviewApi(this); + } - closeAllGroups(): void { - for (const entry of this._groups.entries()) { - const [_, group] = entry; + updateOptions(options: DockviewComponentUpdateOptions): void { + const hasOrientationChanged = + typeof options.orientation === 'string' && + this.options.orientation !== options.orientation; - group.value.model.closeAllPanels(); - } - } + this._options = { ...this.options, ...options }; - fireMouseEvent(event: LayoutMouseEvent): void { - if (event.kind === MouseEventKind.CONTEXT_MENU) { - if (event.tab && event.panel) { - this._onTabContextMenu.fire({ - event: event.event, - api: this._api, - panel: event.panel, + if (hasOrientationChanged) { + this.gridview.orientation = options.orientation!; + } + + this.layout(this.gridview.width, this.gridview.height, true); + } + + focus(): void { + this.activeGroup?.focus(); + } + + getGroupPanel(id: string): IDockviewPanel | undefined { + return this.panels.find((panel) => panel.id === id); + } + + setActivePanel(panel: IDockviewPanel): void { + this.doSetGroupActive(panel.group); + panel.group.model.openPanel(panel); + } + + moveToNext(options: MovementOptions = {}): void { + if (!options.group) { + if (!this.activeGroup) { + return; + } + options.group = this.activeGroup; + } + + if (options.includePanel && options.group) { + if ( + options.group.activePanel !== + options.group.panels[ + options.group.panels.length - 1 + ] + ) { + options.group.model.moveToNext({ suppressRoll: true }); + return; + } + } + + const location = getGridLocation(options.group.element); + const next = this.gridview.next(location)?.view + this.doSetGroupActive(next); + } + + moveToPrevious(options: MovementOptions = {}): void { + if (!options.group) { + if (!this.activeGroup) { + return; + } + options.group = this.activeGroup; + } + + if (options.includePanel && options.group) { + if ( + options.group.activePanel !== + options.group.panels[0] + ) { + options.group.model.moveToPrevious({ suppressRoll: true }); + return; + } + } + + const location = getGridLocation(options.group.element); + const next = this.gridview.previous(location)?.view; + if (next) { + this.doSetGroupActive(next as GroupPanel); + } + } + + /** + * Serialize the current state of the layout + * + * @returns A JSON respresentation of the layout + */ + toJSON(): SerializedDockview { + const data = this.gridview.serialize(); + + const panels = this.panels.reduce((collection, panel) => { + collection[panel.id] = panel.toJSON(); + return collection; + }, {} as { [key: string]: GroupviewPanelState }); + + return { + grid: data, + panels, + activeGroup: this.activeGroup?.id, + options: { tabHeight: this.tabHeight }, + }; + } + + fromJSON(data: SerializedDockview): void { + const groups = Array.from(this._groups.values()).map((_) => _.value); + + const hasActiveGroup = !!this.activeGroup; + const hasActivePanel = !!this.activePanel + + for (const group of groups) { + // remove the group will automatically remove the panels + this.removeGroup(group, true); + } + + if (hasActiveGroup) { + this.doSetGroupActive(undefined); + } + + if( hasActivePanel) { + this._onDidActivePanelChange.fire(undefined); + } + + this.gridview.clear(); + + if (!this.deserializer) { + throw new Error('invalid deserializer'); + } + const { grid, panels, options, activeGroup } = data; + + if (typeof options?.tabHeight === 'number') { + this.tabHeight = options.tabHeight; + } + + if (!this.deserializer) { + throw new Error('no deserializer provided'); + } + + this.gridview.deserialize( + grid, + { + fromJSON:(node: ISerializedLeafNode) => { + const { id, locked, hideHeader, views, activeView } = node.data + + const group = this.createGroup({ + id, + locked: !!locked, + hideHeader: !!hideHeader, + }); + + this._onDidAddGroup.fire(group); + + for (const child of views) { + const panel = this.deserializer!.fromJSON(panels[child], group); + + const isActive = typeof activeView === 'string' && activeView === panel.id; + + group.model.openPanel(panel, { + skipSetPanelActive: !isActive, + skipSetGroupActive:true }); } - } - } - addPanel(options: AddPanelOptions): IDockviewPanel { - if (this.panels.find((_) => _.id === options.id)) { - throw new Error(`panel with id ${options.id} already exists`); - } - - let referenceGroup: GroupPanel | undefined; - - if (options.position?.referencePanel) { - const referencePanel = this.getGroupPanel( - options.position.referencePanel - ); - - if (!referencePanel) { - throw new Error( - `referencePanel ${options.position.referencePanel} does not exist` - ); + if (!group.activePanel && group.panels.length > 0) { + group.model.openPanel(group.panels[group.panels.length - 1],{ + skipSetGroupActive:true + }); } - referenceGroup = this.findGroup(referencePanel); - } else { - referenceGroup = this.activeGroup; + return group; + } } + ) - let panel: IDockviewPanel; + if (typeof activeGroup === 'string') { + const panel = this.getPanel(activeGroup); + if (panel) { + this.doSetGroupActive(panel); + } + } - if (referenceGroup) { - const target = toTarget(options.position?.direction || 'within'); - if (target === Position.Center) { - panel = this.createPanel(options, referenceGroup); - referenceGroup.model.openPanel(panel); - } else { - const location = getGridLocation(referenceGroup.element); - const relativeLocation = getRelativeLocation( - this.gridview.orientation, - location, - target - ); - const group = this.createGroupAtLocation(relativeLocation); - panel = this.createPanel(options, group); - group.model.openPanel(panel); - } - } else { - const group = this.createGroupAtLocation(); - panel = this.createPanel(options, group); - group.model.openPanel(panel); - } + this.gridview.layout(this.width, this.height); - return panel; - } + this._onDidLayoutFromJSON.fire(); + } - removePanel( - panel: IDockviewPanel, - options: { removeEmptyGroup: boolean; skipDispose: boolean } = { - removeEmptyGroup: true, - skipDispose: false, - } - ): void { - const group = panel.group; + closeAllGroups(): void { + for (const entry of this._groups.entries()) { + const [_, group] = entry; - if (!group) { - throw new Error( - `cannot remove panel ${panel.id}. it's missing a group.` - ); - } + group.value.model.closeAllPanels(); + } + } - group.model.removePanel(panel); + fireMouseEvent(event: LayoutMouseEvent): void { + if (event.kind === MouseEventKind.CONTEXT_MENU) { + if (event.tab && event.panel) { + this._onTabContextMenu.fire({ + event: event.event, + api: this._api, + panel: event.panel, + }); + } + } + } - panel.dispose(); + addPanel(options: AddPanelOptions): IDockviewPanel { + if (this.panels.find((_) => _.id === options.id)) { + throw new Error(`panel with id ${options.id} already exists`); + } - const retainGroupForWatermark = this.size === 1; + let referenceGroup: GroupPanel | undefined; - if ( - !retainGroupForWatermark && - group.size === 0 && - options.removeEmptyGroup - ) { - this.removeGroup(group); - } - } - - createWatermarkComponent(): IWatermarkRenderer { - return createComponent( - 'watermark-id', - 'watermark-name', - this.options.watermarkComponent - ? { 'watermark-name': this.options.watermarkComponent } - : {}, - this.options.watermarkFrameworkComponent - ? { 'watermark-name': this.options.watermarkFrameworkComponent } - : {}, - this.options.frameworkComponentFactory?.watermark + if (options.position?.referencePanel) { + const referencePanel = this.getGroupPanel( + options.position.referencePanel ); - } - addEmptyGroup(options: AddGroupOptions): void { - const group = this.createGroup(); - - if (options) { - const referencePanel = this.panels.find( - (panel) => panel.id === options.referencePanel + if (!referencePanel) { + throw new Error( + `referencePanel ${options.position.referencePanel} does not exist` ); - - if (!referencePanel) { - throw new Error( - `reference panel ${options.referencePanel} does not exist` - ); - } - - const referenceGroup = this.findGroup(referencePanel); - - if (!referenceGroup) { - throw new Error( - `reference group for reference panel ${options.referencePanel} does not exist` - ); - } - - const target = toTarget(options.direction || 'within'); - - const location = getGridLocation(referenceGroup.element); - const relativeLocation = getRelativeLocation( - this.gridview.orientation, - location, - target - ); - this.doAddGroup(group, relativeLocation); - } else { - this.doAddGroup(group); - } - } - - removeGroup(group: GroupPanel, skipActive = false): void { - const panels = [...group.panels]; // reassign since group panels will mutate - - for (const panel of panels) { - this.removePanel(panel, { - removeEmptyGroup: false, - skipDispose: false, - }); } - super.doRemoveGroup(group, { skipActive }); - } + referenceGroup = this.findGroup(referencePanel); + } else { + referenceGroup = this.activeGroup; + } - moveGroupOrPanel( - referenceGroup: GroupPanel, - groupId: string, - itemId: string, - target: Position, - index?: number - ): void { - const sourceGroup = groupId - ? this._groups.get(groupId)?.value - : undefined; + let panel: IDockviewPanel - if (!target || target === Position.Center) { - const groupItem: IDockviewPanel | undefined = - sourceGroup?.model.removePanel(itemId) || - this.panels.find((panel) => panel.id === itemId); + if (referenceGroup) { + const target = toTarget(options.position?.direction || 'within'); + if (target === Position.Center) { + panel = this.createPanel(options, referenceGroup) + referenceGroup.model.openPanel(panel); + } else { + const location = getGridLocation(referenceGroup.element); + const relativeLocation = getRelativeLocation( + this.gridview.orientation, + location, + target + ); + const group = this.createGroupAtLocation(relativeLocation); + panel = this.createPanel(options, group) + group.model.openPanel(panel); + } + } else { + const group = this.createGroupAtLocation(); + panel = this.createPanel(options, group); + group.model.openPanel(panel); + } - if (!groupItem) { - throw new Error(`No panel with id ${itemId}`); - } + return panel; + } - if (sourceGroup?.model.size === 0) { - this.doRemoveGroup(sourceGroup); - } + removePanel( + panel: IDockviewPanel, + options: { removeEmptyGroup: boolean; skipDispose: boolean } = { + removeEmptyGroup: true, + skipDispose: false, + } + ): void { + const group = panel.group; - referenceGroup.model.openPanel(groupItem, { index }); - } else { - const referenceLocation = getGridLocation(referenceGroup.element); - const targetLocation = getRelativeLocation( - this.gridview.orientation, - referenceLocation, - target - ); + if (!group) { + throw new Error( + `cannot remove panel ${panel.id}. it's missing a group.` + ); + } - if (sourceGroup && sourceGroup.size < 2) { - const [targetParentLocation, to] = tail(targetLocation); - const sourceLocation = getGridLocation(sourceGroup.element); - const [sourceParentLocation, from] = tail(sourceLocation); + group.model.removePanel(panel); - if ( - sequenceEquals(sourceParentLocation, targetParentLocation) - ) { - // special case when 'swapping' two views within same grid location - // if a group has one tab - we are essentially moving the 'group' - // which is equivalent to swapping two views in this case - this.gridview.moveView(sourceParentLocation, from, to); - } else { - // source group will become empty so delete the group - const targetGroup = this.doRemoveGroup(sourceGroup, { - skipActive: true, - skipDispose: true, - }); + panel.dispose(); - // after deleting the group we need to re-evaulate the ref location - const updatedReferenceLocation = getGridLocation( - referenceGroup.element - ); - const location = getRelativeLocation( - this.gridview.orientation, - updatedReferenceLocation, - target - ); - this.doAddGroup(targetGroup, location); - } - } else { - const groupItem: IDockviewPanel | undefined = - sourceGroup?.model.removePanel(itemId) || - this.panels.find((panel) => panel.id === itemId); + const retainGroupForWatermark = this.size === 1; - if (!groupItem) { - throw new Error(`No panel with id ${itemId}`); - } + if ( + !retainGroupForWatermark && + group.size === 0 && + options.removeEmptyGroup + ) { + this.removeGroup(group); + } + } - const dropLocation = getRelativeLocation( - this.gridview.orientation, - referenceLocation, - target - ); + createWatermarkComponent(): IWatermarkRenderer { + return createComponent( + 'watermark-id', + 'watermark-name', + this.options.watermarkComponent + ? { 'watermark-name': this.options.watermarkComponent } + : {}, + this.options.watermarkFrameworkComponent + ? { 'watermark-name': this.options.watermarkFrameworkComponent } + : {}, + this.options.frameworkComponentFactory?.watermark + ); + } - const group = this.createGroupAtLocation(dropLocation); - group.model.openPanel(groupItem); - } - } - } + addEmptyGroup(options: AddGroupOptions): void { + const group = this.createGroup(); - override doSetGroupActive( + if (options) { + const referencePanel = this.panels.find( + (panel) => panel.id === options.referencePanel + ); + + if (!referencePanel) { + throw new Error( + `reference panel ${options.referencePanel} does not exist` + ); + } + + const referenceGroup = this.findGroup(referencePanel); + + if (!referenceGroup) { + throw new Error( + `reference group for reference panel ${options.referencePanel} does not exist` + ); + } + + const target = toTarget(options.direction || 'within'); + + const location = getGridLocation(referenceGroup.element); + const relativeLocation = getRelativeLocation( + this.gridview.orientation, + location, + target + ); + this.doAddGroup(group, relativeLocation); + } else { + this.doAddGroup(group); + } + } + + removeGroup(group: GroupPanel, skipActive = false): void { + const panels = [...group.panels]; // reassign since group panels will mutate + + for (const panel of panels) { + this.removePanel(panel, { + removeEmptyGroup: false, + skipDispose: false, + }); + } + + super.doRemoveGroup(group, { skipActive }); + } + + moveGroupOrPanel( + referenceGroup: GroupPanel, + groupId: string, + itemId: string, + target: Position, + index?: number + ): void { + const sourceGroup = groupId + ? this._groups.get(groupId)?.value + : undefined; + + if (!target || target === Position.Center) { + const groupItem: IDockviewPanel | undefined = + sourceGroup?.model.removePanel(itemId) || + this.panels.find((panel) => panel.id === itemId); + + if (!groupItem) { + throw new Error(`No panel with id ${itemId}`); + } + + if (sourceGroup?.model.size === 0) { + this.doRemoveGroup(sourceGroup); + } + + referenceGroup.model.openPanel(groupItem, { index }); + } else { + const referenceLocation = getGridLocation(referenceGroup.element); + const targetLocation = getRelativeLocation( + this.gridview.orientation, + referenceLocation, + target + ); + + if (sourceGroup && sourceGroup.size < 2) { + const [targetParentLocation, to] = tail(targetLocation); + const sourceLocation = getGridLocation(sourceGroup.element); + const [sourceParentLocation, from] = tail(sourceLocation); + + if ( + sequenceEquals(sourceParentLocation, targetParentLocation) + ) { + // special case when 'swapping' two views within same grid location + // if a group has one tab - we are essentially moving the 'group' + // which is equivalent to swapping two views in this case + this.gridview.moveView(sourceParentLocation, from, to); + } else { + // source group will become empty so delete the group + const targetGroup = this.doRemoveGroup(sourceGroup, { + skipActive: true, + skipDispose: true, + }); + + // after deleting the group we need to re-evaulate the ref location + const updatedReferenceLocation = getGridLocation( + referenceGroup.element + ); + const location = getRelativeLocation( + this.gridview.orientation, + updatedReferenceLocation, + target + ); + this.doAddGroup(targetGroup, location); + } + } else { + const groupItem: IDockviewPanel | undefined = + sourceGroup?.model.removePanel(itemId) || + this.panels.find((panel) => panel.id === itemId); + + if (!groupItem) { + throw new Error(`No panel with id ${itemId}`); + } + + const dropLocation = getRelativeLocation( + this.gridview.orientation, + referenceLocation, + target + ); + + const group = this.createGroupAtLocation( dropLocation); + group.model.openPanel(groupItem); + } + } + } + + override doSetGroupActive( group: GroupPanel | undefined, skipFocus?: boolean ): void { @@ -632,29 +675,29 @@ export class DockviewComponent } } - createGroup(options?: GroupOptions): GroupPanel { - if (!options) { - options = { tabHeight: this.tabHeight }; - } - if (typeof options.tabHeight !== 'number') { - options.tabHeight = this.tabHeight; - } + createGroup(options?: GroupOptions): GroupPanel { + if (!options) { + options = { tabHeight: this.tabHeight }; + } + if (typeof options.tabHeight !== 'number') { + options.tabHeight = this.tabHeight; + } - let id = options?.id; + let id = options?.id; - if (id && this._groups.has(options.id!)) { - console.warn( - `Duplicate group id ${options?.id}. reassigning group id to avoid errors` - ); - id = undefined; - } + if (id && this._groups.has(options.id!)) { + console.warn( + `Duplicate group id ${options?.id}. reassigning group id to avoid errors` + ); + id = undefined; + } - if (!id) { - id = nextGroupId.next(); - while (this._groups.has(id)) { - id = nextGroupId.next(); - } - } + if (!id) { + id = nextGroupId.next(); + while (this._groups.has(id)) { + id = nextGroupId.next(); + } + } const view = new GroupPanel(this, id, options); view.init({ params: {}, accessor: null }); // required to initialized .part and allow for correct disposal of group @@ -687,90 +730,84 @@ export class DockviewComponent }) ); - this._groups.set(view.id, { value: view, disposable }); - } + this._groups.set(view.id, { value: view, disposable }); + } - // TODO: must be called after the above listeners have been setup, - // not an ideal pattern - view.initialize(); + // TODO: must be called after the above listeners have been setup, + // not an ideal pattern + view.initialize(); - if (typeof this.options.tabHeight === 'number') { - view.model.header.height = this.options.tabHeight; - } + if (typeof this.options.tabHeight === 'number') { + view.model.header.height = this.options.tabHeight; + } - return view; - } + return view; + } - private createPanel( - options: AddPanelOptions, - group: GroupPanel - ): IDockviewPanel { - const view = new DefaultGroupPanelView({ - content: this.createContentComponent(options.id, options.component), - tab: this.createTabComponent(options.id, options.tabComponent), - }); + private createPanel(options: AddPanelOptions, group: GroupPanel): IDockviewPanel { + const view = new DefaultGroupPanelView({ + content: this.createContentComponent(options.id, options.component), + tab: this.createTabComponent(options.id, options.tabComponent), + }); - const panel = new DockviewGroupPanel( - options.id, - this, - this._api, - group - ); - panel.init({ - view, - title: options.title || options.id, - suppressClosable: options?.suppressClosable, - params: options?.params || {}, - }); + const panel = new DockviewGroupPanel(options.id, this, this._api, group); + panel.init({ + view, + title: options.title || options.id, + suppressClosable: options?.suppressClosable, + params: options?.params || {}, + }); - return panel; - } + return panel; + } - private createContentComponent( - id: string, - componentName: string - ): IContentRenderer { - return createComponent( - id, - componentName, - this.options.components || {}, - this.options.frameworkComponents, - this.options.frameworkComponentFactory?.content - ); - } + private createContentComponent( + id: string, + componentName: string + ): IContentRenderer { + return createComponent( + id, + componentName, + this.options.components || {}, + this.options.frameworkComponents, + this.options.frameworkComponentFactory?.content + ); + } - private createTabComponent( - id: string, - componentName?: string - ): ITabRenderer { - return createComponent( - id, - componentName, - this.options.tabComponents || {}, - this.options.frameworkTabComponents, - this.options.frameworkComponentFactory?.tab, - () => new DefaultTab() - ); - } + private createTabComponent( + id: string, + componentName?: string + ): ITabRenderer { + return createComponent( + id, + componentName, + this.options.tabComponents || {}, + this.options.frameworkTabComponents, + this.options.frameworkComponentFactory?.tab, + () => new DefaultTab() + ); + } - private createGroupAtLocation(location: number[] = [0]): GroupPanel { - const group = this.createGroup(); - this.doAddGroup(group, location); - return group; - } + private createGroupAtLocation( + location: number[] = [0] + ): GroupPanel { + const group = this.createGroup(); + this.doAddGroup(group, location); + return group + } - private findGroup(panel: IDockviewPanel): GroupPanel | undefined { - return Array.from(this._groups.values()).find((group) => - group.value.model.containsPanel(panel) - )?.value; - } + private findGroup(panel: IDockviewPanel): GroupPanel | undefined { + return Array.from(this._groups.values()).find((group) => + group.value.model.containsPanel(panel) + )?.value; + } - public dispose(): void { - super.dispose(); + public dispose(): void { + super.dispose(); - this._onDidActivePanelChange.dispose(); - this._onDidAddPanel.dispose(); - this._onDidRemovePanel.dispose(); - this._onDidLayoutfromJSON.dispose(); - } + this._onDidActivePanelChange.dispose(); + this._onDidAddPanel.dispose(); + this._onDidRemovePanel.dispose(); + this._onDidLayoutFromJSON.dispose(); + } } diff --git a/packages/dockview/src/gridview/baseComponentGridview.ts b/packages/dockview/src/gridview/baseComponentGridview.ts index eb429bee4..fddc69392 100644 --- a/packages/dockview/src/gridview/baseComponentGridview.ts +++ b/packages/dockview/src/gridview/baseComponentGridview.ts @@ -82,7 +82,7 @@ export abstract class BaseGrid private readonly _onDidRemoveGroup = new Emitter(); readonly onDidRemoveGroup: Event = this._onDidRemoveGroup.event; - private readonly _onDidAddGroup = new Emitter(); + protected readonly _onDidAddGroup = new Emitter(); readonly onDidAddGroup: Event = this._onDidAddGroup.event; private readonly _onDidActiveGroupChange = new Emitter(); @@ -150,7 +150,7 @@ export abstract class BaseGrid this.addDisposables( this.gridview.onDidChange(() => { - this._onDidLayoutChange.fire(); + this._bufferOnDidLayoutChange.fire(); }) ); diff --git a/packages/dockview/src/gridview/gridviewComponent.ts b/packages/dockview/src/gridview/gridviewComponent.ts index 7738f6158..c9bbe6ba4 100644 --- a/packages/dockview/src/gridview/gridviewComponent.ts +++ b/packages/dockview/src/gridview/gridviewComponent.ts @@ -175,12 +175,18 @@ export class GridviewComponent public fromJSON(serializedGridview: SerializedGridview) { const { grid, activePanel } = serializedGridview; + const hasActiveGroup = this.activeGroup; + const groups = Array.from(this._groups.values()); // reassign since group panels will mutate for (const group of groups) { group.disposable.dispose(); this.doRemoveGroup(group.value, { skipActive: true }); } + if (hasActiveGroup) { + this.doSetGroupActive(undefined); + } + this.gridview.clear(); const queue: Function[] = []; @@ -216,6 +222,8 @@ export class GridviewComponent }) ); + this._onDidAddGroup.fire(view); + this.registerPanel(view); return view; diff --git a/packages/dockview/src/groupview/groupview.ts b/packages/dockview/src/groupview/groupview.ts index c25edcfaa..4a7e09028 100644 --- a/packages/dockview/src/groupview/groupview.ts +++ b/packages/dockview/src/groupview/groupview.ts @@ -400,7 +400,8 @@ export class Groupview extends CompositeDisposable implements IGroupview { options: { index?: number; skipFocus?: boolean; - skipSetActive?: boolean; + skipSetPanelActive?: boolean; + skipSetGroupActive?: boolean; } = {} ) { if ( @@ -410,20 +411,26 @@ export class Groupview extends CompositeDisposable implements IGroupview { options.index = this.panels.length; } - const skipSetActive = !!options.skipSetActive; + const skipSetPanelActive = !!options.skipSetPanelActive; + const skipSetGroupActive = !!options.skipSetGroupActive; // ensure the group is updated before we fire any events panel.updateParentGroup(this.parent, true); - if (!skipSetActive && this._activePanel === panel) { - this.accessor.doSetGroupActive(this.parent); + if (this._activePanel === panel) { + if (!skipSetGroupActive) { + this.accessor.doSetGroupActive(this.parent); + } return; } - this.doAddPanel(panel, options.index, skipSetActive); + this.doAddPanel(panel, options.index, skipSetPanelActive); - if (!skipSetActive) { + if (!skipSetPanelActive) { this.doSetActivePanel(panel); + } + + if (!skipSetGroupActive) { this.accessor.doSetGroupActive(this.parent, !!options.skipFocus); }