add list smooth scrolling

fixes #98175
This commit is contained in:
João Moreno
2020-05-27 11:09:15 +02:00
parent 42ae88fd51
commit da8f44cf08
5 changed files with 51 additions and 19 deletions
+18 -8
View File
@@ -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<T> {
isChecked?(element: T): boolean | undefined;
}
export interface IListViewOptions<T> {
export interface IListViewOptionsUpdate {
readonly additionalScrollHeight?: number;
readonly smoothScrolling?: boolean;
}
export interface IListViewOptions<T> extends IListViewOptionsUpdate {
readonly dnd?: IListViewDragAndDrop<T>;
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
@@ -58,7 +63,6 @@ export interface IListViewOptions<T> {
readonly mouseSupport?: boolean;
readonly horizontalScrolling?: boolean;
readonly accessibilityProvider?: IListViewAccessibilityProvider<T>;
readonly additionalScrollHeight?: number;
readonly transformOptimization?: boolean;
}
@@ -204,7 +208,8 @@ export class ListView<T> implements ISpliceable<T>, 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<void>(50);
@@ -285,12 +290,13 @@ export class ListView<T> implements ISpliceable<T>, 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<T> implements ISpliceable<T>, IDisposable {
if (options.additionalScrollHeight !== undefined) {
this.additionalScrollHeight = options.additionalScrollHeight;
}
if (options.smoothScrolling !== undefined) {
this.scrollable.setSmoothScrollDuration(options.smoothScrolling ? 125 : 0);
}
}
triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
+3 -6
View File
@@ -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<T> implements IListViewDragAndDrop<T> {
}
}
export interface IListOptionsUpdate {
export interface IListOptionsUpdate extends IListViewOptionsUpdate {
readonly enableKeyboardNavigation?: boolean;
readonly automaticKeyboardNavigation?: boolean;
readonly additionalScrollHeight?: number;
}
export class List<T> implements ISpliceable<T>, IDisposable {
@@ -1288,9 +1287,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.typeLabelController.updateOptions(this._options);
}
if (optionsUpdate.additionalScrollHeight !== undefined) {
this.view.updateOptions(optionsUpdate);
}
this.view.updateOptions(optionsUpdate);
}
get options(): IListOptions<T> {
@@ -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 {
+3 -1
View File
@@ -962,6 +962,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions {
readonly simpleKeyboardNavigation?: boolean;
readonly filterOnType?: boolean;
readonly openOnSingleClick?: boolean;
readonly smoothScrolling?: boolean;
}
export interface IAbstractTreeOptions<T, TFilterData = void> extends IAbstractTreeOptionsUpdate, IListOptions<T> {
@@ -1360,7 +1361,8 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.view.updateOptions({
enableKeyboardNavigation: this._options.simpleKeyboardNavigation,
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation
automaticKeyboardNavigation: this._options.automaticKeyboardNavigation,
smoothScrolling: this._options.smoothScrolling
});
if (this.typeFilterController) {
+19 -4
View File
@@ -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<boolean>(configurationService, horizontalScrollingKey, 'workbench.tree.horizontalScrolling');
@@ -823,6 +824,7 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
...workbenchListOptions,
indent: configurationService.getValue<number>(treeIndentKey),
renderIndentGuides: configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey),
smoothScrolling: configurationService.getValue<boolean>(listSmoothScrolling),
automaticKeyboardNavigation: getAutomaticKeyboardNavigation(),
simpleKeyboardNavigation: keyboardNavigation === 'simple',
filterOnType: keyboardNavigation === 'filter',
@@ -898,25 +900,33 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
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<number>(treeIndentKey);
tree.updateOptions({ indent });
options = { ...options, indent };
}
if (e.affectsConfiguration(treeRenderIndentGuidesKey)) {
const renderIndentGuides = configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey);
tree.updateOptions({ renderIndentGuides });
options = { ...options, renderIndentGuides };
}
if (e.affectsConfiguration(listSmoothScrolling)) {
const smoothScrolling = configurationService.getValue<boolean>(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'],