diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b7c858d1ed2..84978eab45d 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -9,8 +9,8 @@ import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/brow import * as DOM from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; -import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; +import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; @@ -48,7 +48,12 @@ export interface IListViewAccessibilityProvider { isChecked?(element: T): boolean | undefined; } -export interface IListViewOptions { +export interface IListViewOptionsUpdate { + readonly additionalScrollHeight?: number; + readonly smoothScrolling?: boolean; +} + +export interface IListViewOptions extends IListViewOptionsUpdate { readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; @@ -58,7 +63,6 @@ export interface IListViewOptions { readonly mouseSupport?: boolean; readonly horizontalScrolling?: boolean; readonly accessibilityProvider?: IListViewAccessibilityProvider; - readonly additionalScrollHeight?: number; readonly transformOptimization?: boolean; } @@ -204,7 +208,8 @@ export class ListView implements ISpliceable, IDisposable { private lastRenderHeight: number; private renderWidth = 0; private rowsContainer: HTMLElement; - private scrollableElement: ScrollableElement; + private scrollable: Scrollable; + private scrollableElement: SmoothScrollableElement; private _scrollHeight: number = 0; private scrollableElementUpdateDisposable: IDisposable | null = null; private scrollableElementWidthDelayer = new Delayer(50); @@ -285,12 +290,13 @@ export class ListView implements ISpliceable, IDisposable { this.disposables.add(Gesture.addTarget(this.rowsContainer)); - this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, { + this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => DOM.scheduleAtNextAnimationFrame(cb)); + this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) - })); + useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), + }, this.scrollable)); this.domNode.appendChild(this.scrollableElement.getDomNode()); container.appendChild(this.domNode); @@ -320,6 +326,10 @@ export class ListView implements ISpliceable, IDisposable { if (options.additionalScrollHeight !== undefined) { this.additionalScrollHeight = options.additionalScrollHeight; } + + if (options.smoothScrolling !== undefined) { + this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0); + } } triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 856b97f1a6a..ca09bbf018a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE import { Event, Emitter, EventBufferer } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list'; -import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView'; +import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; @@ -1107,10 +1107,9 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } } -export interface IListOptionsUpdate { +export interface IListOptionsUpdate extends IListViewOptionsUpdate { readonly enableKeyboardNavigation?: boolean; readonly automaticKeyboardNavigation?: boolean; - readonly additionalScrollHeight?: number; } export class List implements ISpliceable, IDisposable { @@ -1288,9 +1287,7 @@ export class List implements ISpliceable, IDisposable { this.typeLabelController.updateOptions(this._options); } - if (optionsUpdate.additionalScrollHeight !== undefined) { - this.view.updateOptions(optionsUpdate); - } + this.view.updateOptions(optionsUpdate); } get options(): IListOptions { diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index da2f2b6cee8..933af1512ed 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -533,6 +533,14 @@ export class SmoothScrollableElement extends AbstractScrollableElement { super(element, options, scrollable); } + public setScrollPosition(update: INewScrollPosition): void { + this._scrollable.setScrollPositionNow(update); + } + + public getScrollPosition(): IScrollPosition { + return this._scrollable.getCurrentScrollPosition(); + } + } export class DomScrollableElement extends ScrollableElement { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index a71e4d6ae48..f19a1cc4cad 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly simpleKeyboardNavigation?: boolean; readonly filterOnType?: boolean; readonly openOnSingleClick?: boolean; + readonly smoothScrolling?: boolean; } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -1360,7 +1361,8 @@ export abstract class AbstractTree implements IDisposable this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation, - automaticKeyboardNavigation: this._options.automaticKeyboardNavigation + automaticKeyboardNavigation: this._options.automaticKeyboardNavigation, + smoothScrolling: this._options.smoothScrolling }); if (this.typeFilterController) { diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index f81f8534749..235c3a7e78c 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -124,6 +124,7 @@ export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation'; const treeIndentKey = 'workbench.tree.indent'; const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides'; +const listSmoothScrolling = 'workbench.list.smoothScrolling'; function getHorizontalScrollingSetting(configurationService: IConfigurationService): boolean { return getMigratedSettingValue(configurationService, horizontalScrollingKey, 'workbench.tree.horizontalScrolling'); @@ -823,6 +824,7 @@ function workbenchTreeDataPreamble(treeIndentKey), renderIndentGuides: configurationService.getValue(treeRenderIndentGuidesKey), + smoothScrolling: configurationService.getValue(listSmoothScrolling), automaticKeyboardNavigation: getAutomaticKeyboardNavigation(), simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', @@ -898,25 +900,33 @@ class WorkbenchTreeInternals { this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { + let options: any = {}; if (e.affectsConfiguration(openModeSettingKey)) { - tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); + options = { ...options, openOnSingleClick: useSingleClickToOpen(configurationService) }; } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } if (e.affectsConfiguration(treeIndentKey)) { const indent = configurationService.getValue(treeIndentKey); - tree.updateOptions({ indent }); + options = { ...options, indent }; } if (e.affectsConfiguration(treeRenderIndentGuidesKey)) { const renderIndentGuides = configurationService.getValue(treeRenderIndentGuidesKey); - tree.updateOptions({ renderIndentGuides }); + options = { ...options, renderIndentGuides }; + } + if (e.affectsConfiguration(listSmoothScrolling)) { + const smoothScrolling = configurationService.getValue(listSmoothScrolling); + options = { ...options, smoothScrolling }; } if (e.affectsConfiguration(keyboardNavigationSettingKey)) { updateKeyboardNavigation(); } if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) { - tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }); + options = { ...options, automaticKeyboardNavigation: getAutomaticKeyboardNavigation() }; + } + if (Object.keys(options).length > 0) { + tree.updateOptions(options); } }), this.contextKeyService.onDidChangeContext(e => { @@ -1001,6 +1011,11 @@ configurationRegistry.registerConfiguration({ default: 'onHover', description: localize('render tree indent guides', "Controls whether the tree should render indent guides.") }, + [listSmoothScrolling]: { + type: 'boolean', + default: false, + description: localize('list smoothScrolling setting', "Controls whether lists and trees have smooth scrolling."), + }, [keyboardNavigationSettingKey]: { 'type': 'string', 'enum': ['simple', 'highlight', 'filter'],