Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 86 additions & 19 deletions packages/dockview-core/src/__tests__/events.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,6 @@ describe('events', () => {
expect(value).toBeUndefined();
});

it('should stop emitting after dispose', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

const stream = emitter.event((x) => {
value = x;
});

emitter.fire(0);
expect(value).toBe(0);

stream.dispose();

value = undefined;
emitter.fire(1);
expect(value).toBeUndefined();
});

it('should replay last value in replay mode', () => {
const emitter = new Emitter<number>({ replay: true });
let value: number | undefined = undefined;
Expand All @@ -76,7 +58,7 @@ describe('events', () => {
stream.dispose();
});

it('should not replay last value in replay mode', () => {
it('should not replay last value when not in replay mode', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

Expand Down Expand Up @@ -284,4 +266,89 @@ describe('events', () => {
undefined
);
});

describe('pausing and resuming events', () => {
it('should not fire events when paused', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

const stream = emitter.event((x) => {
value = x;
});

const pauseDisposable = emitter.pauseEvents();

emitter.fire(0);
expect(value).toBeUndefined();

emitter.fire(1);
expect(value).toBeUndefined();

pauseDisposable.dispose();
stream.dispose();
});

it('should fire events fired after resuming', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

const stream = emitter.event((x) => {
value = x;
});

const pauseDisposable = emitter.pauseEvents();

emitter.fire(0);
expect(value).toBeUndefined();

pauseDisposable.dispose();

emitter.fire(1);
expect(value).toBe(1);

stream.dispose();
});

it('should not replay values fired while paused when in replay mode', () => {
const emitter = new Emitter<number>({ replay: true });
let value: number | undefined = undefined;

const pauseDisposable = emitter.pauseEvents();

emitter.fire(1);

const stream = emitter.event((x) => {
value = x;
});
expect(value).toBeUndefined();

pauseDisposable.dispose();
stream.dispose();
});

it('should allow multiple pause tokens to each pause event emissions', () => {
const emitter = new Emitter<number>();
let value: number | undefined = undefined;

const stream = emitter.event((x) => {
value = x;
});

const pauseDisposable1 = emitter.pauseEvents();
const pauseDisposable2 = emitter.pauseEvents();

emitter.fire(0);
expect(value).toBeUndefined();

pauseDisposable1.dispose();
emitter.fire(1);
expect(value).toBeUndefined();

pauseDisposable2.dispose();
emitter.fire(2);
expect(value).toBe(2);

stream.dispose();
});
});
});
20 changes: 20 additions & 0 deletions packages/dockview-core/src/__tests__/gridview/gridview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,4 +1316,24 @@ describe('gridview', () => {
expect(() => gridview.normalize()).not.toThrow();
});
});

test('that serialize does not cause an onDidMaximizedNodeChange event', () => {
const gridview = new Gridview(
true,
{ separatorBorder: '' },
Orientation.HORIZONTAL
);
gridview.layout(1000, 1000);

let counter = 0;
const subscription = gridview.onDidMaximizedNodeChange(() => {
counter++;
});

gridview.serialize();

expect(counter).toBe(0);

subscription.dispose();
});
});
13 changes: 12 additions & 1 deletion packages/dockview-core/src/events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IDisposable } from './lifecycle';
import { Disposable, IDisposable } from './lifecycle';

export interface Event<T> {
(listener: (e: T) => any): IDisposable;
Expand Down Expand Up @@ -105,6 +105,8 @@ export class Emitter<T> implements IDisposable {
private _listeners: Listener<any>[] = [];
private _disposed = false;

private readonly _pauseTokens = new Set<object>();

static ENABLE_TRACKING = false;
static readonly MEMORY_LEAK_WATCHER = new LeakageMonitor();

Expand Down Expand Up @@ -160,6 +162,9 @@ export class Emitter<T> implements IDisposable {
}

public fire(e: T): void {
if (this._pauseTokens.size > 0) {
return;
}
if (this.options?.replay) {
this._last = e;
}
Expand All @@ -168,6 +173,12 @@ export class Emitter<T> implements IDisposable {
}
}

public pauseEvents(): IDisposable {
const lock = {};
this._pauseTokens.add(lock);
return Disposable.from(() => this._pauseTokens.delete(lock));
}

public dispose(): void {
if (!this._disposed) {
this._disposed = true;
Expand Down
67 changes: 39 additions & 28 deletions packages/dockview-core/src/gridview/gridview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,39 +511,50 @@ export class Gridview implements IDisposable {
maxmizedViewLocation = getGridLocation(maximizedView.element);
}

if (this.hasMaximizedView()) {
/**
* the saved layout cannot be in its maxmized state otherwise all of the underlying
* view dimensions will be wrong
*
* To counteract this we temporaily remove the maximized view to compute the serialized output
* of the grid before adding back the maxmized view as to not alter the layout from the users
* perspective when `.toJSON()` is called
*/
this.exitMaximizedView();
}
/**
* We pause the onDidMaximizedNodeChange events because this method needs to
* call `this.exitMaximizedView()`. We don't want this to invoke any listeners
* since we undo it before leaving this method
*/
const pauseToken = this._onDidMaximizedNodeChange.pauseEvents();

const root = serializeBranchNode(this.getView(), this.orientation);
try {
if (this.hasMaximizedView()) {
/**
* the saved layout cannot be in its maxmized state otherwise all of the underlying
* view dimensions will be wrong
*
* To counteract this we temporaily remove the maximized view to compute the serialized output
* of the grid before adding back the maxmized view as to not alter the layout from the users
* perspective when `.toJSON()` is called
*/
this.exitMaximizedView();
}

const resullt: SerializedGridview<any> = {
root,
width: this.width,
height: this.height,
orientation: this.orientation,
};

if (maxmizedViewLocation) {
resullt.maximizedNode = {
location: maxmizedViewLocation,
const root = serializeBranchNode(this.getView(), this.orientation);

const result: SerializedGridview<any> = {
root,
width: this.width,
height: this.height,
orientation: this.orientation,
};
}

if (maximizedView) {
// replace any maximzied view that was removed for serialization purposes
this.maximizeView(maximizedView);
}
if (maxmizedViewLocation) {
result.maximizedNode = {
location: maxmizedViewLocation,
};
}

if (maximizedView) {
// replace any maximzied view that was removed for serialization purposes
this.maximizeView(maximizedView);
}

return resullt;
return result;
} finally {
pauseToken.dispose();
}
}

public dispose(): void {
Expand Down
Loading