Skip to content

Commit da8f44c

Browse files
committed
add list smooth scrolling
fixes microsoft#98175
1 parent 42ae88f commit da8f44c

5 files changed

Lines changed: 51 additions & 19 deletions

File tree

src/vs/base/browser/ui/list/listView.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/brow
99
import * as DOM from 'vs/base/browser/dom';
1010
import { Event, Emitter } from 'vs/base/common/event';
1111
import { domEvent } from 'vs/base/browser/event';
12-
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
13-
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable';
12+
import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
13+
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions, Scrollable } from 'vs/base/common/scrollable';
1414
import { RangeMap, shift } from './rangeMap';
1515
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list';
1616
import { RowCache, IRow } from './rowCache';
@@ -48,7 +48,12 @@ export interface IListViewAccessibilityProvider<T> {
4848
isChecked?(element: T): boolean | undefined;
4949
}
5050

51-
export interface IListViewOptions<T> {
51+
export interface IListViewOptionsUpdate {
52+
readonly additionalScrollHeight?: number;
53+
readonly smoothScrolling?: boolean;
54+
}
55+
56+
export interface IListViewOptions<T> extends IListViewOptionsUpdate {
5257
readonly dnd?: IListViewDragAndDrop<T>;
5358
readonly useShadows?: boolean;
5459
readonly verticalScrollMode?: ScrollbarVisibility;
@@ -58,7 +63,6 @@ export interface IListViewOptions<T> {
5863
readonly mouseSupport?: boolean;
5964
readonly horizontalScrolling?: boolean;
6065
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
61-
readonly additionalScrollHeight?: number;
6266
readonly transformOptimization?: boolean;
6367
}
6468

@@ -204,7 +208,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
204208
private lastRenderHeight: number;
205209
private renderWidth = 0;
206210
private rowsContainer: HTMLElement;
207-
private scrollableElement: ScrollableElement;
211+
private scrollable: Scrollable;
212+
private scrollableElement: SmoothScrollableElement;
208213
private _scrollHeight: number = 0;
209214
private scrollableElementUpdateDisposable: IDisposable | null = null;
210215
private scrollableElementWidthDelayer = new Delayer<void>(50);
@@ -285,12 +290,13 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
285290

286291
this.disposables.add(Gesture.addTarget(this.rowsContainer));
287292

288-
this.scrollableElement = this.disposables.add(new ScrollableElement(this.rowsContainer, {
293+
this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => DOM.scheduleAtNextAnimationFrame(cb));
294+
this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, {
289295
alwaysConsumeMouseWheel: true,
290296
horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden,
291297
vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode),
292-
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows)
293-
}));
298+
useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows),
299+
}, this.scrollable));
294300

295301
this.domNode.appendChild(this.scrollableElement.getDomNode());
296302
container.appendChild(this.domNode);
@@ -320,6 +326,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
320326
if (options.additionalScrollHeight !== undefined) {
321327
this.additionalScrollHeight = options.additionalScrollHeight;
322328
}
329+
330+
if (options.smoothScrolling !== undefined) {
331+
this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0);
332+
}
323333
}
324334

325335
triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {

src/vs/base/browser/ui/list/listWidget.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE
1616
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
1717
import { domEvent } from 'vs/base/browser/event';
1818
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction, ListError, IKeyboardNavigationDelegate } from './list';
19-
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider } from './listView';
19+
import { ListView, IListViewOptions, IListViewDragAndDrop, IListViewAccessibilityProvider, IListViewOptionsUpdate } from './listView';
2020
import { Color } from 'vs/base/common/color';
2121
import { mixin } from 'vs/base/common/objects';
2222
import { ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable';
@@ -1107,10 +1107,9 @@ class ListViewDragAndDrop<T> implements IListViewDragAndDrop<T> {
11071107
}
11081108
}
11091109

1110-
export interface IListOptionsUpdate {
1110+
export interface IListOptionsUpdate extends IListViewOptionsUpdate {
11111111
readonly enableKeyboardNavigation?: boolean;
11121112
readonly automaticKeyboardNavigation?: boolean;
1113-
readonly additionalScrollHeight?: number;
11141113
}
11151114

11161115
export class List<T> implements ISpliceable<T>, IDisposable {
@@ -1288,9 +1287,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
12881287
this.typeLabelController.updateOptions(this._options);
12891288
}
12901289

1291-
if (optionsUpdate.additionalScrollHeight !== undefined) {
1292-
this.view.updateOptions(optionsUpdate);
1293-
}
1290+
this.view.updateOptions(optionsUpdate);
12941291
}
12951292

12961293
get options(): IListOptions<T> {

src/vs/base/browser/ui/scrollbar/scrollableElement.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,14 @@ export class SmoothScrollableElement extends AbstractScrollableElement {
533533
super(element, options, scrollable);
534534
}
535535

536+
public setScrollPosition(update: INewScrollPosition): void {
537+
this._scrollable.setScrollPositionNow(update);
538+
}
539+
540+
public getScrollPosition(): IScrollPosition {
541+
return this._scrollable.getCurrentScrollPosition();
542+
}
543+
536544
}
537545

538546
export class DomScrollableElement extends ScrollableElement {

src/vs/base/browser/ui/tree/abstractTree.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
962962
readonly simpleKeyboardNavigation?: boolean;
963963
readonly filterOnType?: boolean;
964964
readonly openOnSingleClick?: boolean;
965+
readonly smoothScrolling?: boolean;
965966
}
966967

967968
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
@@ -1360,7 +1361,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
13601361

13611362
this.view.updateOptions({
13621363
enableKeyboardNavigation: this._options.simpleKeyboardNavigation,
1363-
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation
1364+
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation,
1365+
smoothScrolling: this._options.smoothScrolling
13641366
});
13651367

13661368
if (this.typeFilterController) {

src/vs/platform/list/browser/listService.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation';
124124
export const automaticKeyboardNavigationSettingKey = 'workbench.list.automaticKeyboardNavigation';
125125
const treeIndentKey = 'workbench.tree.indent';
126126
const treeRenderIndentGuidesKey = 'workbench.tree.renderIndentGuides';
127+
const listSmoothScrolling = 'workbench.list.smoothScrolling';
127128

128129
function getHorizontalScrollingSetting(configurationService: IConfigurationService): boolean {
129130
return getMigratedSettingValue<boolean>(configurationService, horizontalScrollingKey, 'workbench.tree.horizontalScrolling');
@@ -823,6 +824,7 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
823824
...workbenchListOptions,
824825
indent: configurationService.getValue<number>(treeIndentKey),
825826
renderIndentGuides: configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey),
827+
smoothScrolling: configurationService.getValue<boolean>(listSmoothScrolling),
826828
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
827829
simpleKeyboardNavigation: keyboardNavigation === 'simple',
828830
filterOnType: keyboardNavigation === 'filter',
@@ -898,25 +900,33 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
898900
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
899901
}),
900902
configurationService.onDidChangeConfiguration(e => {
903+
let options: any = {};
901904
if (e.affectsConfiguration(openModeSettingKey)) {
902-
tree.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) });
905+
options = { ...options, openOnSingleClick: useSingleClickToOpen(configurationService) };
903906
}
904907
if (e.affectsConfiguration(multiSelectModifierSettingKey)) {
905908
this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService);
906909
}
907910
if (e.affectsConfiguration(treeIndentKey)) {
908911
const indent = configurationService.getValue<number>(treeIndentKey);
909-
tree.updateOptions({ indent });
912+
options = { ...options, indent };
910913
}
911914
if (e.affectsConfiguration(treeRenderIndentGuidesKey)) {
912915
const renderIndentGuides = configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey);
913-
tree.updateOptions({ renderIndentGuides });
916+
options = { ...options, renderIndentGuides };
917+
}
918+
if (e.affectsConfiguration(listSmoothScrolling)) {
919+
const smoothScrolling = configurationService.getValue<boolean>(listSmoothScrolling);
920+
options = { ...options, smoothScrolling };
914921
}
915922
if (e.affectsConfiguration(keyboardNavigationSettingKey)) {
916923
updateKeyboardNavigation();
917924
}
918925
if (e.affectsConfiguration(automaticKeyboardNavigationSettingKey)) {
919-
tree.updateOptions({ automaticKeyboardNavigation: getAutomaticKeyboardNavigation() });
926+
options = { ...options, automaticKeyboardNavigation: getAutomaticKeyboardNavigation() };
927+
}
928+
if (Object.keys(options).length > 0) {
929+
tree.updateOptions(options);
920930
}
921931
}),
922932
this.contextKeyService.onDidChangeContext(e => {
@@ -1001,6 +1011,11 @@ configurationRegistry.registerConfiguration({
10011011
default: 'onHover',
10021012
description: localize('render tree indent guides', "Controls whether the tree should render indent guides.")
10031013
},
1014+
[listSmoothScrolling]: {
1015+
type: 'boolean',
1016+
default: false,
1017+
description: localize('list smoothScrolling setting', "Controls whether lists and trees have smooth scrolling."),
1018+
},
10041019
[keyboardNavigationSettingKey]: {
10051020
'type': 'string',
10061021
'enum': ['simple', 'highlight', 'filter'],

0 commit comments

Comments
 (0)