Skip to content

Commit 8548bcb

Browse files
committed
Merge branch 'tree-horizontal-scroll'
fixes microsoft#15539
2 parents f57cf85 + e2bc0e1 commit 8548bcb

10 files changed

Lines changed: 184 additions & 39 deletions

File tree

src/vs/base/browser/dom.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,13 +459,22 @@ const sizeUtils = {
459459
getBorderLeftWidth: function (element: HTMLElement): number {
460460
return getDimension(element, 'border-left-width', 'borderLeftWidth');
461461
},
462+
getBorderRightWidth: function (element: HTMLElement): number {
463+
return getDimension(element, 'border-right-width', 'borderRightWidth');
464+
},
462465
getBorderTopWidth: function (element: HTMLElement): number {
463466
return getDimension(element, 'border-top-width', 'borderTopWidth');
464467
},
465468
getBorderBottomWidth: function (element: HTMLElement): number {
466469
return getDimension(element, 'border-bottom-width', 'borderBottomWidth');
467470
},
468471

472+
getPaddingLeft: function (element: HTMLElement): number {
473+
return getDimension(element, 'padding-left', 'paddingLeft');
474+
},
475+
getPaddingRight: function (element: HTMLElement): number {
476+
return getDimension(element, 'padding-right', 'paddingRight');
477+
},
469478
getPaddingTop: function (element: HTMLElement): number {
470479
return getDimension(element, 'padding-top', 'paddingTop');
471480
},
@@ -571,6 +580,12 @@ export function getTotalWidth(element: HTMLElement): number {
571580
return element.offsetWidth + margin;
572581
}
573582

583+
export function getContentWidth(element: HTMLElement): number {
584+
let border = sizeUtils.getBorderLeftWidth(element) + sizeUtils.getBorderRightWidth(element);
585+
let padding = sizeUtils.getPaddingLeft(element) + sizeUtils.getPaddingRight(element);
586+
return element.offsetWidth - border - padding;
587+
}
588+
574589
export function getTotalScrollWidth(element: HTMLElement): number {
575590
let margin = sizeUtils.getMarginLeft(element) + sizeUtils.getMarginRight(element);
576591
return element.scrollWidth + margin;

src/vs/base/parts/tree/browser/tree.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export interface ITree {
7878
*/
7979
refresh(element?: any, recursive?: boolean): WinJS.Promise;
8080

81+
/**
82+
* Updates an element's width.
83+
*/
84+
updateWidth(element: any): void;
85+
8186
/**
8287
* Expands an element.
8388
* The returned promise returns a boolean for whether the element was expanded or not.
@@ -674,6 +679,7 @@ export interface ITreeOptions extends ITreeStyles {
674679
showTwistie?: boolean;
675680
indentPixels?: number;
676681
verticalScrollMode?: ScrollbarVisibility;
682+
horizontalScrollMode?: ScrollbarVisibility;
677683
alwaysFocused?: boolean;
678684
autoExpandSingleChildren?: boolean;
679685
useShadows?: boolean;

src/vs/base/parts/tree/browser/treeImpl.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ export class Tree implements _.ITree {
160160
return this.model.refresh(element, recursive);
161161
}
162162

163+
public updateWidth(element: any): void {
164+
let item = this.model.getItem(element);
165+
return this.view.updateWidth(item);
166+
}
167+
163168
public expand(element: any): WinJS.Promise {
164169
return this.model.expand(element);
165170
}
@@ -214,7 +219,7 @@ export class Tree implements _.ITree {
214219
}
215220

216221
getContentHeight(): number {
217-
return this.view.getTotalHeight();
222+
return this.view.getContentHeight();
218223
}
219224

220225
public setHighlight(element?: any, eventPayload?: any): void {

src/vs/base/parts/tree/browser/treeView.ts

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Event, { Emitter } from 'vs/base/common/event';
2626
import { IDomNodePagePosition } from 'vs/base/browser/dom';
2727
import { DataTransfers } from 'vs/base/browser/dnd';
2828
import { DefaultTreestyler } from './treeDefaults';
29+
import { Delayer } from 'vs/base/common/async';
2930

3031
export interface IRow {
3132
element: HTMLElement;
@@ -101,6 +102,7 @@ export class RowCache implements Lifecycle.IDisposable {
101102

102103
export interface IViewContext extends _.ITreeContext {
103104
cache: RowCache;
105+
horizontalScrolling: boolean;
104106
}
105107

106108
export class ViewItem implements IViewItem {
@@ -113,6 +115,7 @@ export class ViewItem implements IViewItem {
113115

114116
public top: number;
115117
public height: number;
118+
public width: number = 0;
116119
public onDragStart: (e: DragEvent) => void;
117120

118121
public needsRender: boolean;
@@ -251,8 +254,32 @@ export class ViewItem implements IViewItem {
251254
}
252255

253256
if (!skipUserRender) {
257+
const style = window.getComputedStyle(this.element);
258+
const paddingLeft = parseFloat(style.paddingLeft);
259+
260+
if (this.context.horizontalScrolling) {
261+
this.element.style.width = 'fit-content';
262+
}
263+
254264
this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData);
265+
266+
if (this.context.horizontalScrolling) {
267+
this.width = DOM.getContentWidth(this.element) + paddingLeft;
268+
this.element.style.width = '';
269+
}
270+
}
271+
}
272+
273+
updateWidth(): any {
274+
if (!this.context.horizontalScrolling) {
275+
return;
255276
}
277+
278+
const style = window.getComputedStyle(this.element);
279+
const paddingLeft = parseFloat(style.paddingLeft);
280+
this.element.style.width = 'fit-content';
281+
this.width = DOM.getContentWidth(this.element) + paddingLeft;
282+
this.element.style.width = '';
256283
}
257284

258285
public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void {
@@ -386,6 +413,9 @@ export class TreeView extends HeightMap {
386413
private lastPointerType: string;
387414
private lastClickTimeStamp: number = 0;
388415

416+
private horizontalScrolling: boolean = true;
417+
private contentWidthUpdateDelayer = new Delayer<void>(50);
418+
389419
private lastRenderTop: number;
390420
private lastRenderHeight: number;
391421

@@ -422,6 +452,9 @@ export class TreeView extends HeightMap {
422452
TreeView.counter++;
423453
this.instance = TreeView.counter;
424454

455+
const horizontalScrollMode = typeof context.options.horizontalScrollMode === 'undefined' ? ScrollbarVisibility.Hidden : context.options.horizontalScrollMode;
456+
const horizontalScrolling = horizontalScrollMode !== ScrollbarVisibility.Hidden;
457+
425458
this.context = {
426459
dataSource: context.dataSource,
427460
renderer: context.renderer,
@@ -432,7 +465,8 @@ export class TreeView extends HeightMap {
432465
tree: context.tree,
433466
accessibilityProvider: context.accessibilityProvider,
434467
options: context.options,
435-
cache: new RowCache(context)
468+
cache: new RowCache(context),
469+
horizontalScrolling
436470
};
437471

438472
this.modelListeners = [];
@@ -471,12 +505,12 @@ export class TreeView extends HeightMap {
471505
this.wrapper.className = 'monaco-tree-wrapper';
472506
this.scrollableElement = new ScrollableElement(this.wrapper, {
473507
alwaysConsumeMouseWheel: true,
474-
horizontal: ScrollbarVisibility.Hidden,
508+
horizontal: horizontalScrollMode,
475509
vertical: (typeof context.options.verticalScrollMode !== 'undefined' ? context.options.verticalScrollMode : ScrollbarVisibility.Auto),
476510
useShadows: context.options.useShadows
477511
});
478512
this.scrollableElement.onScroll((e) => {
479-
this.render(e.scrollTop, e.height);
513+
this.render(e.scrollTop, e.height, e.scrollLeft, e.width, e.scrollWidth);
480514
});
481515

482516
if (Browser.isIE) {
@@ -609,9 +643,14 @@ export class TreeView extends HeightMap {
609643
}
610644

611645
this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render
646+
this.scrollHeight = this.getContentHeight();
647+
648+
if (this.horizontalScrolling) {
649+
this.viewWidth = DOM.getContentWidth(this.wrapper);
650+
}
612651
}
613652

614-
private render(scrollTop: number, viewHeight: number): void {
653+
private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void {
615654
var i: number;
616655
var stop: number;
617656

@@ -645,6 +684,11 @@ export class TreeView extends HeightMap {
645684
this.rowsContainer.style.top = (topItem.top - renderTop) + 'px';
646685
}
647686

687+
if (this.horizontalScrolling) {
688+
this.rowsContainer.style.left = -scrollLeft + 'px';
689+
this.rowsContainer.style.width = `${Math.max(scrollWidth, viewWidth)}px`;
690+
}
691+
648692
this.lastRenderTop = renderTop;
649693
this.lastRenderHeight = renderBottom - renderTop;
650694
}
@@ -685,6 +729,24 @@ export class TreeView extends HeightMap {
685729
}
686730

687731
this.scrollTop = scrollTop;
732+
this.updateScrollWidth();
733+
}
734+
735+
private updateScrollWidth(): void {
736+
if (!this.horizontalScrolling) {
737+
return;
738+
}
739+
740+
this.contentWidthUpdateDelayer.trigger(() => {
741+
const keys = Object.keys(this.items);
742+
let scrollWidth = 0;
743+
744+
for (const key of keys) {
745+
scrollWidth = Math.max(scrollWidth, this.items[key].width);
746+
}
747+
748+
this.scrollWidth = scrollWidth + 10 /* scrollbar */;
749+
});
688750
}
689751

690752
public focusNextPage(eventPayload?: any): void {
@@ -742,11 +804,25 @@ export class TreeView extends HeightMap {
742804
return scrollDimensions.height;
743805
}
744806

745-
public set viewHeight(viewHeight: number) {
746-
this.scrollableElement.setScrollDimensions({
747-
height: viewHeight,
748-
scrollHeight: this.getTotalHeight()
749-
});
807+
public set viewHeight(height: number) {
808+
this.scrollableElement.setScrollDimensions({ height });
809+
}
810+
811+
private set scrollHeight(scrollHeight: number) {
812+
this.scrollableElement.setScrollDimensions({ scrollHeight });
813+
}
814+
815+
public get viewWidth(): number {
816+
const scrollDimensions = this.scrollableElement.getScrollDimensions();
817+
return scrollDimensions.width;
818+
}
819+
820+
public set viewWidth(viewWidth: number) {
821+
this.scrollableElement.setScrollDimensions({ width: viewWidth });
822+
}
823+
824+
private set scrollWidth(scrollWidth: number) {
825+
this.scrollableElement.setScrollDimensions({ scrollWidth });
750826
}
751827

752828
public get scrollTop(): number {
@@ -756,20 +832,20 @@ export class TreeView extends HeightMap {
756832

757833
public set scrollTop(scrollTop: number) {
758834
this.scrollableElement.setScrollDimensions({
759-
scrollHeight: this.getTotalHeight()
835+
scrollHeight: this.getContentHeight()
760836
});
761837
this.scrollableElement.setScrollPosition({
762838
scrollTop: scrollTop
763839
});
764840
}
765841

766842
public getScrollPosition(): number {
767-
const height = this.getTotalHeight() - this.viewHeight;
843+
const height = this.getContentHeight() - this.viewHeight;
768844
return height <= 0 ? 1 : this.scrollTop / height;
769845
}
770846

771847
public setScrollPosition(pos: number): void {
772-
const height = this.getTotalHeight() - this.viewHeight;
848+
const height = this.getContentHeight() - this.viewHeight;
773849
this.scrollTop = height * pos;
774850
}
775851

@@ -938,6 +1014,21 @@ export class TreeView extends HeightMap {
9381014
}
9391015
}
9401016

1017+
public updateWidth(item: Model.Item): void {
1018+
if (!item || !item.isVisible()) {
1019+
return;
1020+
}
1021+
1022+
const viewItem = this.items[item.id];
1023+
1024+
if (!viewItem) {
1025+
return;
1026+
}
1027+
1028+
viewItem.updateWidth();
1029+
this.updateScrollWidth();
1030+
}
1031+
9411032
public getRelativeTop(item: Model.Item): number {
9421033
if (item && item.isVisible()) {
9431034
var viewItem = this.items[item.id];

src/vs/base/parts/tree/browser/treeViewModel.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface IViewItem {
1010
model: Item;
1111
top: number;
1212
height: number;
13+
width: number;
1314
}
1415

1516
export class HeightMap {
@@ -22,7 +23,7 @@ export class HeightMap {
2223
this.indexes = {};
2324
}
2425

25-
public getTotalHeight(): number {
26+
public getContentHeight(): number {
2627
var last = this.heightMap[this.heightMap.length - 1];
2728
return !last ? 0 : last.top + last.height;
2829
}

src/vs/base/parts/tree/test/browser/treeViewModel.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ class TestHeightMap extends HeightMap {
4545
return {
4646
model: item,
4747
top: 0,
48-
height: item.getHeight()
48+
height: item.getHeight(),
49+
width: 0
4950
};
5051
}
5152
}

0 commit comments

Comments
 (0)