Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Clicking in Scrollbars Move By Page #104923

Merged
merged 8 commits into from Nov 9, 2020
Merged
12 changes: 11 additions & 1 deletion src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts
Expand Up @@ -38,12 +38,14 @@ export interface AbstractScrollbarOptions {
visibility: ScrollbarVisibility;
extraScrollbarClassName: string;
scrollable: Scrollable;
scrollByPage: boolean;
}

export abstract class AbstractScrollbar extends Widget {

protected _host: ScrollbarHost;
protected _scrollable: Scrollable;
protected _scrollByPage: boolean;
private _lazyRender: boolean;
protected _scrollbarState: ScrollbarState;
private _visibilityController: ScrollbarVisibilityController;
Expand All @@ -59,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget {
this._lazyRender = opts.lazyRender;
this._host = opts.host;
this._scrollable = opts.scrollable;
this._scrollByPage = opts.scrollByPage;
this._scrollbarState = opts.scrollbarState;
this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
Expand Down Expand Up @@ -210,7 +213,14 @@ export abstract class AbstractScrollbar extends Widget {
offsetX = e.posx - domNodePosition.left;
offsetY = e.posy - domNodePosition.top;
}
this._setDesiredScrollPositionNow(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(offsetX, offsetY)));

const offset = this._mouseDownRelativePosition(offsetX, offsetY);
this._setDesiredScrollPositionNow(
this._scrollByPage
? this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(offset)
: this._scrollbarState.getDesiredScrollPositionFromOffset(offset)
);

if (e.leftButton) {
e.preventDefault();
this._sliderMouseDown(e, () => { /*nothing to do*/ });
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts
Expand Up @@ -33,7 +33,8 @@ export class HorizontalScrollbar extends AbstractScrollbar {
),
visibility: options.horizontal,
extraScrollbarClassName: 'horizontal',
scrollable: scrollable
scrollable: scrollable,
scrollByPage: options.scrollByPage
});

if (options.horizontalHasArrows) {
Expand Down
4 changes: 3 additions & 1 deletion src/vs/base/browser/ui/scrollbar/scrollableElement.ts
Expand Up @@ -617,7 +617,9 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : ScrollbarVisibility.Auto),
verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10),
verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false),
verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0)
verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0),

scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : false)
};

result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize);
Expand Down
6 changes: 6 additions & 0 deletions src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts
Expand Up @@ -114,6 +114,11 @@ export interface ScrollableElementCreationOptions {
* Defaults to false.
*/
verticalHasArrows?: boolean;
/**
* Scroll gutter clicks move by page vs. jump to position.
* Defaults to false.
*/
scrollByPage?: boolean;
}

export interface ScrollableElementChangeOptions {
Expand Down Expand Up @@ -146,4 +151,5 @@ export interface ScrollableElementResolvedOptions {
verticalScrollbarSize: number;
verticalSliderSize: number;
verticalHasArrows: boolean;
scrollByPage: boolean;
}
22 changes: 22 additions & 0 deletions src/vs/base/browser/ui/scrollbar/scrollbarState.ts
Expand Up @@ -202,6 +202,28 @@ export class ScrollbarState {
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}

/**
* Compute a desired `scrollPosition` from if offset is before or after the slider position.
* If offset is before slider, treat as a page up (or left). If after, page down (or right).
* `offset` and `_computedSliderPosition` are based on the same coordinate system.
* `_visibleSize` corresponds to a "page" of lines in the returned coordinate system.
*/
public getDesiredScrollPositionFromOffsetPaged(offset: number): number {
if (!this._computedIsNeeded) {
// no need for a slider
return 0;
}

let correctedOffset = offset - this._arrowSize; // compensate if has arrows
let desiredScrollPosition = this._scrollPosition;
if (correctedOffset < this._computedSliderPosition) {
desiredScrollPosition -= this._visibleSize; // page up/left
} else {
desiredScrollPosition += this._visibleSize; // page down/right
}
return desiredScrollPosition;
}

/**
* Compute a desired `scrollPosition` such that the slider moves by `delta`.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts
Expand Up @@ -33,7 +33,8 @@ export class VerticalScrollbar extends AbstractScrollbar {
),
visibility: options.vertical,
extraScrollbarClassName: 'vertical',
scrollable: scrollable
scrollable: scrollable,
scrollByPage: options.scrollByPage
});

if (options.verticalHasArrows) {
Expand Down
10 changes: 8 additions & 2 deletions src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts
Expand Up @@ -18,8 +18,11 @@ suite('ScrollbarState', () => {
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 249);


assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849);

// 259 is greater than 230 so page down, 32787 + 339 = 33126
assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(259), 33126);

actual.setScrollPosition(32849);
assert.equal(actual.getArrowSize(), 0);
assert.equal(actual.getScrollPosition(), 32849);
Expand All @@ -41,8 +44,11 @@ suite('ScrollbarState', () => {
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 230);


assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811);

// 240 + 12 = 252; greater than 230 so page down, 32787 + 339 = 33126
assert.equal(actual.getDesiredScrollPositionFromOffsetPaged(240 + 12), 33126);

actual.setScrollPosition(32811);
assert.equal(actual.getArrowSize(), 12);
assert.equal(actual.getScrollPosition(), 32811);
Expand Down
Expand Up @@ -56,6 +56,7 @@ export class EditorScrollbar extends ViewPart {
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity,
fastScrollSensitivity: fastScrollSensitivity,
scrollPredominantAxis: scrollPredominantAxis,
scrollByPage: scrollbar.scrollByPage,
};

this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable()));
Expand Down
10 changes: 9 additions & 1 deletion src/vs/editor/common/config/editorOptions.ts
Expand Up @@ -2920,6 +2920,11 @@ export interface IEditorScrollbarOptions {
* Defaults to `horizontalScrollbarSize`.
*/
horizontalSliderSize?: number;
/**
* Scroll gutter clicks move by page vs jump to position.
* Defaults to false.
*/
scrollByPage?: boolean;
}

export interface InternalEditorScrollbarOptions {
Expand All @@ -2935,6 +2940,7 @@ export interface InternalEditorScrollbarOptions {
readonly horizontalSliderSize: number;
readonly verticalScrollbarSize: number;
readonly verticalSliderSize: number;
readonly scrollByPage: boolean;
}

function _scrollbarVisibilityFromString(visibility: string | undefined, defaultValue: ScrollbarVisibility): ScrollbarVisibility {
Expand Down Expand Up @@ -2965,7 +2971,8 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
verticalScrollbarSize: 14,
verticalSliderSize: 14,
handleMouseWheel: true,
alwaysConsumeMouseWheel: true
alwaysConsumeMouseWheel: true,
scrollByPage: false
}
);
}
Expand All @@ -2990,6 +2997,7 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
horizontalSliderSize: EditorIntOption.clampedInt(input.horizontalSliderSize, horizontalScrollbarSize, 0, 1000),
verticalScrollbarSize: verticalScrollbarSize,
verticalSliderSize: EditorIntOption.clampedInt(input.verticalSliderSize, verticalScrollbarSize, 0, 1000),
scrollByPage: EditorBooleanOption.boolean(input.scrollByPage, this.defaultValue.scrollByPage),
};
}
}
Expand Down
Expand Up @@ -68,6 +68,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize,
verticalScrollbarSize: input.verticalScrollbarWidth,
verticalSliderSize: EditorOptions.scrollbar.defaultValue.verticalSliderSize,
scrollByPage: EditorOptions.scrollbar.defaultValue.scrollByPage,
};
options._write(EditorOption.scrollbar, scrollbarOptions);
const lineNumbersOptions: InternalEditorRenderLineNumbersOptions = {
Expand Down
6 changes: 6 additions & 0 deletions src/vs/monaco.d.ts
Expand Up @@ -3660,6 +3660,11 @@ declare namespace monaco.editor {
* Defaults to `horizontalScrollbarSize`.
*/
horizontalSliderSize?: number;
/**
* Scroll gutter clicks move by page vs jump to position.
* Defaults to false.
*/
scrollByPage?: boolean;
}

export interface InternalEditorScrollbarOptions {
Expand All @@ -3675,6 +3680,7 @@ declare namespace monaco.editor {
readonly horizontalSliderSize: number;
readonly verticalScrollbarSize: number;
readonly verticalSliderSize: number;
readonly scrollByPage: boolean;
}

/**
Expand Down