Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/browser/ui/splitview/splitview.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { Sash, SashState } from '../../../../browser/ui/sash/sash.js';
8
import { IView, LayoutPriority, Sizing, SplitView } from '../../../../browser/ui/splitview/splitview.js';
9
import { Emitter } from '../../../../common/event.js';
10
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';
11
12
class TestView implements IView<number> {
13
14
private readonly _onDidChange = new Emitter<number | undefined>();
15
readonly onDidChange = this._onDidChange.event;
16
17
get minimumSize(): number { return this._minimumSize; }
18
set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(undefined); }
19
20
get maximumSize(): number { return this._maximumSize; }
21
set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(undefined); }
22
23
private _element: HTMLElement = document.createElement('div');
24
get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; }
25
26
private readonly _onDidGetElement = new Emitter<void>();
27
readonly onDidGetElement = this._onDidGetElement.event;
28
29
private _size = 0;
30
get size(): number { return this._size; }
31
private _orthogonalSize: number | undefined = 0;
32
get orthogonalSize(): number | undefined { return this._orthogonalSize; }
33
private readonly _onDidLayout = new Emitter<{ size: number; orthogonalSize: number | undefined }>();
34
readonly onDidLayout = this._onDidLayout.event;
35
36
private readonly _onDidFocus = new Emitter<void>();
37
readonly onDidFocus = this._onDidFocus.event;
38
39
constructor(
40
private _minimumSize: number,
41
private _maximumSize: number,
42
readonly priority: LayoutPriority = LayoutPriority.Normal
43
) {
44
assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size');
45
}
46
47
layout(size: number, _offset: number, orthogonalSize: number | undefined): void {
48
this._size = size;
49
this._orthogonalSize = orthogonalSize;
50
this._onDidLayout.fire({ size, orthogonalSize });
51
}
52
53
focus(): void {
54
this._onDidFocus.fire();
55
}
56
57
dispose(): void {
58
this._onDidChange.dispose();
59
this._onDidGetElement.dispose();
60
this._onDidLayout.dispose();
61
this._onDidFocus.dispose();
62
}
63
}
64
65
function getSashes(splitview: SplitView): Sash[] {
66
return splitview.sashItems.map((i: any) => i.sash) as Sash[];
67
}
68
69
suite('Splitview', () => {
70
71
const store = ensureNoDisposablesAreLeakedInTestSuite();
72
73
let container: HTMLElement;
74
75
setup(() => {
76
container = document.createElement('div');
77
container.style.position = 'absolute';
78
container.style.width = `${200}px`;
79
container.style.height = `${200}px`;
80
});
81
82
test('empty splitview has empty DOM', () => {
83
store.add(new SplitView(container));
84
assert.strictEqual(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty');
85
});
86
87
test('has views and sashes as children', () => {
88
const view1 = store.add(new TestView(20, 20));
89
const view2 = store.add(new TestView(20, 20));
90
const view3 = store.add(new TestView(20, 20));
91
const splitview = store.add(new SplitView(container));
92
93
splitview.addView(view1, 20);
94
splitview.addView(view2, 20);
95
splitview.addView(view3, 20);
96
97
let viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');
98
assert.strictEqual(viewQuery.length, 3, 'split view should have 3 views');
99
100
let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
101
assert.strictEqual(sashQuery.length, 2, 'split view should have 2 sashes');
102
103
splitview.removeView(2);
104
105
viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');
106
assert.strictEqual(viewQuery.length, 2, 'split view should have 2 views');
107
108
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
109
assert.strictEqual(sashQuery.length, 1, 'split view should have 1 sash');
110
111
splitview.removeView(0);
112
113
viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');
114
assert.strictEqual(viewQuery.length, 1, 'split view should have 1 view');
115
116
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
117
assert.strictEqual(sashQuery.length, 0, 'split view should have no sashes');
118
119
splitview.removeView(0);
120
121
viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');
122
assert.strictEqual(viewQuery.length, 0, 'split view should have no views');
123
124
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
125
assert.strictEqual(sashQuery.length, 0, 'split view should have no sashes');
126
});
127
128
test('calls view methods on addView and removeView', () => {
129
const view = store.add(new TestView(20, 20));
130
const splitview = store.add(new SplitView(container));
131
132
let didLayout = false;
133
store.add(view.onDidLayout(() => didLayout = true));
134
store.add(view.onDidGetElement(() => undefined));
135
136
splitview.addView(view, 20);
137
138
assert.strictEqual(view.size, 20, 'view has right size');
139
assert(didLayout, 'layout is called');
140
assert(didLayout, 'render is called');
141
});
142
143
test('stretches view to viewport', () => {
144
const view = store.add(new TestView(20, Number.POSITIVE_INFINITY));
145
const splitview = store.add(new SplitView(container));
146
splitview.layout(200);
147
148
splitview.addView(view, 20);
149
assert.strictEqual(view.size, 200, 'view is stretched');
150
151
splitview.layout(200);
152
assert.strictEqual(view.size, 200, 'view stayed the same');
153
154
splitview.layout(100);
155
assert.strictEqual(view.size, 100, 'view is collapsed');
156
157
splitview.layout(20);
158
assert.strictEqual(view.size, 20, 'view is collapsed');
159
160
splitview.layout(10);
161
assert.strictEqual(view.size, 20, 'view is clamped');
162
163
splitview.layout(200);
164
assert.strictEqual(view.size, 200, 'view is stretched');
165
});
166
167
test('can resize views', () => {
168
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
169
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
170
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
171
const splitview = store.add(new SplitView(container));
172
splitview.layout(200);
173
174
splitview.addView(view1, 20);
175
splitview.addView(view2, 20);
176
splitview.addView(view3, 20);
177
178
assert.strictEqual(view1.size, 160, 'view1 is stretched');
179
assert.strictEqual(view2.size, 20, 'view2 size is 20');
180
assert.strictEqual(view3.size, 20, 'view3 size is 20');
181
182
splitview.resizeView(1, 40);
183
184
assert.strictEqual(view1.size, 140, 'view1 is collapsed');
185
assert.strictEqual(view2.size, 40, 'view2 is stretched');
186
assert.strictEqual(view3.size, 20, 'view3 stays the same');
187
188
splitview.resizeView(0, 70);
189
190
assert.strictEqual(view1.size, 70, 'view1 is collapsed');
191
assert.strictEqual(view2.size, 40, 'view2 stays the same');
192
assert.strictEqual(view3.size, 90, 'view3 is stretched');
193
194
splitview.resizeView(2, 40);
195
196
assert.strictEqual(view1.size, 70, 'view1 stays the same');
197
assert.strictEqual(view2.size, 90, 'view2 is collapsed');
198
assert.strictEqual(view3.size, 40, 'view3 is stretched');
199
});
200
201
test('reacts to view changes', () => {
202
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
203
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
204
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
205
const splitview = store.add(new SplitView(container));
206
splitview.layout(200);
207
208
splitview.addView(view1, 20);
209
splitview.addView(view2, 20);
210
splitview.addView(view3, 20);
211
212
assert.strictEqual(view1.size, 160, 'view1 is stretched');
213
assert.strictEqual(view2.size, 20, 'view2 size is 20');
214
assert.strictEqual(view3.size, 20, 'view3 size is 20');
215
216
view1.maximumSize = 20;
217
218
assert.strictEqual(view1.size, 20, 'view1 is collapsed');
219
assert.strictEqual(view2.size, 20, 'view2 stays the same');
220
assert.strictEqual(view3.size, 160, 'view3 is stretched');
221
222
view3.maximumSize = 40;
223
224
assert.strictEqual(view1.size, 20, 'view1 stays the same');
225
assert.strictEqual(view2.size, 140, 'view2 is stretched');
226
assert.strictEqual(view3.size, 40, 'view3 is collapsed');
227
228
view2.maximumSize = 200;
229
230
assert.strictEqual(view1.size, 20, 'view1 stays the same');
231
assert.strictEqual(view2.size, 140, 'view2 stays the same');
232
assert.strictEqual(view3.size, 40, 'view3 stays the same');
233
234
view3.maximumSize = Number.POSITIVE_INFINITY;
235
view3.minimumSize = 100;
236
237
assert.strictEqual(view1.size, 20, 'view1 is collapsed');
238
assert.strictEqual(view2.size, 80, 'view2 is collapsed');
239
assert.strictEqual(view3.size, 100, 'view3 is stretched');
240
});
241
242
test('sashes are properly enabled/disabled', () => {
243
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
244
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
245
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
246
const splitview = store.add(new SplitView(container));
247
splitview.layout(200);
248
249
splitview.addView(view1, Sizing.Distribute);
250
splitview.addView(view2, Sizing.Distribute);
251
splitview.addView(view3, Sizing.Distribute);
252
253
const sashes = getSashes(splitview);
254
assert.strictEqual(sashes.length, 2, 'there are two sashes');
255
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
256
assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');
257
258
splitview.layout(60);
259
assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');
260
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
261
262
splitview.layout(20);
263
assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');
264
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
265
266
splitview.layout(200);
267
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
268
assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');
269
270
view1.maximumSize = 20;
271
assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');
272
assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');
273
274
view2.maximumSize = 20;
275
assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');
276
assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');
277
278
view1.maximumSize = 300;
279
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
280
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
281
282
view2.maximumSize = 200;
283
assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');
284
assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');
285
286
splitview.resizeView(0, 40);
287
assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');
288
assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');
289
});
290
291
test('issue #35497', () => {
292
const view1 = store.add(new TestView(160, Number.POSITIVE_INFINITY));
293
const view2 = store.add(new TestView(66, 66));
294
295
const splitview = store.add(new SplitView(container));
296
splitview.layout(986);
297
298
splitview.addView(view1, 142, 0);
299
assert.strictEqual(view1.size, 986, 'first view is stretched');
300
301
store.add(view2.onDidGetElement(() => {
302
assert.throws(() => splitview.resizeView(1, 922));
303
assert.throws(() => splitview.resizeView(1, 922));
304
}));
305
306
splitview.addView(view2, 66, 0);
307
assert.strictEqual(view2.size, 66, 'second view is fixed');
308
assert.strictEqual(view1.size, 986 - 66, 'first view is collapsed');
309
310
const viewContainers = container.querySelectorAll('.split-view-view');
311
assert.strictEqual(viewContainers.length, 2, 'there are two view containers');
312
assert.strictEqual((viewContainers.item(0) as HTMLElement).style.height, '66px', 'second view container is 66px');
313
assert.strictEqual<string>((viewContainers.item(1) as HTMLElement).style.height, `${986 - 66}px`, 'first view container is 66px');
314
});
315
316
test('automatic size distribution', () => {
317
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
318
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
319
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
320
const splitview = store.add(new SplitView(container));
321
splitview.layout(200);
322
323
splitview.addView(view1, Sizing.Distribute);
324
assert.strictEqual(view1.size, 200);
325
326
splitview.addView(view2, 50);
327
assert.deepStrictEqual([view1.size, view2.size], [150, 50]);
328
329
splitview.addView(view3, Sizing.Distribute);
330
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 66, 68]);
331
332
splitview.removeView(1, Sizing.Distribute);
333
assert.deepStrictEqual([view1.size, view3.size], [100, 100]);
334
});
335
336
test('add views before layout', () => {
337
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
338
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
339
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
340
const splitview = store.add(new SplitView(container));
341
342
splitview.addView(view1, 100);
343
splitview.addView(view2, 75);
344
splitview.addView(view3, 25);
345
346
splitview.layout(200);
347
assert.deepStrictEqual([view1.size, view2.size, view3.size], [67, 67, 66]);
348
});
349
350
test('split sizing', () => {
351
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
352
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
353
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
354
const splitview = store.add(new SplitView(container));
355
splitview.layout(200);
356
357
splitview.addView(view1, Sizing.Distribute);
358
assert.strictEqual(view1.size, 200);
359
360
splitview.addView(view2, Sizing.Split(0));
361
assert.deepStrictEqual([view1.size, view2.size], [100, 100]);
362
363
splitview.addView(view3, Sizing.Split(1));
364
assert.deepStrictEqual([view1.size, view2.size, view3.size], [100, 50, 50]);
365
});
366
367
test('split sizing 2', () => {
368
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
369
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
370
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
371
const splitview = store.add(new SplitView(container));
372
splitview.layout(200);
373
374
splitview.addView(view1, Sizing.Distribute);
375
assert.strictEqual(view1.size, 200);
376
377
splitview.addView(view2, Sizing.Split(0));
378
assert.deepStrictEqual([view1.size, view2.size], [100, 100]);
379
380
splitview.addView(view3, Sizing.Split(0));
381
assert.deepStrictEqual([view1.size, view2.size, view3.size], [50, 100, 50]);
382
});
383
384
test('proportional layout', () => {
385
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
386
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
387
const splitview = store.add(new SplitView(container));
388
splitview.layout(200);
389
390
splitview.addView(view1, Sizing.Distribute);
391
splitview.addView(view2, Sizing.Distribute);
392
assert.deepStrictEqual([view1.size, view2.size], [100, 100]);
393
394
splitview.layout(100);
395
assert.deepStrictEqual([view1.size, view2.size], [50, 50]);
396
});
397
398
test('disable proportional layout', () => {
399
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
400
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
401
const splitview = store.add(new SplitView(container, { proportionalLayout: false }));
402
splitview.layout(200);
403
404
splitview.addView(view1, Sizing.Distribute);
405
splitview.addView(view2, Sizing.Distribute);
406
assert.deepStrictEqual([view1.size, view2.size], [100, 100]);
407
408
splitview.layout(100);
409
assert.deepStrictEqual([view1.size, view2.size], [80, 20]);
410
});
411
412
test('high layout priority', () => {
413
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
414
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.High));
415
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
416
const splitview = store.add(new SplitView(container, { proportionalLayout: false }));
417
splitview.layout(200);
418
419
splitview.addView(view1, Sizing.Distribute);
420
splitview.addView(view2, Sizing.Distribute);
421
splitview.addView(view3, Sizing.Distribute);
422
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
423
424
splitview.layout(180);
425
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
426
427
splitview.layout(124);
428
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 20, 38]);
429
430
splitview.layout(60);
431
assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 20, 20]);
432
433
splitview.layout(200);
434
assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 160, 20]);
435
});
436
437
test('low layout priority', () => {
438
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
439
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
440
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low));
441
const splitview = store.add(new SplitView(container, { proportionalLayout: false }));
442
splitview.layout(200);
443
444
splitview.addView(view1, Sizing.Distribute);
445
splitview.addView(view2, Sizing.Distribute);
446
splitview.addView(view3, Sizing.Distribute);
447
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
448
449
splitview.layout(180);
450
assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
451
452
splitview.layout(132);
453
assert.deepStrictEqual([view1.size, view2.size, view3.size], [46, 20, 66]);
454
455
splitview.layout(60);
456
assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 20, 20]);
457
458
splitview.layout(200);
459
assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 160, 20]);
460
});
461
462
test('context propagates to views', () => {
463
const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
464
const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));
465
const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low));
466
const splitview = store.add(new SplitView<number>(container, { proportionalLayout: false }));
467
splitview.layout(200);
468
469
splitview.addView(view1, Sizing.Distribute);
470
splitview.addView(view2, Sizing.Distribute);
471
splitview.addView(view3, Sizing.Distribute);
472
473
splitview.layout(200, 100);
474
assert.deepStrictEqual([view1.orthogonalSize, view2.orthogonalSize, view3.orthogonalSize], [100, 100, 100]);
475
});
476
});
477
478