Reduce size of scrollable impl

This commit is contained in:
Daniel Imms
2026-02-01 21:28:18 -08:00
parent f666135881
commit beebe8b5dc
27 changed files with 3295 additions and 3295 deletions
File diff suppressed because it is too large Load Diff
+67 -67
View File
@@ -8,93 +8,93 @@ import { Emitter } from './event';
class WindowManager {
static readonly INSTANCE = new WindowManager();
static readonly INSTANCE = new WindowManager();
// --- Zoom Level
// --- Zoom Level
private readonly mapWindowIdToZoomLevel = new Map<number, number>();
private readonly mapWindowIdToZoomLevel = new Map<number, number>();
private readonly _onDidChangeZoomLevel = new Emitter<number>();
readonly onDidChangeZoomLevel = this._onDidChangeZoomLevel.event;
private readonly _onDidChangeZoomLevel = new Emitter<number>();
readonly onDidChangeZoomLevel = this._onDidChangeZoomLevel.event;
getZoomLevel(targetWindow: Window): number {
return this.mapWindowIdToZoomLevel.get(this.getWindowId(targetWindow)) ?? 0;
}
setZoomLevel(zoomLevel: number, targetWindow: Window): void {
if (this.getZoomLevel(targetWindow) === zoomLevel) {
return;
}
getZoomLevel(targetWindow: Window): number {
return this.mapWindowIdToZoomLevel.get(this.getWindowId(targetWindow)) ?? 0;
}
setZoomLevel(zoomLevel: number, targetWindow: Window): void {
if (this.getZoomLevel(targetWindow) === zoomLevel) {
return;
}
const targetWindowId = this.getWindowId(targetWindow);
this.mapWindowIdToZoomLevel.set(targetWindowId, zoomLevel);
this._onDidChangeZoomLevel.fire(targetWindowId);
}
const targetWindowId = this.getWindowId(targetWindow);
this.mapWindowIdToZoomLevel.set(targetWindowId, zoomLevel);
this._onDidChangeZoomLevel.fire(targetWindowId);
}
// --- Zoom Factor
// --- Zoom Factor
private readonly mapWindowIdToZoomFactor = new Map<number, number>();
private readonly mapWindowIdToZoomFactor = new Map<number, number>();
getZoomFactor(targetWindow: Window): number {
return this.mapWindowIdToZoomFactor.get(this.getWindowId(targetWindow)) ?? 1;
}
setZoomFactor(zoomFactor: number, targetWindow: Window): void {
this.mapWindowIdToZoomFactor.set(this.getWindowId(targetWindow), zoomFactor);
}
getZoomFactor(targetWindow: Window): number {
return this.mapWindowIdToZoomFactor.get(this.getWindowId(targetWindow)) ?? 1;
}
setZoomFactor(zoomFactor: number, targetWindow: Window): void {
this.mapWindowIdToZoomFactor.set(this.getWindowId(targetWindow), zoomFactor);
}
// --- Fullscreen
// --- Fullscreen
private readonly _onDidChangeFullscreen = new Emitter<number>();
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;
private readonly _onDidChangeFullscreen = new Emitter<number>();
readonly onDidChangeFullscreen = this._onDidChangeFullscreen.event;
private readonly mapWindowIdToFullScreen = new Map<number, boolean>();
private readonly mapWindowIdToFullScreen = new Map<number, boolean>();
setFullscreen(fullscreen: boolean, targetWindow: Window): void {
if (this.isFullscreen(targetWindow) === fullscreen) {
return;
}
setFullscreen(fullscreen: boolean, targetWindow: Window): void {
if (this.isFullscreen(targetWindow) === fullscreen) {
return;
}
const windowId = this.getWindowId(targetWindow);
this.mapWindowIdToFullScreen.set(windowId, fullscreen);
this._onDidChangeFullscreen.fire(windowId);
}
isFullscreen(targetWindow: Window): boolean {
return !!this.mapWindowIdToFullScreen.get(this.getWindowId(targetWindow));
}
const windowId = this.getWindowId(targetWindow);
this.mapWindowIdToFullScreen.set(windowId, fullscreen);
this._onDidChangeFullscreen.fire(windowId);
}
isFullscreen(targetWindow: Window): boolean {
return !!this.mapWindowIdToFullScreen.get(this.getWindowId(targetWindow));
}
private getWindowId(targetWindow: Window): number {
return (targetWindow as CodeWindow).vscodeWindowId;
}
private getWindowId(targetWindow: Window): number {
return (targetWindow as CodeWindow).vscodeWindowId;
}
}
export function addMatchMediaChangeListener(targetWindow: Window, query: string | MediaQueryList, callback: (this: MediaQueryList, ev: MediaQueryListEvent) => any): void {
if (typeof query === 'string') {
query = targetWindow.matchMedia(query);
}
query.addEventListener('change', callback);
if (typeof query === 'string') {
query = targetWindow.matchMedia(query);
}
query.addEventListener('change', callback);
}
/** A zoom index, e.g. 1, 2, 3 */
export function setZoomLevel(zoomLevel: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomLevel(zoomLevel, targetWindow);
WindowManager.INSTANCE.setZoomLevel(zoomLevel, targetWindow);
}
export function getZoomLevel(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomLevel(targetWindow);
return WindowManager.INSTANCE.getZoomLevel(targetWindow);
}
export const onDidChangeZoomLevel = WindowManager.INSTANCE.onDidChangeZoomLevel;
/** The zoom scale for an index, e.g. 1, 1.2, 1.4 */
export function getZoomFactor(targetWindow: Window): number {
return WindowManager.INSTANCE.getZoomFactor(targetWindow);
return WindowManager.INSTANCE.getZoomFactor(targetWindow);
}
export function setZoomFactor(zoomFactor: number, targetWindow: Window): void {
WindowManager.INSTANCE.setZoomFactor(zoomFactor, targetWindow);
WindowManager.INSTANCE.setZoomFactor(zoomFactor, targetWindow);
}
export function setFullscreen(fullscreen: boolean, targetWindow: Window): void {
WindowManager.INSTANCE.setFullscreen(fullscreen, targetWindow);
WindowManager.INSTANCE.setFullscreen(fullscreen, targetWindow);
}
export function isFullscreen(targetWindow: Window): boolean {
return WindowManager.INSTANCE.isFullscreen(targetWindow);
return WindowManager.INSTANCE.isFullscreen(targetWindow);
}
export const onDidChangeFullscreen = WindowManager.INSTANCE.onDidChangeFullscreen;
@@ -110,32 +110,32 @@ export const isAndroid = (userAgent.indexOf('Android') >= 0);
let standalone = false;
if (typeof mainWindow.matchMedia === 'function') {
const standaloneMatchMedia = mainWindow.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
const fullScreenMatchMedia = mainWindow.matchMedia('(display-mode: fullscreen)');
standalone = standaloneMatchMedia.matches;
addMatchMediaChangeListener(mainWindow, standaloneMatchMedia, ({ matches }) => {
// entering fullscreen would change standaloneMatchMedia.matches to false
// if standalone is true (running as PWA) and entering fullscreen, skip this change
if (standalone && fullScreenMatchMedia.matches) {
return;
}
// otherwise update standalone (browser to PWA or PWA to browser)
standalone = matches;
});
const standaloneMatchMedia = mainWindow.matchMedia('(display-mode: standalone) or (display-mode: window-controls-overlay)');
const fullScreenMatchMedia = mainWindow.matchMedia('(display-mode: fullscreen)');
standalone = standaloneMatchMedia.matches;
addMatchMediaChangeListener(mainWindow, standaloneMatchMedia, ({ matches }) => {
// entering fullscreen would change standaloneMatchMedia.matches to false
// if standalone is true (running as PWA) and entering fullscreen, skip this change
if (standalone && fullScreenMatchMedia.matches) {
return;
}
// otherwise update standalone (browser to PWA or PWA to browser)
standalone = matches;
});
}
export function isStandalone(): boolean {
return standalone;
return standalone;
}
// Visible means that the feature is enabled, not necessarily being rendered
// e.g. visible is true even in fullscreen mode where the controls are hidden
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/visible
export function isWCOEnabled(): boolean {
return (navigator as any)?.windowControlsOverlay?.visible;
return (navigator as any)?.windowControlsOverlay?.visible;
}
// Returns the bounding rect of the titlebar area if it is supported and defined
// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect
export function getWCOBoundingRect(): DOMRect | undefined {
return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect();
}
+88 -88
View File
@@ -20,48 +20,48 @@ export type INumberDictionary<V> = Record<number, V>;
* group function.
*/
export function groupBy<K extends string | number | symbol, V>(data: V[], groupFn: (element: V) => K): Record<K, V[]> {
const result: Record<K, V[]> = Object.create(null);
for (const element of data) {
const key = groupFn(element);
let target = result[key];
if (!target) {
target = result[key] = [];
}
target.push(element);
}
return result;
const result: Record<K, V[]> = Object.create(null);
for (const element of data) {
const key = groupFn(element);
let target = result[key];
if (!target) {
target = result[key] = [];
}
target.push(element);
}
return result;
}
export function diffSets<T>(before: Set<T>, after: Set<T>): { removed: T[]; added: T[] } {
const removed: T[] = [];
const added: T[] = [];
for (const element of before) {
if (!after.has(element)) {
removed.push(element);
}
}
for (const element of after) {
if (!before.has(element)) {
added.push(element);
}
}
return { removed, added };
export function diffSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
const removed: T[] = [];
const added: T[] = [];
for (const element of before) {
if (!after.has(element)) {
removed.push(element);
}
}
for (const element of after) {
if (!before.has(element)) {
added.push(element);
}
}
return { removed, added };
}
export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[]; added: V[] } {
const removed: V[] = [];
const added: V[] = [];
for (const [index, value] of before) {
if (!after.has(index)) {
removed.push(value);
}
}
for (const [index, value] of after) {
if (!before.has(index)) {
added.push(value);
}
}
return { removed, added };
export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
const removed: V[] = [];
const added: V[] = [];
for (const [index, value] of before) {
if (!after.has(index)) {
removed.push(value);
}
}
for (const [index, value] of after) {
if (!before.has(index)) {
added.push(value);
}
}
return { removed, added };
}
/**
@@ -72,69 +72,69 @@ export function diffMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed:
* @returns A new set containing the elements that are in both `setA` and `setB`.
*/
export function intersection<T>(setA: Set<T>, setB: Iterable<T>): Set<T> {
const result = new Set<T>();
for (const elem of setB) {
if (setA.has(elem)) {
result.add(elem);
}
}
return result;
const result = new Set<T>();
for (const elem of setB) {
if (setA.has(elem)) {
result.add(elem);
}
}
return result;
}
export class SetWithKey<T> implements Set<T> {
private _map = new Map<any, T>();
private _map = new Map<any, T>();
constructor(values: T[], private toKey: (t: T) => any) {
for (const value of values) {
this.add(value);
}
}
constructor(values: T[], private toKey: (t: T) => any) {
for (const value of values) {
this.add(value);
}
}
get size(): number {
return this._map.size;
}
get size(): number {
return this._map.size;
}
add(value: T): this {
const key = this.toKey(value);
this._map.set(key, value);
return this;
}
add(value: T): this {
const key = this.toKey(value);
this._map.set(key, value);
return this;
}
delete(value: T): boolean {
return this._map.delete(this.toKey(value));
}
delete(value: T): boolean {
return this._map.delete(this.toKey(value));
}
has(value: T): boolean {
return this._map.has(this.toKey(value));
}
has(value: T): boolean {
return this._map.has(this.toKey(value));
}
*entries(): IterableIterator<[T, T]> {
for (const entry of this._map.values()) {
yield [entry, entry];
}
}
*entries(): IterableIterator<[T, T]> {
for (const entry of this._map.values()) {
yield [entry, entry];
}
}
keys(): IterableIterator<T> {
return this.values();
}
keys(): IterableIterator<T> {
return this.values();
}
*values(): IterableIterator<T> {
for (const entry of this._map.values()) {
yield entry;
}
}
*values(): IterableIterator<T> {
for (const entry of this._map.values()) {
yield entry;
}
}
clear(): void {
this._map.clear();
}
clear(): void {
this._map.clear();
}
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void {
this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
}
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void {
this._map.forEach(entry => callbackfn.call(thisArg, entry, entry, this));
}
[Symbol.iterator](): IterableIterator<T> {
return this.values();
}
[Symbol.iterator](): IterableIterator<T> {
return this.values();
}
[Symbol.toStringTag]: string = 'SetWithKey';
[Symbol.toStringTag]: string = 'SetWithKey';
}
+95 -95
View File
@@ -4,127 +4,127 @@
*--------------------------------------------------------------------------------------------*/
function createDecorator(mapFn: (fn: Function, key: string) => Function): Function {
return (target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;
return (target: any, key: string, descriptor: any) => {
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn) {
throw new Error('not supported');
}
if (!fn) {
throw new Error('not supported');
}
descriptor[fnKey!] = mapFn(fn, key);
};
descriptor[fnKey!] = mapFn(fn, key);
};
}
export function memoize(_target: any, key: string, descriptor: any) {
let fnKey: string | null = null;
let fn: Function | null = null;
let fnKey: string | null = null;
let fn: Function | null = null;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
if (typeof descriptor.value === 'function') {
fnKey = 'value';
fn = descriptor.value;
if (fn!.length !== 0) {
console.warn('Memoize should only be used in functions with zero parameters');
}
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (fn!.length !== 0) {
console.warn('Memoize should only be used in functions with zero parameters');
}
} else if (typeof descriptor.get === 'function') {
fnKey = 'get';
fn = descriptor.get;
}
if (!fn) {
throw new Error('not supported');
}
if (!fn) {
throw new Error('not supported');
}
const memoizeKey = `$memoize$${key}`;
descriptor[fnKey!] = function (...args: any[]) {
if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, {
configurable: false,
enumerable: false,
writable: false,
value: fn.apply(this, args)
});
}
const memoizeKey = `$memoize$${key}`;
descriptor[fnKey!] = function (...args: any[]) {
if (!this.hasOwnProperty(memoizeKey)) {
Object.defineProperty(this, memoizeKey, {
configurable: false,
enumerable: false,
writable: false,
value: fn.apply(this, args)
});
}
return this[memoizeKey];
};
return this[memoizeKey];
};
}
export interface IDebounceReducer<T> {
(previousValue: T, ...args: any[]): T;
(previousValue: T, ...args: any[]): T;
}
export function debounce<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$debounce$${key}`;
const resultKey = `$debounce$result$${key}`;
return createDecorator((fn, key) => {
const timerKey = `$debounce$${key}`;
const resultKey = `$debounce$result$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
clearTimeout(this[timerKey]);
clearTimeout(this[timerKey]);
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
args = [this[resultKey]];
}
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
args = [this[resultKey]];
}
this[timerKey] = setTimeout(() => {
fn.apply(this, args);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, delay);
};
});
this[timerKey] = setTimeout(() => {
fn.apply(this, args);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, delay);
};
});
}
export function throttle<T>(delay: number, reducer?: IDebounceReducer<T>, initialValueProvider?: () => T): Function {
return createDecorator((fn, key) => {
const timerKey = `$throttle$timer$${key}`;
const resultKey = `$throttle$result$${key}`;
const lastRunKey = `$throttle$lastRun$${key}`;
const pendingKey = `$throttle$pending$${key}`;
return createDecorator((fn, key) => {
const timerKey = `$throttle$timer$${key}`;
const resultKey = `$throttle$result$${key}`;
const lastRunKey = `$throttle$lastRun$${key}`;
const pendingKey = `$throttle$pending$${key}`;
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
if (this[lastRunKey] === null || this[lastRunKey] === undefined) {
this[lastRunKey] = -Number.MAX_VALUE;
}
return function (this: any, ...args: any[]) {
if (!this[resultKey]) {
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}
if (this[lastRunKey] === null || this[lastRunKey] === undefined) {
this[lastRunKey] = -Number.MAX_VALUE;
}
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
}
if (reducer) {
this[resultKey] = reducer(this[resultKey], ...args);
}
if (this[pendingKey]) {
return;
}
if (this[pendingKey]) {
return;
}
const nextTime = this[lastRunKey] + delay;
if (nextTime <= Date.now()) {
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
} else {
this[pendingKey] = true;
this[timerKey] = setTimeout(() => {
this[pendingKey] = false;
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, nextTime - Date.now());
}
};
});
const nextTime = this[lastRunKey] + delay;
if (nextTime <= Date.now()) {
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
} else {
this[pendingKey] = true;
this[timerKey] = setTimeout(() => {
this[pendingKey] = false;
this[lastRunKey] = Date.now();
fn.apply(this, [this[resultKey]]);
this[resultKey] = initialValueProvider ? initialValueProvider() : undefined;
}, nextTime - Date.now());
}
};
});
}
+1 -1
View File
@@ -12,7 +12,7 @@ export interface Event<T> {
}
export class Emitter<T> {
private _listeners: { fn: (e: T) => any; thisArgs: any }[] = [];
private _listeners: { fn: (e: T) => any, thisArgs: any }[] = [];
private _disposed = false;
private _event: Event<T> | undefined;
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -7,26 +7,26 @@
* Given a function, returns a function that is only calling that function once.
*/
export function createSingleCallFunction<T extends Function>(this: unknown, fn: T, fnDidRunCallback?: () => void): T {
const _this = this;
let didCall = false;
let result: unknown;
const _this = this;
let didCall = false;
let result: unknown;
return function () {
if (didCall) {
return result;
}
return function () {
if (didCall) {
return result;
}
didCall = true;
if (fnDidRunCallback) {
try {
result = fn.apply(_this, arguments);
} finally {
fnDidRunCallback();
}
} else {
result = fn.apply(_this, arguments);
}
didCall = true;
if (fnDidRunCallback) {
try {
result = fn.apply(_this, arguments);
} finally {
fnDidRunCallback();
}
} else {
result = fn.apply(_this, arguments);
}
return result;
} as unknown as T;
return result;
} as unknown as T;
}
@@ -7,89 +7,89 @@ import * as dom from './dom';
import { DisposableStore, IDisposable, toDisposable } from './lifecycle';
export interface IPointerMoveCallback {
(event: PointerEvent): void;
(event: PointerEvent): void;
}
export interface IOnStopCallback {
(browserEvent?: PointerEvent | KeyboardEvent): void;
(browserEvent?: PointerEvent | KeyboardEvent): void;
}
export class GlobalPointerMoveMonitor implements IDisposable {
private readonly _hooks = new DisposableStore();
private _pointerMoveCallback: IPointerMoveCallback | null = null;
private _onStopCallback: IOnStopCallback | null = null;
private readonly _hooks = new DisposableStore();
private _pointerMoveCallback: IPointerMoveCallback | null = null;
private _onStopCallback: IOnStopCallback | null = null;
public dispose(): void {
this.stopMonitoring(false);
this._hooks.dispose();
}
public dispose(): void {
this.stopMonitoring(false);
this._hooks.dispose();
}
public stopMonitoring(invokeStopCallback: boolean, browserEvent?: PointerEvent | KeyboardEvent): void {
if (!this.isMonitoring()) {
return;
}
public stopMonitoring(invokeStopCallback: boolean, browserEvent?: PointerEvent | KeyboardEvent): void {
if (!this.isMonitoring()) {
return;
}
this._hooks.clear();
this._pointerMoveCallback = null;
const onStopCallback = this._onStopCallback;
this._onStopCallback = null;
this._hooks.clear();
this._pointerMoveCallback = null;
const onStopCallback = this._onStopCallback;
this._onStopCallback = null;
if (invokeStopCallback && onStopCallback) {
onStopCallback(browserEvent);
}
}
if (invokeStopCallback && onStopCallback) {
onStopCallback(browserEvent);
}
}
public isMonitoring(): boolean {
return !!this._pointerMoveCallback;
}
public isMonitoring(): boolean {
return !!this._pointerMoveCallback;
}
public startMonitoring(
initialElement: Element,
pointerId: number,
initialButtons: number,
pointerMoveCallback: IPointerMoveCallback,
onStopCallback: IOnStopCallback
): void {
if (this.isMonitoring()) {
this.stopMonitoring(false);
}
this._pointerMoveCallback = pointerMoveCallback;
this._onStopCallback = onStopCallback;
public startMonitoring(
initialElement: Element,
pointerId: number,
initialButtons: number,
pointerMoveCallback: IPointerMoveCallback,
onStopCallback: IOnStopCallback
): void {
if (this.isMonitoring()) {
this.stopMonitoring(false);
}
this._pointerMoveCallback = pointerMoveCallback;
this._onStopCallback = onStopCallback;
let eventSource: Element | Window = initialElement;
let eventSource: Element | Window = initialElement;
try {
initialElement.setPointerCapture(pointerId);
this._hooks.add(toDisposable(() => {
try {
initialElement.releasePointerCapture(pointerId);
} catch (err) {
// ignore
}
}));
} catch (err) {
eventSource = dom.getWindow(initialElement);
}
try {
initialElement.setPointerCapture(pointerId);
this._hooks.add(toDisposable(() => {
try {
initialElement.releasePointerCapture(pointerId);
} catch (err) {
// ignore
}
}));
} catch (err) {
eventSource = dom.getWindow(initialElement);
}
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_MOVE,
(e) => {
if (e.buttons !== initialButtons) {
this.stopMonitoring(true);
return;
}
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_MOVE,
(e) => {
if (e.buttons !== initialButtons) {
this.stopMonitoring(true);
return;
}
e.preventDefault();
this._pointerMoveCallback!(e);
}
));
e.preventDefault();
this._pointerMoveCallback!(e);
}
));
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_UP,
(e: PointerEvent) => this.stopMonitoring(true)
));
}
this._hooks.add(dom.addDisposableListener(
eventSource,
dom.EventType.POINTER_UP,
(e: PointerEvent) => this.stopMonitoring(true)
));
}
}
+61 -61
View File
@@ -10,76 +10,76 @@ import { INewScrollPosition, Scrollable, ScrollbarVisibility, ScrollEvent } from
export class HorizontalScrollbar extends AbstractScrollbar {
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
const scrollDimensions = scrollable.getScrollDimensions();
const scrollPosition = scrollable.getCurrentScrollPosition();
super({
lazyRender: options.lazyRender,
host: host,
scrollbarState: new ScrollbarState(
(options.horizontalHasArrows ? options.arrowSize : 0),
(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
scrollDimensions.width,
scrollDimensions.scrollWidth,
scrollPosition.scrollLeft
),
visibility: options.horizontal,
extraScrollbarClassName: 'horizontal',
scrollable: scrollable,
scrollByPage: options.scrollByPage
});
constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
const scrollDimensions = scrollable.getScrollDimensions();
const scrollPosition = scrollable.getCurrentScrollPosition();
super({
lazyRender: options.lazyRender,
host: host,
scrollbarState: new ScrollbarState(
(options.horizontalHasArrows ? options.arrowSize : 0),
(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize),
scrollDimensions.width,
scrollDimensions.scrollWidth,
scrollPosition.scrollLeft
),
visibility: options.horizontal,
extraScrollbarClassName: 'horizontal',
scrollable: scrollable,
scrollByPage: options.scrollByPage
});
if (options.horizontalHasArrows) {
throw new Error('horizontalHasArrows is not supported in xterm.js');
}
if (options.horizontalHasArrows) {
throw new Error('horizontalHasArrows is not supported in xterm.js');
}
this._createSlider(Math.floor((options.horizontalScrollbarSize - options.horizontalSliderSize) / 2), 0, undefined, options.horizontalSliderSize);
}
this._createSlider(Math.floor((options.horizontalScrollbarSize - options.horizontalSliderSize) / 2), 0, undefined, options.horizontalSliderSize);
}
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
this.slider.setWidth(sliderSize);
this.slider.setLeft(sliderPosition);
}
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
this.slider.setWidth(sliderSize);
this.slider.setLeft(sliderPosition);
}
protected _renderDomNode(largeSize: number, smallSize: number): void {
this.domNode.setWidth(largeSize);
this.domNode.setHeight(smallSize);
this.domNode.setLeft(0);
this.domNode.setBottom(0);
}
protected _renderDomNode(largeSize: number, smallSize: number): void {
this.domNode.setWidth(largeSize);
this.domNode.setHeight(smallSize);
this.domNode.setLeft(0);
this.domNode.setBottom(0);
}
public onDidScroll(e: ScrollEvent): boolean {
this._shouldRender = this._onElementScrollSize(e.scrollWidth) || this._shouldRender;
this._shouldRender = this._onElementScrollPosition(e.scrollLeft) || this._shouldRender;
this._shouldRender = this._onElementSize(e.width) || this._shouldRender;
return this._shouldRender;
}
public onDidScroll(e: ScrollEvent): boolean {
this._shouldRender = this._onElementScrollSize(e.scrollWidth) || this._shouldRender;
this._shouldRender = this._onElementScrollPosition(e.scrollLeft) || this._shouldRender;
this._shouldRender = this._onElementSize(e.width) || this._shouldRender;
return this._shouldRender;
}
protected _pointerDownRelativePosition(offsetX: number, offsetY: number): number {
return offsetX;
}
protected _pointerDownRelativePosition(offsetX: number, offsetY: number): number {
return offsetX;
}
protected _sliderPointerPosition(e: ISimplifiedPointerEvent): number {
return e.pageX;
}
protected _sliderPointerPosition(e: ISimplifiedPointerEvent): number {
return e.pageX;
}
protected _sliderOrthogonalPointerPosition(e: ISimplifiedPointerEvent): number {
return e.pageY;
}
protected _sliderOrthogonalPointerPosition(e: ISimplifiedPointerEvent): number {
return e.pageY;
}
protected _updateScrollbarSize(size: number): void {
this.slider.setHeight(size);
}
protected _updateScrollbarSize(size: number): void {
this.slider.setHeight(size);
}
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
target.scrollLeft = scrollPosition;
}
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
target.scrollLeft = scrollPosition;
}
public updateOptions(options: ScrollableElementResolvedOptions): void {
this.updateScrollbarSize(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize);
this._scrollbarState.setOppositeScrollbarSize(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize);
this._visibilityController.setVisibility(options.horizontal);
this._scrollByPage = options.scrollByPage;
}
public updateOptions(options: ScrollableElementResolvedOptions): void {
this.updateScrollbarSize(options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize);
this._scrollbarState.setOppositeScrollbarSize(options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize);
this._visibilityController.setVisibility(options.horizontal);
this._scrollByPage = options.scrollByPage;
}
}
+95 -95
View File
@@ -7,129 +7,129 @@
* Represents a window in a possible chain of iframes
*/
interface IWindowChainElement {
/**
* The window object for it
*/
readonly window: WeakRef<Window>;
/**
* The iframe element inside the window.parent corresponding to window
*/
readonly iframeElement: Element | null;
/**
* The window object for it
*/
readonly window: WeakRef<Window>;
/**
* The iframe element inside the window.parent corresponding to window
*/
readonly iframeElement: Element | null;
}
const sameOriginWindowChainCache = new WeakMap<Window, IWindowChainElement[] | null>();
function getParentWindowIfSameOrigin(w: Window): Window | null {
if (!w.parent || w.parent === w) {
return null;
}
if (!w.parent || w.parent === w) {
return null;
}
// Cannot really tell if we have access to the parent window unless we try to access something in it
try {
const location = w.location;
const parentLocation = w.parent.location;
if (location.origin !== 'null' && parentLocation.origin !== 'null' && location.origin !== parentLocation.origin) {
return null;
}
} catch (e) {
return null;
}
// Cannot really tell if we have access to the parent window unless we try to access something in it
try {
const location = w.location;
const parentLocation = w.parent.location;
if (location.origin !== 'null' && parentLocation.origin !== 'null' && location.origin !== parentLocation.origin) {
return null;
}
} catch (e) {
return null;
}
return w.parent;
return w.parent;
}
export class IframeUtils {
/**
* Returns a chain of embedded windows with the same origin (which can be accessed programmatically).
* Having a chain of length 1 might mean that the current execution environment is running outside of an iframe or inside an iframe embedded in a window with a different origin.
*/
private static getSameOriginWindowChain(targetWindow: Window): IWindowChainElement[] {
let windowChainCache = sameOriginWindowChainCache.get(targetWindow);
if (!windowChainCache) {
windowChainCache = [];
sameOriginWindowChainCache.set(targetWindow, windowChainCache);
let w: Window | null = targetWindow;
let parent: Window | null;
do {
parent = getParentWindowIfSameOrigin(w);
if (parent) {
windowChainCache.push({
window: new WeakRef(w),
iframeElement: w.frameElement || null
});
} else {
windowChainCache.push({
window: new WeakRef(w),
iframeElement: null
});
}
w = parent;
} while (w);
}
return windowChainCache.slice(0);
}
/**
* Returns a chain of embedded windows with the same origin (which can be accessed programmatically).
* Having a chain of length 1 might mean that the current execution environment is running outside of an iframe or inside an iframe embedded in a window with a different origin.
*/
private static getSameOriginWindowChain(targetWindow: Window): IWindowChainElement[] {
let windowChainCache = sameOriginWindowChainCache.get(targetWindow);
if (!windowChainCache) {
windowChainCache = [];
sameOriginWindowChainCache.set(targetWindow, windowChainCache);
let w: Window | null = targetWindow;
let parent: Window | null;
do {
parent = getParentWindowIfSameOrigin(w);
if (parent) {
windowChainCache.push({
window: new WeakRef(w),
iframeElement: w.frameElement || null
});
} else {
windowChainCache.push({
window: new WeakRef(w),
iframeElement: null
});
}
w = parent;
} while (w);
}
return windowChainCache.slice(0);
}
/**
* Returns the position of `childWindow` relative to `ancestorWindow`
*/
public static getPositionOfChildWindowRelativeToAncestorWindow(childWindow: Window, ancestorWindow: Window | null) {
/**
* Returns the position of `childWindow` relative to `ancestorWindow`
*/
public static getPositionOfChildWindowRelativeToAncestorWindow(childWindow: Window, ancestorWindow: Window | null) {
if (!ancestorWindow || childWindow === ancestorWindow) {
return {
top: 0,
left: 0
};
}
if (!ancestorWindow || childWindow === ancestorWindow) {
return {
top: 0,
left: 0
};
}
let top = 0, left = 0;
let top = 0; let left = 0;
const windowChain = this.getSameOriginWindowChain(childWindow);
const windowChain = this.getSameOriginWindowChain(childWindow);
for (const windowChainEl of windowChain) {
const windowInChain = windowChainEl.window.deref();
top += windowInChain?.scrollY ?? 0;
left += windowInChain?.scrollX ?? 0;
for (const windowChainEl of windowChain) {
const windowInChain = windowChainEl.window.deref();
top += windowInChain?.scrollY ?? 0;
left += windowInChain?.scrollX ?? 0;
if (windowInChain === ancestorWindow) {
break;
}
if (windowInChain === ancestorWindow) {
break;
}
if (!windowChainEl.iframeElement) {
break;
}
if (!windowChainEl.iframeElement) {
break;
}
const boundingRect = windowChainEl.iframeElement.getBoundingClientRect();
top += boundingRect.top;
left += boundingRect.left;
}
const boundingRect = windowChainEl.iframeElement.getBoundingClientRect();
top += boundingRect.top;
left += boundingRect.left;
}
return {
top: top,
left: left
};
}
return {
top: top,
left: left
};
}
}
/**
* Returns a sha-256 composed of `parentOrigin` and `salt` converted to base 32
*/
export async function parentOriginHash(parentOrigin: string, salt: string): Promise<string> {
// This same code is also inlined at `src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html`
if (!crypto.subtle) {
throw new Error(`'crypto.subtle' is not available so webviews will not work. This is likely because the editor is not running in a secure context (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).`);
}
// This same code is also inlined at `src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html`
if (!crypto.subtle) {
throw new Error(`'crypto.subtle' is not available so webviews will not work. This is likely because the editor is not running in a secure context (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).`);
}
const strData = JSON.stringify({ parentOrigin, salt });
const encoder = new TextEncoder();
const arrData = encoder.encode(strData);
const hash = await crypto.subtle.digest('sha-256', arrData);
return sha256AsBase32(hash);
const strData = JSON.stringify({ parentOrigin, salt });
const encoder = new TextEncoder();
const arrData = encoder.encode(strData);
const hash = await crypto.subtle.digest('sha-256', arrData);
return sha256AsBase32(hash);
}
function sha256AsBase32(bytes: ArrayBuffer): string {
const array = Array.from(new Uint8Array(bytes));
const hexArray = array.map(b => b.toString(16).padStart(2, '0')).join('');
// sha256 has 256 bits, so we need at most ceil(lg(2^256-1)/lg(32)) = 52 chars to represent it in base 32
return BigInt(`0x${hexArray}`).toString(32).padStart(52, '0');
const array = Array.from(new Uint8Array(bytes));
const hexArray = array.map(b => b.toString(16).padStart(2, '0')).join('');
// sha256 has 256 bits, so we need at most ceil(lg(2^256-1)/lg(32)) = 52 chars to represent it in base 32
return BigInt(`0x${hexArray}`).toString(32).padStart(52, '0');
}
+125 -125
View File
@@ -5,155 +5,155 @@
export namespace Iterable {
export function is<T = any>(thing: any): thing is Iterable<T> {
return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function';
}
export function is<T = any>(thing: any): thing is Iterable<T> {
return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function';
}
const _empty: Iterable<any> = Object.freeze([]);
export function empty<T = any>(): Iterable<T> {
return _empty;
}
const _empty: Iterable<any> = Object.freeze([]);
export function empty<T = any>(): Iterable<T> {
return _empty;
}
export function* single<T>(element: T): Iterable<T> {
yield element;
}
export function* single<T>(element: T): Iterable<T> {
yield element;
}
export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
if (is(iterableOrElement)) {
return iterableOrElement;
} else {
return single(iterableOrElement);
}
}
export function wrap<T>(iterableOrElement: Iterable<T> | T): Iterable<T> {
if (is(iterableOrElement)) {
return iterableOrElement;
}
return single(iterableOrElement);
export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
return iterable || _empty;
}
}
export function* reverse<T>(array: Array<T>): Iterable<T> {
for (let i = array.length - 1; i >= 0; i--) {
yield array[i];
}
}
export function from<T>(iterable: Iterable<T> | undefined | null): Iterable<T> {
return iterable || _empty;
}
export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
return !iterable || iterable[Symbol.iterator]().next().done === true;
}
export function* reverse<T>(array: T[]): Iterable<T> {
for (let i = array.length - 1; i >= 0; i--) {
yield array[i];
}
}
export function first<T>(iterable: Iterable<T>): T | undefined {
return iterable[Symbol.iterator]().next().value;
}
export function isEmpty<T>(iterable: Iterable<T> | undefined | null): boolean {
return !iterable || iterable[Symbol.iterator]().next().done === true;
}
export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
let i = 0;
for (const element of iterable) {
if (predicate(element, i++)) {
return true;
}
}
return false;
}
export function first<T>(iterable: Iterable<T>): T | undefined {
return iterable[Symbol.iterator]().next().value;
}
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
for (const element of iterable) {
if (predicate(element)) {
return element;
}
}
export function some<T>(iterable: Iterable<T>, predicate: (t: T, i: number) => unknown): boolean {
let i = 0;
for (const element of iterable) {
if (predicate(element, i++)) {
return true;
}
}
return false;
}
return undefined;
}
export function find<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): R | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined;
export function find<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): T | undefined {
for (const element of iterable) {
if (predicate(element)) {
return element;
}
}
export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
for (const element of iterable) {
if (predicate(element)) {
yield element;
}
}
}
return undefined;
}
export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield fn(element, index++);
}
}
export function filter<T, R extends T>(iterable: Iterable<T>, predicate: (t: T) => t is R): Iterable<R>;
export function filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T>;
export function* filter<T>(iterable: Iterable<T>, predicate: (t: T) => boolean): Iterable<T> {
for (const element of iterable) {
if (predicate(element)) {
yield element;
}
}
}
export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield* fn(element, index++);
}
}
export function* map<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => R): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield fn(element, index++);
}
}
export function* concat<T>(...iterables: Iterable<T>[]): Iterable<T> {
for (const iterable of iterables) {
yield* iterable;
}
}
export function* flatMap<T, R>(iterable: Iterable<T>, fn: (t: T, index: number) => Iterable<R>): Iterable<R> {
let index = 0;
for (const element of iterable) {
yield* fn(element, index++);
}
}
export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
let value = initialValue;
for (const element of iterable) {
value = reducer(value, element);
}
return value;
}
export function* concat<T>(...iterables: Iterable<T>[]): Iterable<T> {
for (const iterable of iterables) {
yield* iterable;
}
}
/**
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
*/
export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
if (from < 0) {
from += arr.length;
}
export function reduce<T, R>(iterable: Iterable<T>, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R {
let value = initialValue;
for (const element of iterable) {
value = reducer(value, element);
}
return value;
}
if (to < 0) {
to += arr.length;
} else if (to > arr.length) {
to = arr.length;
}
/**
* Returns an iterable slice of the array, with the same semantics as `array.slice()`.
*/
export function* slice<T>(arr: ReadonlyArray<T>, from: number, to = arr.length): Iterable<T> {
if (from < 0) {
from += arr.length;
}
for (; from < to; from++) {
yield arr[from];
}
}
if (to < 0) {
to += arr.length;
} else if (to > arr.length) {
to = arr.length;
}
/**
* Consumes `atMost` elements from iterable and returns the consumed elements,
* and an iterable for the rest of the elements.
*/
export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
const consumed: T[] = [];
for (; from < to; from++) {
yield arr[from];
}
}
if (atMost === 0) {
return [consumed, iterable];
}
/**
* Consumes `atMost` elements from iterable and returns the consumed elements,
* and an iterable for the rest of the elements.
*/
export function consume<T>(iterable: Iterable<T>, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable<T>] {
const consumed: T[] = [];
const iterator = iterable[Symbol.iterator]();
if (atMost === 0) {
return [consumed, iterable];
}
for (let i = 0; i < atMost; i++) {
const next = iterator.next();
const iterator = iterable[Symbol.iterator]();
if (next.done) {
return [consumed, Iterable.empty()];
}
for (let i = 0; i < atMost; i++) {
const next = iterator.next();
consumed.push(next.value);
}
if (next.done) {
return [consumed, Iterable.empty()];
}
return [consumed, { [Symbol.iterator]() { return iterator; } }];
}
consumed.push(next.value);
}
export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
const result: T[] = [];
for await (const item of iterable) {
result.push(item);
}
return Promise.resolve(result);
}
return [consumed, { [Symbol.iterator]() { return iterator; } }];
}
export async function asyncToArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
const result: T[] = [];
for await (const item of iterable) {
result.push(item);
}
return Promise.resolve(result);
}
}
+110 -110
View File
@@ -5,138 +5,138 @@
class Node<E> {
static readonly Undefined = new Node<any>(undefined);
static readonly Undefined = new Node<any>(undefined);
element: E;
next: Node<E>;
prev: Node<E>;
element: E;
next: Node<E>;
prev: Node<E>;
constructor(element: E) {
this.element = element;
this.next = Node.Undefined;
this.prev = Node.Undefined;
}
constructor(element: E) {
this.element = element;
this.next = Node.Undefined;
this.prev = Node.Undefined;
}
}
export class LinkedList<E> {
private _first: Node<E> = Node.Undefined;
private _last: Node<E> = Node.Undefined;
private _size: number = 0;
private _first: Node<E> = Node.Undefined;
private _last: Node<E> = Node.Undefined;
private _size: number = 0;
get size(): number {
return this._size;
}
get size(): number {
return this._size;
}
isEmpty(): boolean {
return this._first === Node.Undefined;
}
isEmpty(): boolean {
return this._first === Node.Undefined;
}
clear(): void {
let node = this._first;
while (node !== Node.Undefined) {
const next = node.next;
node.prev = Node.Undefined;
node.next = Node.Undefined;
node = next;
}
clear(): void {
let node = this._first;
while (node !== Node.Undefined) {
const next = node.next;
node.prev = Node.Undefined;
node.next = Node.Undefined;
node = next;
}
this._first = Node.Undefined;
this._last = Node.Undefined;
this._size = 0;
}
this._first = Node.Undefined;
this._last = Node.Undefined;
this._size = 0;
}
unshift(element: E): () => void {
return this._insert(element, false);
}
unshift(element: E): () => void {
return this._insert(element, false);
}
push(element: E): () => void {
return this._insert(element, true);
}
push(element: E): () => void {
return this._insert(element, true);
}
private _insert(element: E, atTheEnd: boolean): () => void {
const newNode = new Node(element);
if (this._first === Node.Undefined) {
this._first = newNode;
this._last = newNode;
private _insert(element: E, atTheEnd: boolean): () => void {
const newNode = new Node(element);
if (this._first === Node.Undefined) {
this._first = newNode;
this._last = newNode;
} else if (atTheEnd) {
// push
const oldLast = this._last;
this._last = newNode;
newNode.prev = oldLast;
oldLast.next = newNode;
} else if (atTheEnd) {
// push
const oldLast = this._last;
this._last = newNode;
newNode.prev = oldLast;
oldLast.next = newNode;
} else {
// unshift
const oldFirst = this._first;
this._first = newNode;
newNode.next = oldFirst;
oldFirst.prev = newNode;
}
this._size += 1;
} else {
// unshift
const oldFirst = this._first;
this._first = newNode;
newNode.next = oldFirst;
oldFirst.prev = newNode;
}
this._size += 1;
let didRemove = false;
return () => {
if (!didRemove) {
didRemove = true;
this._remove(newNode);
}
};
}
let didRemove = false;
return () => {
if (!didRemove) {
didRemove = true;
this._remove(newNode);
}
};
}
shift(): E | undefined {
if (this._first === Node.Undefined) {
return undefined;
} else {
const res = this._first.element;
this._remove(this._first);
return res;
}
}
shift(): E | undefined {
if (this._first === Node.Undefined) {
return undefined;
}
const res = this._first.element;
this._remove(this._first);
return res;
pop(): E | undefined {
if (this._last === Node.Undefined) {
return undefined;
} else {
const res = this._last.element;
this._remove(this._last);
return res;
}
}
}
private _remove(node: Node<E>): void {
if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
// middle
const anchor = node.prev;
anchor.next = node.next;
node.next.prev = anchor;
pop(): E | undefined {
if (this._last === Node.Undefined) {
return undefined;
}
const res = this._last.element;
this._remove(this._last);
return res;
} else if (node.prev === Node.Undefined && node.next === Node.Undefined) {
// only node
this._first = Node.Undefined;
this._last = Node.Undefined;
}
} else if (node.next === Node.Undefined) {
// last
this._last = this._last.prev!;
this._last.next = Node.Undefined;
private _remove(node: Node<E>): void {
if (node.prev !== Node.Undefined && node.next !== Node.Undefined) {
// middle
const anchor = node.prev;
anchor.next = node.next;
node.next.prev = anchor;
} else if (node.prev === Node.Undefined) {
// first
this._first = this._first.next!;
this._first.prev = Node.Undefined;
}
} else if (node.prev === Node.Undefined && node.next === Node.Undefined) {
// only node
this._first = Node.Undefined;
this._last = Node.Undefined;
// done
this._size -= 1;
}
} else if (node.next === Node.Undefined) {
// last
this._last = this._last.prev!;
this._last.next = Node.Undefined;
*[Symbol.iterator](): Iterator<E> {
let node = this._first;
while (node !== Node.Undefined) {
yield node.element;
node = node.next;
}
}
} else if (node.prev === Node.Undefined) {
// first
this._first = this._first.next!;
this._first.prev = Node.Undefined;
}
// done
this._size -= 1;
}
*[Symbol.iterator](): Iterator<E> {
let node = this._first;
while (node !== Node.Undefined) {
yield node.element;
node = node.next;
}
}
}
+133 -133
View File
@@ -4,69 +4,69 @@
*--------------------------------------------------------------------------------------------*/
export function getOrSet<K, V>(map: Map<K, V>, key: K, value: V): V {
let result = map.get(key);
if (result === undefined) {
result = value;
map.set(key, result);
}
let result = map.get(key);
if (result === undefined) {
result = value;
map.set(key, result);
}
return result;
return result;
}
export function mapToString<K, V>(map: Map<K, V>): string {
const entries: string[] = [];
map.forEach((value, key) => {
entries.push(`${key} => ${value}`);
});
const entries: string[] = [];
map.forEach((value, key) => {
entries.push(`${key} => ${value}`);
});
return `Map(${map.size}) {${entries.join(', ')}}`;
return `Map(${map.size}) {${entries.join(', ')}}`;
}
export function setToString<K>(set: Set<K>): string {
const entries: K[] = [];
set.forEach(value => {
entries.push(value);
});
const entries: K[] = [];
set.forEach(value => {
entries.push(value);
});
return `Set(${set.size}) {${entries.join(', ')}}`;
return `Set(${set.size}) {${entries.join(', ')}}`;
}
export const enum Touch {
None = 0,
AsOld = 1,
AsNew = 2
None = 0,
AsOld = 1,
AsNew = 2
}
export class CounterSet<T> {
private map = new Map<T, number>();
private map = new Map<T, number>();
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
add(value: T): CounterSet<T> {
this.map.set(value, (this.map.get(value) || 0) + 1);
return this;
}
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
delete(value: T): boolean {
let counter = this.map.get(value) || 0;
if (counter === 0) {
return false;
}
if (counter === 0) {
return false;
}
counter--;
counter--;
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
if (counter === 0) {
this.map.delete(value);
} else {
this.map.set(value, counter);
}
return true;
}
return true;
}
has(value: T): boolean {
return this.map.has(value);
}
has(value: T): boolean {
return this.map.has(value);
}
}
/**
@@ -75,128 +75,128 @@ export class CounterSet<T> {
*/
export class BidirectionalMap<K, V> {
private readonly _m1 = new Map<K, V>();
private readonly _m2 = new Map<V, K>();
private readonly _m1 = new Map<K, V>();
private readonly _m2 = new Map<V, K>();
constructor(entries?: readonly (readonly [K, V])[]) {
if (entries) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
}
constructor(entries?: ReadonlyArray<readonly [K, V]>) {
if (entries) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
}
clear(): void {
this._m1.clear();
this._m2.clear();
}
clear(): void {
this._m1.clear();
this._m2.clear();
}
set(key: K, value: V): void {
this._m1.set(key, value);
this._m2.set(value, key);
}
set(key: K, value: V): void {
this._m1.set(key, value);
this._m2.set(value, key);
}
get(key: K): V | undefined {
return this._m1.get(key);
}
get(key: K): V | undefined {
return this._m1.get(key);
}
getKey(value: V): K | undefined {
return this._m2.get(value);
}
getKey(value: V): K | undefined {
return this._m2.get(value);
}
delete(key: K): boolean {
const value = this._m1.get(key);
if (value === undefined) {
return false;
}
this._m1.delete(key);
this._m2.delete(value);
return true;
}
delete(key: K): boolean {
const value = this._m1.get(key);
if (value === undefined) {
return false;
}
this._m1.delete(key);
this._m2.delete(value);
return true;
}
forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: any): void {
this._m1.forEach((value, key) => {
callbackfn.call(thisArg, value, key, this);
});
}
forEach(callbackfn: (value: V, key: K, map: BidirectionalMap<K, V>) => void, thisArg?: any): void {
this._m1.forEach((value, key) => {
callbackfn.call(thisArg, value, key, this);
});
}
keys(): IterableIterator<K> {
return this._m1.keys();
}
keys(): IterableIterator<K> {
return this._m1.keys();
}
values(): IterableIterator<V> {
return this._m1.values();
}
values(): IterableIterator<V> {
return this._m1.values();
}
}
export class SetMap<K, V> {
private map = new Map<K, Set<V>>();
private map = new Map<K, Set<V>>();
add(key: K, value: V): void {
let values = this.map.get(key);
add(key: K, value: V): void {
let values = this.map.get(key);
if (!values) {
values = new Set<V>();
this.map.set(key, values);
}
if (!values) {
values = new Set<V>();
this.map.set(key, values);
}
values.add(value);
}
values.add(value);
}
delete(key: K, value: V): void {
const values = this.map.get(key);
delete(key: K, value: V): void {
const values = this.map.get(key);
if (!values) {
return;
}
if (!values) {
return;
}
values.delete(value);
values.delete(value);
if (values.size === 0) {
this.map.delete(key);
}
}
if (values.size === 0) {
this.map.delete(key);
}
}
forEach(key: K, fn: (value: V) => void): void {
const values = this.map.get(key);
forEach(key: K, fn: (value: V) => void): void {
const values = this.map.get(key);
if (!values) {
return;
}
if (!values) {
return;
}
values.forEach(fn);
}
values.forEach(fn);
}
get(key: K): ReadonlySet<V> {
const values = this.map.get(key);
if (!values) {
return new Set<V>();
}
return values;
}
get(key: K): ReadonlySet<V> {
const values = this.map.get(key);
if (!values) {
return new Set<V>();
}
return values;
}
}
export function mapsStrictEqualIgnoreOrder(a: Map<unknown, unknown>, b: Map<unknown, unknown>): boolean {
if (a === b) {
return true;
}
if (a === b) {
return true;
}
if (a.size !== b.size) {
return false;
}
if (a.size !== b.size) {
return false;
}
for (const [key, value] of a) {
if (!b.has(key) || b.get(key) !== value) {
return false;
}
}
for (const [key, value] of a) {
if (!b.has(key) || b.get(key) !== value) {
return false;
}
}
for (const [key] of b) {
if (!a.has(key)) {
return false;
}
}
for (const [key] of b) {
if (!a.has(key)) {
return false;
}
}
return true;
return true;
}
+152 -152
View File
@@ -8,195 +8,195 @@ import { IframeUtils } from './iframe';
import * as platform from './platform';
export interface IMouseEvent {
readonly browserEvent: MouseEvent;
readonly leftButton: boolean;
readonly middleButton: boolean;
readonly rightButton: boolean;
readonly buttons: number;
readonly target: HTMLElement;
readonly detail: number;
readonly posx: number;
readonly posy: number;
readonly ctrlKey: boolean;
readonly shiftKey: boolean;
readonly altKey: boolean;
readonly metaKey: boolean;
readonly timestamp: number;
readonly browserEvent: MouseEvent;
readonly leftButton: boolean;
readonly middleButton: boolean;
readonly rightButton: boolean;
readonly buttons: number;
readonly target: HTMLElement;
readonly detail: number;
readonly posx: number;
readonly posy: number;
readonly ctrlKey: boolean;
readonly shiftKey: boolean;
readonly altKey: boolean;
readonly metaKey: boolean;
readonly timestamp: number;
preventDefault(): void;
stopPropagation(): void;
preventDefault(): void;
stopPropagation(): void;
}
export class StandardMouseEvent implements IMouseEvent {
public readonly browserEvent: MouseEvent;
public readonly browserEvent: MouseEvent;
public readonly leftButton: boolean;
public readonly middleButton: boolean;
public readonly rightButton: boolean;
public readonly buttons: number;
public readonly target: HTMLElement;
public detail: number;
public readonly posx: number;
public readonly posy: number;
public readonly ctrlKey: boolean;
public readonly shiftKey: boolean;
public readonly altKey: boolean;
public readonly metaKey: boolean;
public readonly timestamp: number;
public readonly leftButton: boolean;
public readonly middleButton: boolean;
public readonly rightButton: boolean;
public readonly buttons: number;
public readonly target: HTMLElement;
public detail: number;
public readonly posx: number;
public readonly posy: number;
public readonly ctrlKey: boolean;
public readonly shiftKey: boolean;
public readonly altKey: boolean;
public readonly metaKey: boolean;
public readonly timestamp: number;
constructor(targetWindow: Window, e: MouseEvent) {
this.timestamp = Date.now();
this.browserEvent = e;
this.leftButton = e.button === 0;
this.middleButton = e.button === 1;
this.rightButton = e.button === 2;
this.buttons = e.buttons;
constructor(targetWindow: Window, e: MouseEvent) {
this.timestamp = Date.now();
this.browserEvent = e;
this.leftButton = e.button === 0;
this.middleButton = e.button === 1;
this.rightButton = e.button === 2;
this.buttons = e.buttons;
this.target = e.target as HTMLElement;
this.target = e.target as HTMLElement;
this.detail = e.detail || 1;
if (e.type === 'dblclick') {
this.detail = 2;
}
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
this.altKey = e.altKey;
this.metaKey = e.metaKey;
this.detail = e.detail || 1;
if (e.type === 'dblclick') {
this.detail = 2;
}
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
this.altKey = e.altKey;
this.metaKey = e.metaKey;
if (typeof e.pageX === 'number') {
this.posx = e.pageX;
this.posy = e.pageY;
} else {
this.posx = e.clientX + this.target.ownerDocument.body.scrollLeft + this.target.ownerDocument.documentElement.scrollLeft;
this.posy = e.clientY + this.target.ownerDocument.body.scrollTop + this.target.ownerDocument.documentElement.scrollTop;
}
if (typeof e.pageX === 'number') {
this.posx = e.pageX;
this.posy = e.pageY;
} else {
this.posx = e.clientX + this.target.ownerDocument.body.scrollLeft + this.target.ownerDocument.documentElement.scrollLeft;
this.posy = e.clientY + this.target.ownerDocument.body.scrollTop + this.target.ownerDocument.documentElement.scrollTop;
}
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(targetWindow, e.view);
this.posx -= iframeOffsets.left;
this.posy -= iframeOffsets.top;
}
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(targetWindow, e.view);
this.posx -= iframeOffsets.left;
this.posy -= iframeOffsets.top;
}
public preventDefault(): void {
this.browserEvent.preventDefault();
}
public preventDefault(): void {
this.browserEvent.preventDefault();
}
public stopPropagation(): void {
this.browserEvent.stopPropagation();
}
public stopPropagation(): void {
this.browserEvent.stopPropagation();
}
}
export interface IMouseWheelEvent extends MouseEvent {
readonly wheelDelta: number;
readonly wheelDeltaX: number;
readonly wheelDeltaY: number;
readonly wheelDelta: number;
readonly wheelDeltaX: number;
readonly wheelDeltaY: number;
readonly deltaX: number;
readonly deltaY: number;
readonly deltaZ: number;
readonly deltaMode: number;
readonly deltaX: number;
readonly deltaY: number;
readonly deltaZ: number;
readonly deltaMode: number;
}
interface IWebKitMouseWheelEvent {
wheelDeltaY: number;
wheelDeltaX: number;
wheelDeltaY: number;
wheelDeltaX: number;
}
interface IGeckoMouseWheelEvent {
HORIZONTAL_AXIS: number;
VERTICAL_AXIS: number;
axis: number;
detail: number;
HORIZONTAL_AXIS: number;
VERTICAL_AXIS: number;
axis: number;
detail: number;
}
export class StandardWheelEvent {
public readonly browserEvent: IMouseWheelEvent | null;
public readonly deltaY: number;
public readonly deltaX: number;
public readonly target: Node | null;
public readonly browserEvent: IMouseWheelEvent | null;
public readonly deltaY: number;
public readonly deltaX: number;
public readonly target: Node | null;
constructor(e: IMouseWheelEvent | null, deltaX: number = 0, deltaY: number = 0) {
constructor(e: IMouseWheelEvent | null, deltaX: number = 0, deltaY: number = 0) {
this.browserEvent = e || null;
this.target = e ? (e.target || (e as any).targetNode || e.srcElement) : null;
this.browserEvent = e || null;
this.target = e ? (e.target || (e as any).targetNode || e.srcElement) : null;
this.deltaY = deltaY;
this.deltaX = deltaX;
this.deltaY = deltaY;
this.deltaX = deltaX;
let shouldFactorDPR: boolean = false;
if (browser.isChrome) {
const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123;
shouldFactorDPR = chromeMajorVersion <= 122;
}
let shouldFactorDPR: boolean = false;
if (browser.isChrome) {
const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123;
shouldFactorDPR = chromeMajorVersion <= 122;
}
if (e) {
const e1 = e as IWebKitMouseWheelEvent as any;
const e2 = e as unknown as IGeckoMouseWheelEvent;
const devicePixelRatio = e.view?.devicePixelRatio || 1;
if (e) {
const e1 = e as IWebKitMouseWheelEvent as any;
const e2 = e as unknown as IGeckoMouseWheelEvent;
const devicePixelRatio = e.view?.devicePixelRatio || 1;
if (typeof e1.wheelDeltaY !== 'undefined') {
if (shouldFactorDPR) {
this.deltaY = e1.wheelDeltaY / (120 * devicePixelRatio);
} else {
this.deltaY = e1.wheelDeltaY / 120;
}
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3;
} else if (e.type === 'wheel') {
const ev = e as unknown as WheelEvent;
if (typeof e1.wheelDeltaY !== 'undefined') {
if (shouldFactorDPR) {
this.deltaY = e1.wheelDeltaY / (120 * devicePixelRatio);
} else {
this.deltaY = e1.wheelDeltaY / 120;
}
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
this.deltaY = -e2.detail / 3;
} else if (e.type === 'wheel') {
const ev = e as unknown as WheelEvent;
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
this.deltaY = -e.deltaY / 3;
} else {
this.deltaY = -e.deltaY;
}
} else {
this.deltaY = -e.deltaY / 40;
}
}
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
this.deltaY = -e.deltaY / 3;
} else {
this.deltaY = -e.deltaY;
}
} else {
this.deltaY = -e.deltaY / 40;
}
}
if (typeof e1.wheelDeltaX !== 'undefined') {
if (browser.isSafari && platform.isWindows) {
this.deltaX = -(e1.wheelDeltaX / 120);
} else if (shouldFactorDPR) {
this.deltaX = e1.wheelDeltaX / (120 * devicePixelRatio);
} else {
this.deltaX = e1.wheelDeltaX / 120;
}
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3;
} else if (e.type === 'wheel') {
const ev = e as unknown as WheelEvent;
if (typeof e1.wheelDeltaX !== 'undefined') {
if (browser.isSafari && platform.isWindows) {
this.deltaX = -(e1.wheelDeltaX / 120);
} else if (shouldFactorDPR) {
this.deltaX = e1.wheelDeltaX / (120 * devicePixelRatio);
} else {
this.deltaX = e1.wheelDeltaX / 120;
}
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
this.deltaX = -e.detail / 3;
} else if (e.type === 'wheel') {
const ev = e as unknown as WheelEvent;
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
this.deltaX = -e.deltaX / 3;
} else {
this.deltaX = -e.deltaX;
}
} else {
this.deltaX = -e.deltaX / 40;
}
}
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
if (browser.isFirefox && !platform.isMacintosh) {
this.deltaX = -e.deltaX / 3;
} else {
this.deltaX = -e.deltaX;
}
} else {
this.deltaX = -e.deltaX / 40;
}
}
if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) {
if (shouldFactorDPR) {
this.deltaY = e.wheelDelta / (120 * devicePixelRatio);
} else {
this.deltaY = e.wheelDelta / 120;
}
}
}
}
if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) {
if (shouldFactorDPR) {
this.deltaY = e.wheelDelta / (120 * devicePixelRatio);
} else {
this.deltaY = e.wheelDelta / 120;
}
}
}
}
public preventDefault(): void {
this.browserEvent?.preventDefault();
}
public preventDefault(): void {
this.browserEvent?.preventDefault();
}
public stopPropagation(): void {
this.browserEvent?.stopPropagation();
}
public stopPropagation(): void {
this.browserEvent?.stopPropagation();
}
}
+59 -59
View File
@@ -4,95 +4,95 @@
*--------------------------------------------------------------------------------------------*/
export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
return Math.min(Math.max(value, min), max);
}
export function rot(index: number, modulo: number): number {
return (modulo + (index % modulo)) % modulo;
return (modulo + (index % modulo)) % modulo;
}
export class Counter {
private _next = 0;
private _next = 0;
getNext(): number {
return this._next++;
}
getNext(): number {
return this._next++;
}
}
export class MovingAverage {
private _n = 1;
private _val = 0;
private _n = 1;
private _val = 0;
update(value: number): number {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this._val;
}
update(value: number): number {
this._val = this._val + (value - this._val) / this._n;
this._n += 1;
return this._val;
}
get value(): number {
return this._val;
}
get value(): number {
return this._val;
}
}
export class SlidingWindowAverage {
private _n: number = 0;
private _val = 0;
private _n: number = 0;
private _val = 0;
private readonly _values: number[] = [];
private _index: number = 0;
private _sum = 0;
private readonly _values: number[] = [];
private _index: number = 0;
private _sum = 0;
constructor(size: number) {
this._values = new Array(size);
this._values.fill(0, 0, size);
}
constructor(size: number) {
this._values = new Array(size);
this._values.fill(0, 0, size);
}
update(value: number): number {
const oldValue = this._values[this._index];
this._values[this._index] = value;
this._index = (this._index + 1) % this._values.length;
update(value: number): number {
const oldValue = this._values[this._index];
this._values[this._index] = value;
this._index = (this._index + 1) % this._values.length;
this._sum -= oldValue;
this._sum += value;
this._sum -= oldValue;
this._sum += value;
if (this._n < this._values.length) {
this._n += 1;
}
if (this._n < this._values.length) {
this._n += 1;
}
this._val = this._sum / this._n;
return this._val;
}
this._val = this._sum / this._n;
return this._val;
}
get value(): number {
return this._val;
}
get value(): number {
return this._val;
}
}
/** Returns whether the point is within the triangle formed by the following 6 x/y point pairs */
export function isPointWithinTriangle(
x: number, y: number,
ax: number, ay: number,
bx: number, by: number,
cx: number, cy: number
x: number, y: number,
ax: number, ay: number,
bx: number, by: number,
cx: number, cy: number
) {
const v0x = cx - ax;
const v0y = cy - ay;
const v1x = bx - ax;
const v1y = by - ay;
const v2x = x - ax;
const v2y = y - ay;
const v0x = cx - ax;
const v0y = cy - ay;
const v1x = bx - ax;
const v1y = by - ay;
const v2x = x - ax;
const v2y = y - ay;
const dot00 = v0x * v0x + v0y * v0y;
const dot01 = v0x * v1x + v0y * v1y;
const dot02 = v0x * v2x + v0y * v2y;
const dot11 = v1x * v1x + v1y * v1y;
const dot12 = v1x * v2x + v1y * v2y;
const dot00 = v0x * v0x + v0y * v0y;
const dot01 = v0x * v1x + v0y * v1y;
const dot02 = v0x * v2x + v0y * v2y;
const dot11 = v1x * v1x + v1y * v1y;
const dot12 = v1x * v2x + v1y * v2y;
const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
const invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
const u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return u >= 0 && v >= 0 && u + v < 1;
return u >= 0 && v >= 0 && u + v < 1;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+150 -150
View File
@@ -6,160 +6,160 @@
import { ScrollbarVisibility } from './scrollable';
export interface ScrollableElementCreationOptions {
/**
* The scrollable element should not do any DOM mutations until renderNow() is called.
* Defaults to false.
*/
lazyRender?: boolean;
/**
* CSS Class name for the scrollable element.
*/
className?: string;
/**
* Drop subtle horizontal and vertical shadows.
* Defaults to false.
*/
useShadows?: boolean;
/**
* Handle mouse wheel (listen to mouse wheel scrolling).
* Defaults to true
*/
handleMouseWheel?: boolean;
/**
* If mouse wheel is handled, make mouse wheel scrolling smooth.
* Defaults to true.
*/
mouseWheelSmoothScroll?: boolean;
/**
* Flip axes. Treat vertical scrolling like horizontal and vice-versa.
* Defaults to false.
*/
flipAxes?: boolean;
/**
* If enabled, will scroll horizontally when scrolling vertical.
* Defaults to false.
*/
scrollYToX?: boolean;
/**
* Consume all mouse wheel events if a scrollbar is needed (i.e. scrollSize > size).
* Defaults to false.
*/
consumeMouseWheelIfScrollbarIsNeeded?: boolean;
/**
* Always consume mouse wheel events, even when scrolling is no longer possible.
* Defaults to false.
*/
alwaysConsumeMouseWheel?: boolean;
/**
* A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.
* Defaults to 1.
*/
mouseWheelScrollSensitivity?: number;
/**
* FastScrolling mulitplier speed when pressing `Alt`
* Defaults to 5.
*/
fastScrollSensitivity?: number;
/**
* Whether the scrollable will only scroll along the predominant axis when scrolling both
* vertically and horizontally at the same time.
* Prevents horizontal drift when scrolling vertically on a trackpad.
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right).
* Defaults to 11.
*/
arrowSize?: number;
/**
* The dom node events should be bound to.
* If no listenOnDomNode is provided, the dom node passed to the constructor will be used for event listening.
*/
listenOnDomNode?: HTMLElement;
/**
* Control the visibility of the horizontal scrollbar.
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
* Defaults to 'auto'.
*/
horizontal?: ScrollbarVisibility;
/**
* Height (in px) of the horizontal scrollbar.
* Defaults to 10.
*/
horizontalScrollbarSize?: number;
/**
* Height (in px) of the horizontal scrollbar slider.
* Defaults to `horizontalScrollbarSize`
*/
horizontalSliderSize?: number;
/**
* Render arrows (left/right) for the horizontal scrollbar.
* Defaults to false.
*/
horizontalHasArrows?: boolean;
/**
* Control the visibility of the vertical scrollbar.
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
* Defaults to 'auto'.
*/
vertical?: ScrollbarVisibility;
/**
* Width (in px) of the vertical scrollbar.
* Defaults to 10.
*/
verticalScrollbarSize?: number;
/**
* Width (in px) of the vertical scrollbar slider.
* Defaults to `verticalScrollbarSize`
*/
verticalSliderSize?: number;
/**
* Render arrows (top/bottom) for the vertical scrollbar.
* Defaults to false.
*/
verticalHasArrows?: boolean;
/**
* Scroll gutter clicks move by page vs. jump to position.
* Defaults to false.
*/
scrollByPage?: boolean;
/**
* The scrollable element should not do any DOM mutations until renderNow() is called.
* Defaults to false.
*/
lazyRender?: boolean;
/**
* CSS Class name for the scrollable element.
*/
className?: string;
/**
* Drop subtle horizontal and vertical shadows.
* Defaults to false.
*/
useShadows?: boolean;
/**
* Handle mouse wheel (listen to mouse wheel scrolling).
* Defaults to true
*/
handleMouseWheel?: boolean;
/**
* If mouse wheel is handled, make mouse wheel scrolling smooth.
* Defaults to true.
*/
mouseWheelSmoothScroll?: boolean;
/**
* Flip axes. Treat vertical scrolling like horizontal and vice-versa.
* Defaults to false.
*/
flipAxes?: boolean;
/**
* If enabled, will scroll horizontally when scrolling vertical.
* Defaults to false.
*/
scrollYToX?: boolean;
/**
* Consume all mouse wheel events if a scrollbar is needed (i.e. scrollSize > size).
* Defaults to false.
*/
consumeMouseWheelIfScrollbarIsNeeded?: boolean;
/**
* Always consume mouse wheel events, even when scrolling is no longer possible.
* Defaults to false.
*/
alwaysConsumeMouseWheel?: boolean;
/**
* A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.
* Defaults to 1.
*/
mouseWheelScrollSensitivity?: number;
/**
* FastScrolling mulitplier speed when pressing `Alt`
* Defaults to 5.
*/
fastScrollSensitivity?: number;
/**
* Whether the scrollable will only scroll along the predominant axis when scrolling both
* vertically and horizontally at the same time.
* Prevents horizontal drift when scrolling vertically on a trackpad.
* Defaults to true.
*/
scrollPredominantAxis?: boolean;
/**
* Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right).
* Defaults to 11.
*/
arrowSize?: number;
/**
* The dom node events should be bound to.
* If no listenOnDomNode is provided, the dom node passed to the constructor will be used for event listening.
*/
listenOnDomNode?: HTMLElement;
/**
* Control the visibility of the horizontal scrollbar.
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
* Defaults to 'auto'.
*/
horizontal?: ScrollbarVisibility;
/**
* Height (in px) of the horizontal scrollbar.
* Defaults to 10.
*/
horizontalScrollbarSize?: number;
/**
* Height (in px) of the horizontal scrollbar slider.
* Defaults to `horizontalScrollbarSize`
*/
horizontalSliderSize?: number;
/**
* Render arrows (left/right) for the horizontal scrollbar.
* Defaults to false.
*/
horizontalHasArrows?: boolean;
/**
* Control the visibility of the vertical scrollbar.
* Accepted values: 'auto' (on mouse over), 'visible' (always visible), 'hidden' (never visible)
* Defaults to 'auto'.
*/
vertical?: ScrollbarVisibility;
/**
* Width (in px) of the vertical scrollbar.
* Defaults to 10.
*/
verticalScrollbarSize?: number;
/**
* Width (in px) of the vertical scrollbar slider.
* Defaults to `verticalScrollbarSize`
*/
verticalSliderSize?: number;
/**
* Render arrows (top/bottom) for the vertical scrollbar.
* Defaults to false.
*/
verticalHasArrows?: boolean;
/**
* Scroll gutter clicks move by page vs. jump to position.
* Defaults to false.
*/
scrollByPage?: boolean;
}
export interface ScrollableElementChangeOptions {
handleMouseWheel?: boolean;
mouseWheelScrollSensitivity?: number;
fastScrollSensitivity?: number;
scrollPredominantAxis?: boolean;
horizontal?: ScrollbarVisibility;
horizontalScrollbarSize?: number;
vertical?: ScrollbarVisibility;
verticalScrollbarSize?: number;
scrollByPage?: boolean;
handleMouseWheel?: boolean;
mouseWheelScrollSensitivity?: number;
fastScrollSensitivity?: number;
scrollPredominantAxis?: boolean;
horizontal?: ScrollbarVisibility;
horizontalScrollbarSize?: number;
vertical?: ScrollbarVisibility;
verticalScrollbarSize?: number;
scrollByPage?: boolean;
}
export interface ScrollableElementResolvedOptions {
lazyRender: boolean;
className: string;
useShadows: boolean;
handleMouseWheel: boolean;
flipAxes: boolean;
scrollYToX: boolean;
consumeMouseWheelIfScrollbarIsNeeded: boolean;
alwaysConsumeMouseWheel: boolean;
mouseWheelScrollSensitivity: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
mouseWheelSmoothScroll: boolean;
arrowSize: number;
listenOnDomNode: HTMLElement | null;
horizontal: ScrollbarVisibility;
horizontalScrollbarSize: number;
horizontalSliderSize: number;
horizontalHasArrows: boolean;
vertical: ScrollbarVisibility;
verticalScrollbarSize: number;
verticalSliderSize: number;
verticalHasArrows: boolean;
scrollByPage: boolean;
lazyRender: boolean;
className: string;
useShadows: boolean;
handleMouseWheel: boolean;
flipAxes: boolean;
scrollYToX: boolean;
consumeMouseWheelIfScrollbarIsNeeded: boolean;
alwaysConsumeMouseWheel: boolean;
mouseWheelScrollSensitivity: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
mouseWheelSmoothScroll: boolean;
arrowSize: number;
listenOnDomNode: HTMLElement | null;
horizontal: ScrollbarVisibility;
horizontalScrollbarSize: number;
horizontalSliderSize: number;
horizontalHasArrows: boolean;
vertical: ScrollbarVisibility;
verticalScrollbarSize: number;
verticalSliderSize: number;
verticalHasArrows: boolean;
scrollByPage: boolean;
}
+81 -81
View File
@@ -14,101 +14,101 @@ import * as dom from './dom';
export const ARROW_IMG_SIZE = 11;
export interface ScrollbarArrowOptions {
onActivate: () => void;
className: string;
// icon: ThemeIcon;
onActivate: () => void;
className: string;
// icon: ThemeIcon;
bgWidth: number;
bgHeight: number;
bgWidth: number;
bgHeight: number;
top?: number;
left?: number;
bottom?: number;
right?: number;
top?: number;
left?: number;
bottom?: number;
right?: number;
}
export class ScrollbarArrow extends Widget {
private _onActivate: () => void;
public bgDomNode: HTMLElement;
public domNode: HTMLElement;
private _pointerdownRepeatTimer: dom.WindowIntervalTimer;
private _pointerdownScheduleRepeatTimer: TimeoutTimer;
private _pointerMoveMonitor: GlobalPointerMoveMonitor;
private _onActivate: () => void;
public bgDomNode: HTMLElement;
public domNode: HTMLElement;
private _pointerdownRepeatTimer: dom.WindowIntervalTimer;
private _pointerdownScheduleRepeatTimer: TimeoutTimer;
private _pointerMoveMonitor: GlobalPointerMoveMonitor;
constructor(opts: ScrollbarArrowOptions) {
super();
this._onActivate = opts.onActivate;
constructor(opts: ScrollbarArrowOptions) {
super();
this._onActivate = opts.onActivate;
this.bgDomNode = document.createElement('div');
this.bgDomNode.className = 'arrow-background';
this.bgDomNode.style.position = 'absolute';
this.bgDomNode.style.width = opts.bgWidth + 'px';
this.bgDomNode.style.height = opts.bgHeight + 'px';
if (typeof opts.top !== 'undefined') {
this.bgDomNode.style.top = '0px';
}
if (typeof opts.left !== 'undefined') {
this.bgDomNode.style.left = '0px';
}
if (typeof opts.bottom !== 'undefined') {
this.bgDomNode.style.bottom = '0px';
}
if (typeof opts.right !== 'undefined') {
this.bgDomNode.style.right = '0px';
}
this.bgDomNode = document.createElement('div');
this.bgDomNode.className = 'arrow-background';
this.bgDomNode.style.position = 'absolute';
this.bgDomNode.style.width = opts.bgWidth + 'px';
this.bgDomNode.style.height = opts.bgHeight + 'px';
if (typeof opts.top !== 'undefined') {
this.bgDomNode.style.top = '0px';
}
if (typeof opts.left !== 'undefined') {
this.bgDomNode.style.left = '0px';
}
if (typeof opts.bottom !== 'undefined') {
this.bgDomNode.style.bottom = '0px';
}
if (typeof opts.right !== 'undefined') {
this.bgDomNode.style.right = '0px';
}
this.domNode = document.createElement('div');
this.domNode.className = opts.className;
// this.domNode.classList.add(...ThemeIcon.asClassNameArray(opts.icon));
this.domNode = document.createElement('div');
this.domNode.className = opts.className;
// this.domNode.classList.add(...ThemeIcon.asClassNameArray(opts.icon));
this.domNode.style.position = 'absolute';
this.domNode.style.width = ARROW_IMG_SIZE + 'px';
this.domNode.style.height = ARROW_IMG_SIZE + 'px';
if (typeof opts.top !== 'undefined') {
this.domNode.style.top = opts.top + 'px';
}
if (typeof opts.left !== 'undefined') {
this.domNode.style.left = opts.left + 'px';
}
if (typeof opts.bottom !== 'undefined') {
this.domNode.style.bottom = opts.bottom + 'px';
}
if (typeof opts.right !== 'undefined') {
this.domNode.style.right = opts.right + 'px';
}
this.domNode.style.position = 'absolute';
this.domNode.style.width = ARROW_IMG_SIZE + 'px';
this.domNode.style.height = ARROW_IMG_SIZE + 'px';
if (typeof opts.top !== 'undefined') {
this.domNode.style.top = opts.top + 'px';
}
if (typeof opts.left !== 'undefined') {
this.domNode.style.left = opts.left + 'px';
}
if (typeof opts.bottom !== 'undefined') {
this.domNode.style.bottom = opts.bottom + 'px';
}
if (typeof opts.right !== 'undefined') {
this.domNode.style.right = opts.right + 'px';
}
this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor());
this._register(dom.addStandardDisposableListener(this.bgDomNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e)));
this._register(dom.addStandardDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e)));
this._pointerMoveMonitor = this._register(new GlobalPointerMoveMonitor());
this._register(dom.addStandardDisposableListener(this.bgDomNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e)));
this._register(dom.addStandardDisposableListener(this.domNode, dom.EventType.POINTER_DOWN, (e) => this._arrowPointerDown(e)));
this._pointerdownRepeatTimer = this._register(new dom.WindowIntervalTimer());
this._pointerdownScheduleRepeatTimer = this._register(new TimeoutTimer());
}
this._pointerdownRepeatTimer = this._register(new dom.WindowIntervalTimer());
this._pointerdownScheduleRepeatTimer = this._register(new TimeoutTimer());
}
private _arrowPointerDown(e: PointerEvent): void {
if (!e.target || !(e.target instanceof Element)) {
return;
}
const scheduleRepeater = () => {
this._pointerdownRepeatTimer.cancelAndSet(() => this._onActivate(), 1000 / 24, dom.getWindow(e));
};
private _arrowPointerDown(e: PointerEvent): void {
if (!e.target || !(e.target instanceof Element)) {
return;
}
const scheduleRepeater = () => {
this._pointerdownRepeatTimer.cancelAndSet(() => this._onActivate(), 1000 / 24, dom.getWindow(e));
};
this._onActivate();
this._pointerdownRepeatTimer.cancel();
this._pointerdownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200);
this._onActivate();
this._pointerdownRepeatTimer.cancel();
this._pointerdownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200);
this._pointerMoveMonitor.startMonitoring(
e.target,
e.pointerId,
e.buttons,
(pointerMoveData) => { /* Intentional empty */ },
() => {
this._pointerdownRepeatTimer.cancel();
this._pointerdownScheduleRepeatTimer.cancel();
}
);
this._pointerMoveMonitor.startMonitoring(
e.target,
e.pointerId,
e.buttons,
(pointerMoveData) => { /* Intentional empty */ },
() => {
this._pointerdownRepeatTimer.cancel();
this._pointerdownScheduleRepeatTimer.cancel();
}
);
e.preventDefault();
}
e.preventDefault();
}
}
+173 -173
View File
@@ -10,214 +10,214 @@ const MINIMUM_SLIDER_SIZE = 20;
export class ScrollbarState {
/**
* For the vertical scrollbar: the width.
* For the horizontal scrollbar: the height.
*/
private _scrollbarSize: number;
/**
* For the vertical scrollbar: the width.
* For the horizontal scrollbar: the height.
*/
private _scrollbarSize: number;
/**
* For the vertical scrollbar: the height of the pair horizontal scrollbar.
* For the horizontal scrollbar: the width of the pair vertical scrollbar.
*/
private _oppositeScrollbarSize: number;
/**
* For the vertical scrollbar: the height of the pair horizontal scrollbar.
* For the horizontal scrollbar: the width of the pair vertical scrollbar.
*/
private _oppositeScrollbarSize: number;
/**
* For the vertical scrollbar: the height of the scrollbar's arrows.
* For the horizontal scrollbar: the width of the scrollbar's arrows.
*/
private readonly _arrowSize: number;
/**
* For the vertical scrollbar: the height of the scrollbar's arrows.
* For the horizontal scrollbar: the width of the scrollbar's arrows.
*/
private readonly _arrowSize: number;
// --- variables
/**
* For the vertical scrollbar: the viewport height.
* For the horizontal scrollbar: the viewport width.
*/
private _visibleSize: number;
// --- variables
/**
* For the vertical scrollbar: the viewport height.
* For the horizontal scrollbar: the viewport width.
*/
private _visibleSize: number;
/**
* For the vertical scrollbar: the scroll height.
* For the horizontal scrollbar: the scroll width.
*/
private _scrollSize: number;
/**
* For the vertical scrollbar: the scroll height.
* For the horizontal scrollbar: the scroll width.
*/
private _scrollSize: number;
/**
* For the vertical scrollbar: the scroll top.
* For the horizontal scrollbar: the scroll left.
*/
private _scrollPosition: number;
/**
* For the vertical scrollbar: the scroll top.
* For the horizontal scrollbar: the scroll left.
*/
private _scrollPosition: number;
// --- computed variables
// --- computed variables
/**
* `visibleSize` - `oppositeScrollbarSize`
*/
private _computedAvailableSize: number;
/**
* (`scrollSize` > 0 && `scrollSize` > `visibleSize`)
*/
private _computedIsNeeded: boolean;
/**
* `visibleSize` - `oppositeScrollbarSize`
*/
private _computedAvailableSize: number;
/**
* (`scrollSize` > 0 && `scrollSize` > `visibleSize`)
*/
private _computedIsNeeded: boolean;
private _computedSliderSize: number;
private _computedSliderRatio: number;
private _computedSliderPosition: number;
private _computedSliderSize: number;
private _computedSliderRatio: number;
private _computedSliderPosition: number;
constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
this._scrollbarSize = Math.round(scrollbarSize);
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
this._arrowSize = Math.round(arrowSize);
constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
this._scrollbarSize = Math.round(scrollbarSize);
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
this._arrowSize = Math.round(arrowSize);
this._visibleSize = visibleSize;
this._scrollSize = scrollSize;
this._scrollPosition = scrollPosition;
this._visibleSize = visibleSize;
this._scrollSize = scrollSize;
this._scrollPosition = scrollPosition;
this._computedAvailableSize = 0;
this._computedIsNeeded = false;
this._computedSliderSize = 0;
this._computedSliderRatio = 0;
this._computedSliderPosition = 0;
this._computedAvailableSize = 0;
this._computedIsNeeded = false;
this._computedSliderSize = 0;
this._computedSliderRatio = 0;
this._computedSliderPosition = 0;
this._refreshComputedValues();
}
this._refreshComputedValues();
}
public clone(): ScrollbarState {
return new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize, this._visibleSize, this._scrollSize, this._scrollPosition);
}
public clone(): ScrollbarState {
return new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize, this._visibleSize, this._scrollSize, this._scrollPosition);
}
public setVisibleSize(visibleSize: number): boolean {
const iVisibleSize = Math.round(visibleSize);
if (this._visibleSize !== iVisibleSize) {
this._visibleSize = iVisibleSize;
this._refreshComputedValues();
return true;
}
return false;
}
public setVisibleSize(visibleSize: number): boolean {
const iVisibleSize = Math.round(visibleSize);
if (this._visibleSize !== iVisibleSize) {
this._visibleSize = iVisibleSize;
this._refreshComputedValues();
return true;
}
return false;
}
public setScrollSize(scrollSize: number): boolean {
const iScrollSize = Math.round(scrollSize);
if (this._scrollSize !== iScrollSize) {
this._scrollSize = iScrollSize;
this._refreshComputedValues();
return true;
}
return false;
}
public setScrollSize(scrollSize: number): boolean {
const iScrollSize = Math.round(scrollSize);
if (this._scrollSize !== iScrollSize) {
this._scrollSize = iScrollSize;
this._refreshComputedValues();
return true;
}
return false;
}
public setScrollPosition(scrollPosition: number): boolean {
const iScrollPosition = Math.round(scrollPosition);
if (this._scrollPosition !== iScrollPosition) {
this._scrollPosition = iScrollPosition;
this._refreshComputedValues();
return true;
}
return false;
}
public setScrollPosition(scrollPosition: number): boolean {
const iScrollPosition = Math.round(scrollPosition);
if (this._scrollPosition !== iScrollPosition) {
this._scrollPosition = iScrollPosition;
this._refreshComputedValues();
return true;
}
return false;
}
public setScrollbarSize(scrollbarSize: number): void {
this._scrollbarSize = Math.round(scrollbarSize);
}
public setScrollbarSize(scrollbarSize: number): void {
this._scrollbarSize = Math.round(scrollbarSize);
}
public setOppositeScrollbarSize(oppositeScrollbarSize: number): void {
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
}
public setOppositeScrollbarSize(oppositeScrollbarSize: number): void {
this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize);
}
private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize);
const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize);
const computedIsNeeded = (scrollSize > 0 && scrollSize > visibleSize);
private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize);
const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize);
const computedIsNeeded = (scrollSize > 0 && scrollSize > visibleSize);
if (!computedIsNeeded) {
return {
computedAvailableSize: Math.round(computedAvailableSize),
computedIsNeeded: computedIsNeeded,
computedSliderSize: Math.round(computedRepresentableSize),
computedSliderRatio: 0,
computedSliderPosition: 0,
};
}
if (!computedIsNeeded) {
return {
computedAvailableSize: Math.round(computedAvailableSize),
computedIsNeeded: computedIsNeeded,
computedSliderSize: Math.round(computedRepresentableSize),
computedSliderRatio: 0,
computedSliderPosition: 0,
};
}
const computedSliderSize = Math.round(Math.max(MINIMUM_SLIDER_SIZE, Math.floor(visibleSize * computedRepresentableSize / scrollSize)));
const computedSliderSize = Math.round(Math.max(MINIMUM_SLIDER_SIZE, Math.floor(visibleSize * computedRepresentableSize / scrollSize)));
const computedSliderRatio = (computedRepresentableSize - computedSliderSize) / (scrollSize - visibleSize);
const computedSliderPosition = (scrollPosition * computedSliderRatio);
const computedSliderRatio = (computedRepresentableSize - computedSliderSize) / (scrollSize - visibleSize);
const computedSliderPosition = (scrollPosition * computedSliderRatio);
return {
computedAvailableSize: Math.round(computedAvailableSize),
computedIsNeeded: computedIsNeeded,
computedSliderSize: Math.round(computedSliderSize),
computedSliderRatio: computedSliderRatio,
computedSliderPosition: Math.round(computedSliderPosition),
};
}
return {
computedAvailableSize: Math.round(computedAvailableSize),
computedIsNeeded: computedIsNeeded,
computedSliderSize: Math.round(computedSliderSize),
computedSliderRatio: computedSliderRatio,
computedSliderPosition: Math.round(computedSliderPosition),
};
}
private _refreshComputedValues(): void {
const r = ScrollbarState._computeValues(this._oppositeScrollbarSize, this._arrowSize, this._visibleSize, this._scrollSize, this._scrollPosition);
this._computedAvailableSize = r.computedAvailableSize;
this._computedIsNeeded = r.computedIsNeeded;
this._computedSliderSize = r.computedSliderSize;
this._computedSliderRatio = r.computedSliderRatio;
this._computedSliderPosition = r.computedSliderPosition;
}
private _refreshComputedValues(): void {
const r = ScrollbarState._computeValues(this._oppositeScrollbarSize, this._arrowSize, this._visibleSize, this._scrollSize, this._scrollPosition);
this._computedAvailableSize = r.computedAvailableSize;
this._computedIsNeeded = r.computedIsNeeded;
this._computedSliderSize = r.computedSliderSize;
this._computedSliderRatio = r.computedSliderRatio;
this._computedSliderPosition = r.computedSliderPosition;
}
public getArrowSize(): number {
return this._arrowSize;
}
public getArrowSize(): number {
return this._arrowSize;
}
public getScrollPosition(): number {
return this._scrollPosition;
}
public getScrollPosition(): number {
return this._scrollPosition;
}
public getRectangleLargeSize(): number {
return this._computedAvailableSize;
}
public getRectangleLargeSize(): number {
return this._computedAvailableSize;
}
public getRectangleSmallSize(): number {
return this._scrollbarSize;
}
public getRectangleSmallSize(): number {
return this._scrollbarSize;
}
public isNeeded(): boolean {
return this._computedIsNeeded;
}
public isNeeded(): boolean {
return this._computedIsNeeded;
}
public getSliderSize(): number {
return this._computedSliderSize;
}
public getSliderSize(): number {
return this._computedSliderSize;
}
public getSliderPosition(): number {
return this._computedSliderPosition;
}
public getSliderPosition(): number {
return this._computedSliderPosition;
}
public getDesiredScrollPositionFromOffset(offset: number): number {
if (!this._computedIsNeeded) {
return 0;
}
public getDesiredScrollPositionFromOffset(offset: number): number {
if (!this._computedIsNeeded) {
return 0;
}
const desiredSliderPosition = offset - this._arrowSize - this._computedSliderSize / 2;
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}
const desiredSliderPosition = offset - this._arrowSize - this._computedSliderSize / 2;
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}
public getDesiredScrollPositionFromOffsetPaged(offset: number): number {
if (!this._computedIsNeeded) {
return 0;
}
public getDesiredScrollPositionFromOffsetPaged(offset: number): number {
if (!this._computedIsNeeded) {
return 0;
}
const correctedOffset = offset - this._arrowSize;
let desiredScrollPosition = this._scrollPosition;
if (correctedOffset < this._computedSliderPosition) {
desiredScrollPosition -= this._visibleSize;
} else {
desiredScrollPosition += this._visibleSize;
}
return desiredScrollPosition;
}
const correctedOffset = offset - this._arrowSize;
let desiredScrollPosition = this._scrollPosition;
if (correctedOffset < this._computedSliderPosition) {
desiredScrollPosition -= this._visibleSize;
} else {
desiredScrollPosition += this._visibleSize;
}
return desiredScrollPosition;
}
public getDesiredScrollPositionFromDelta(delta: number): number {
if (!this._computedIsNeeded) {
return 0;
}
public getDesiredScrollPositionFromDelta(delta: number): number {
if (!this._computedIsNeeded) {
return 0;
}
const desiredSliderPosition = this._computedSliderPosition + delta;
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}
const desiredSliderPosition = this._computedSliderPosition + delta;
return Math.round(desiredSliderPosition / this._computedSliderRatio);
}
}

Some files were not shown because too many files have changed in this diff Show More