From 86e5a0b4c370027b1919e2986b6cfec02d7204c5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 12 Mar 2018 12:30:20 +0100 Subject: [PATCH] wip: tree user-independent horizontal scrolling --- src/vs/base/parts/tree/browser/tree.ts | 2 +- src/vs/base/parts/tree/browser/treeView.ts | 41 +++++++++++++++---- .../base/parts/tree/browser/treeViewModel.ts | 1 + .../tree/test/browser/treeViewModel.test.ts | 3 +- src/vs/platform/list/browser/listService.ts | 3 +- .../electron-browser/views/explorerView.ts | 29 ++----------- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index e9b6eb2fb50..f1b6f8a60ad 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -677,7 +677,7 @@ export interface ITreeOptions extends ITreeStyles { showTwistie?: boolean; indentPixels?: number; verticalScrollMode?: ScrollbarVisibility; - contentWidthProvider?: IContentWidthProvider; + horizontalScrollMode?: ScrollbarVisibility; alwaysFocused?: boolean; autoExpandSingleChildren?: boolean; useShadows?: boolean; diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index bd750717af2..23436abafbf 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -101,6 +101,7 @@ export class RowCache implements Lifecycle.IDisposable { export interface IViewContext extends _.ITreeContext { cache: RowCache; + horizontalScrolling: boolean; } export class ViewItem implements IViewItem { @@ -113,6 +114,7 @@ export class ViewItem implements IViewItem { public top: number; public height: number; + public width: number = 0; public onDragStart: (e: DragEvent) => void; public needsRender: boolean; @@ -251,7 +253,19 @@ export class ViewItem implements IViewItem { } if (!skipUserRender) { + const style = window.getComputedStyle(this.element); + const paddingLeft = parseFloat(style.paddingLeft); + + if (this.context.horizontalScrolling) { + this.element.style.width = 'fit-content'; + } + this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + + if (this.context.horizontalScrolling) { + this.width = DOM.getContentWidth(this.element) + paddingLeft; + this.element.style.width = ''; + } } } @@ -384,7 +398,8 @@ export class TreeView extends HeightMap { private msGesture: MSGesture; private lastPointerType: string; private lastClickTimeStamp: number = 0; - private contentWidthProvider?: _.IContentWidthProvider; + + private horizontalScrolling: boolean = true; private contentWidthUpdateDelayer = new Delayer(50); private lastRenderTop: number; @@ -423,6 +438,9 @@ export class TreeView extends HeightMap { TreeView.counter++; this.instance = TreeView.counter; + const horizontalScrollMode = typeof context.options.horizontalScrollMode === 'undefined' ? ScrollbarVisibility.Hidden : context.options.horizontalScrollMode; + const horizontalScrolling = horizontalScrollMode !== ScrollbarVisibility.Hidden; + this.context = { dataSource: context.dataSource, renderer: context.renderer, @@ -433,7 +451,8 @@ export class TreeView extends HeightMap { tree: context.tree, accessibilityProvider: context.accessibilityProvider, options: context.options, - cache: new RowCache(context) + cache: new RowCache(context), + horizontalScrolling }; this.modelListeners = []; @@ -463,9 +482,6 @@ export class TreeView extends HeightMap { DOM.addClass(this.domNode, 'no-row-padding'); } - const horizontalScrollMode = typeof context.options.contentWidthProvider === 'undefined' ? ScrollbarVisibility.Hidden : ScrollbarVisibility.Auto; - this.contentWidthProvider = context.options.contentWidthProvider; - this.wrapper = document.createElement('div'); this.wrapper.className = 'monaco-tree-wrapper'; this.scrollableElement = new ScrollableElement(this.wrapper, { @@ -678,7 +694,7 @@ export class TreeView extends HeightMap { this.viewHeight = height || DOM.getContentHeight(this.wrapper); // render this.scrollHeight = this.getContentHeight(); - if (this.contentWidthProvider) { + if (this.horizontalScrolling) { this.viewWidth = DOM.getContentWidth(this.wrapper); } } @@ -717,7 +733,7 @@ export class TreeView extends HeightMap { this.rowsContainer.style.top = (topItem.top - renderTop) + 'px'; } - if (this.contentWidthProvider) { + if (this.horizontalScrolling) { this.rowsContainer.style.left = -scrollLeft + 'px'; this.rowsContainer.style.width = `${Math.max(scrollWidth, viewWidth)}px`; } @@ -763,9 +779,16 @@ export class TreeView extends HeightMap { this.scrollTop = scrollTop; - if (this.contentWidthProvider) { + if (this.horizontalScrolling) { this.contentWidthUpdateDelayer.trigger(() => { - this.scrollWidth = this.contentWidthProvider.getContentWidth(); + const keys = Object.keys(this.items); + let scrollWidth = 0; + + for (const key of keys) { + scrollWidth = Math.max(scrollWidth, this.items[key].width); + } + + this.scrollWidth = scrollWidth + 10 /* scrollbar */; }); } } diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts index 016d3ce2093..0238c16102f 100644 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ b/src/vs/base/parts/tree/browser/treeViewModel.ts @@ -10,6 +10,7 @@ export interface IViewItem { model: Item; top: number; height: number; + width: number; } export class HeightMap { diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts index 7906b72e993..9bc0a347dcf 100644 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts @@ -45,7 +45,8 @@ class TestHeightMap extends HeightMap { return { model: item, top: 0, - height: item.getHeight() + height: item.getHeight(), + width: 0 }; } } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 955b1b34b56..eb3980531c4 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -24,6 +24,7 @@ import { DefaultController, IControllerOptions, OpenMode, ClickBehavior } from ' import { isUndefinedOrNull } from 'vs/base/common/types'; import { IEditorOptions } from 'vs/platform/editor/common/editor'; import Event, { Emitter } from 'vs/base/common/event'; +import { ScrollbarVisibility } from '../../../base/common/scrollable'; export type ListWidget = List | PagedList | ITree; @@ -299,7 +300,7 @@ export class WorkbenchTree extends Tree { @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService ) { - super(container, handleTreeController(configuration, instantiationService), mixin(options, { keyboardSupport: false } as ITreeOptions, false)); + super(container, handleTreeController(configuration, instantiationService), { horizontalScrollMode: ScrollbarVisibility.Auto, keyboardSupport: false, ...options }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index bd2ceea814c..acac6760d6a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -416,8 +416,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView accessibilityProvider }, { autoExpandSingleChildren: true, - ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), - contentWidthProvider: { getContentWidth: () => this.getOptimalWidth() } + ariaLabel: nls.localize('treeAriaLabel', "Files Explorer") }); // Bind context keys @@ -460,32 +459,10 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } public getOptimalWidth(): number { - if (!this.explorerViewer) { - return 0; - } - - let result = 0; const parentNode = this.explorerViewer.getHTMLElement(); - const rows = [].slice.call(parentNode.querySelectorAll('.monaco-tree-row')) as HTMLElement[]; - - for (const row of rows) { - const rowStyle = window.getComputedStyle(row); - const content = row.querySelector('.content') as HTMLElement; - const twistieStyle = window.getComputedStyle(content, ':before'); - const iconLabel = content.querySelector('.monaco-icon-label') as HTMLElement; - const iconStyle = window.getComputedStyle(iconLabel, ':before'); // width + padding - const decorationsStyle = window.getComputedStyle(iconLabel, ':after'); // width + padding - const label = iconLabel.querySelector('.monaco-icon-label-description-container > .label-name') as HTMLElement; - - const twistieWidth = (twistieStyle.display !== 'none' && twistieStyle.content) ? parseFloat(twistieStyle.width) + parseFloat(twistieStyle.paddingLeft) + parseFloat(twistieStyle.paddingRight) : 0; - const iconWidth = iconStyle.content ? parseFloat(iconStyle.width) + parseFloat(iconStyle.paddingLeft) + parseFloat(iconStyle.paddingRight) : 0; - const decorationsWidth = decorationsStyle.content ? parseFloat(decorationsStyle.width) + parseFloat(decorationsStyle.paddingLeft) + parseFloat(decorationsStyle.paddingRight) : 0; - const width = parseFloat(rowStyle.paddingLeft) + twistieWidth + iconWidth + decorationsWidth + label.offsetWidth; - - result = Math.max(result, width); - } + const childNodes = [].slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels - return result + 12; // 12 for right padding + return DOM.getLargestChildWidth(parentNode, childNodes); } private onFileOperation(e: FileOperationEvent): void { -- GitLab