Skip to content

Commit

Permalink
More cleanup in ScrollableElement
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Apr 29, 2016
1 parent a322bf0 commit 28438e2
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 342 deletions.
158 changes: 37 additions & 121 deletions src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,154 +7,68 @@
import * as Browser from 'vs/base/browser/browser';
import * as Platform from 'vs/base/common/platform';
import * as DomUtils from 'vs/base/browser/dom';
import {IMouseEvent, StandardMouseEvent} from 'vs/base/browser/mouseEvent';
import {IParent, Visibility} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {Disposable} from 'vs/base/common/lifecycle';
import {IMouseEvent, StandardMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
import {Visibility} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger} from 'vs/base/browser/globalMouseMoveMonitor';
import {Widget} from 'vs/base/browser/ui/widget';
import {TimeoutTimer} from 'vs/base/common/async';
import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
import {ScrollbarArrow, IMouseWheelEventFactory} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import {ScrollbarArrow, ScrollbarArrowOptions} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';
import {ScrollbarVisibilityController} from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController';
import {DelegateScrollable} from 'vs/base/common/scrollable';

/**
* The orthogonal distance to the slider at which dragging "resets". This implements "snapping"
*/
const MOUSE_DRAG_RESET_DISTANCE = 140;

class VisibilityController extends Disposable {
private _visibility: Visibility;
private _visibleClassName: string;
private _invisibleClassName: string;
private _domNode: FastDomNode;
private _shouldBeVisible: boolean;
private _isNeeded: boolean;
private _isVisible: boolean;
private _revealTimer: TimeoutTimer;

constructor(visibility: Visibility, visibleClassName: string, invisibleClassName: string) {
super();
this._visibility = visibility;
this._visibleClassName = visibleClassName;
this._invisibleClassName = invisibleClassName;
this._domNode = null;
this._isVisible = false;
this._isNeeded = false;
this._shouldBeVisible = false;
this._revealTimer = this._register(new TimeoutTimer());
}

// ----------------- Hide / Reveal

private applyVisibilitySetting(shouldBeVisible: boolean): boolean {
if (this._visibility === Visibility.Hidden) {
return false;
}
if (this._visibility === Visibility.Visible) {
return true;
}
return shouldBeVisible;
}

public setShouldBeVisible(rawShouldBeVisible: boolean): void {
let shouldBeVisible = this.applyVisibilitySetting(rawShouldBeVisible);

if (this._shouldBeVisible !== shouldBeVisible) {
this._shouldBeVisible = shouldBeVisible;
this.ensureVisibility();
}
}

public setIsNeeded(isNeeded: boolean): void {
if (this._isNeeded !== isNeeded) {
this._isNeeded = isNeeded;
this.ensureVisibility();
}
}

public setDomNode(domNode: FastDomNode): void {
this._domNode = domNode;
this._domNode.setClassName(this._invisibleClassName);

// Now that the flags & the dom node are in a consistent state, ensure the Hidden/Visible configuration
this.setShouldBeVisible(false);
}

public ensureVisibility(): void {

if (!this._isNeeded) {
// Nothing to be rendered
this._hide(false);
return;
}

if (this._shouldBeVisible) {
this._reveal();
} else {
this._hide(true);
}
}


private _reveal(): void {
if (this._isVisible) {
return;
}
this._isVisible = true;

// The CSS animation doesn't play otherwise
this._revealTimer.setIfNotSet(() => {
this._domNode.setClassName(this._visibleClassName);
}, 0);
}

private _hide(withFadeAway: boolean): void {
this._revealTimer.cancel();
if (!this._isVisible) {
return;
}
this._isVisible = false;
this._domNode.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : ''));
}
}

export interface IMouseMoveEventData {
leftButton: boolean;
posx: number;
posy: number;
}

export interface ScrollbarHost {
onMouseWheel(mouseWheelEvent: StandardMouseWheelEvent): void;
onDragStart(): void;
onDragEnd(): void;
}

export interface AbstractScrollbarOptions {
forbidTranslate3dUse: boolean;
lazyRender:boolean;
host: ScrollbarHost;
scrollbarState: ScrollbarState;
visibility: Visibility;
extraScrollbarClassName: string;
scrollable: DelegateScrollable;
}

export abstract class AbstractScrollbar extends Widget {

protected _forbidTranslate3dUse: boolean;
protected _host: ScrollbarHost;
protected _scrollable: DelegateScrollable;
private _lazyRender: boolean;
private _parent: IParent;
private _scrollbarState: ScrollbarState;
private _visibilityController: VisibilityController;
private _visibilityController: ScrollbarVisibilityController;
private _mouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;

public domNode: FastDomNode;
public slider: FastDomNode;

protected _shouldRender: boolean;

constructor(forbidTranslate3dUse: boolean, lazyRender:boolean, parent: IParent, scrollbarState: ScrollbarState, visibility: Visibility, extraScrollbarClassName: string) {
constructor(opts:AbstractScrollbarOptions) {
super();
this._forbidTranslate3dUse = forbidTranslate3dUse;
this._lazyRender = lazyRender;
this._parent = parent;
this._scrollbarState = scrollbarState;
this._visibilityController = this._register(new VisibilityController(visibility, 'visible scrollbar ' + extraScrollbarClassName, 'invisible scrollbar ' + extraScrollbarClassName));
this._forbidTranslate3dUse = opts.forbidTranslate3dUse;
this._lazyRender = opts.lazyRender;
this._host = opts.host;
this._scrollable = opts.scrollable;
this._scrollbarState = opts.scrollbarState;
this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName));
this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
this._shouldRender = true;
}

// ----------------- initialize & clean-up

/**
* Creates the container dom node for the scrollbar & hooks up the events
*/
protected _createDomNode(): void {
this.domNode = createFastDomNode(document.createElement('div'));
if (!this._forbidTranslate3dUse && Browser.canUseTranslate3d) {
// Put the scrollbar in its own layer
Expand All @@ -167,11 +81,13 @@ export abstract class AbstractScrollbar extends Widget {
this.onmousedown(this.domNode.domNode, (e) => this._domNodeMouseDown(e));
}

// ----------------- creation

/**
* Creates the dom node for an arrow & adds it to the container
*/
protected _createArrow(className: string, top: number, left: number, bottom: number, right: number, bgWidth: number, bgHeight: number, mouseWheelEventFactory: IMouseWheelEventFactory): void {
let arrow = this._register(new ScrollbarArrow(className, top, left, bottom, right, bgWidth, bgHeight, mouseWheelEventFactory, this._parent));
protected _createArrow(opts:ScrollbarArrowOptions): void {
let arrow = this._register(new ScrollbarArrow(opts));
this.domNode.domNode.appendChild(arrow.bgDomNode);
this.domNode.domNode.appendChild(arrow.domNode);
}
Expand Down Expand Up @@ -301,12 +217,12 @@ export abstract class AbstractScrollbar extends Widget {
},
() => {
this.slider.toggleClassName('active', false);
this._parent.onDragEnd();
this._host.onDragEnd();
}
);

e.preventDefault();
this._parent.onDragStart();
this._host.onDragStart();
}
}

Expand Down
56 changes: 37 additions & 19 deletions src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,61 @@
'use strict';

import * as Browser from 'vs/base/browser/browser';
import {AbstractScrollbar, IMouseMoveEventData} from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
import {IDomNodePosition} from 'vs/base/browser/dom';
import {IParent, IScrollableElementOptions, Visibility} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {ScrollableElementResolvedOptions, Visibility} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {DelegateScrollable} from 'vs/base/common/scrollable';
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
import {ARROW_IMG_SIZE} from 'vs/base/browser/ui/scrollbar/scrollbarArrow';

export class HorizontalScrollbar extends AbstractScrollbar {

private _scrollable: DelegateScrollable;
constructor(scrollable: DelegateScrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) {
super({
forbidTranslate3dUse: options.forbidTranslate3dUse,
lazyRender: options.lazyRender,
host: host,
scrollbarState: new ScrollbarState(
(options.horizontalHasArrows ? options.arrowSize : 0),
(options.horizontal === Visibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === Visibility.Hidden ? 0 : options.verticalScrollbarSize)
),
visibility: options.horizontal,
extraScrollbarClassName: 'horizontal',
scrollable: scrollable
});

constructor(scrollable: DelegateScrollable, parent: IParent, options: IScrollableElementOptions) {
let s = new ScrollbarState(
(options.horizontalHasArrows ? options.arrowSize : 0),
(options.horizontal === Visibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === Visibility.Hidden ? 0 : options.verticalScrollbarSize)
);
super(options.forbidTranslate3dUse, options.lazyRender, parent, s, options.horizontal, 'horizontal');
this._scrollable = scrollable;

this._createDomNode();
if (options.horizontalHasArrows) {
let arrowDelta = (options.arrowSize - ARROW_IMG_SIZE) / 2;
let scrollbarDelta = (options.horizontalScrollbarSize - ARROW_IMG_SIZE) / 2;

this._createArrow('left-arrow', scrollbarDelta, arrowDelta, null, null, options.arrowSize, options.horizontalScrollbarSize, () => this._createMouseWheelEvent(1));
this._createArrow('right-arrow', scrollbarDelta, null, null, arrowDelta, options.arrowSize, options.horizontalScrollbarSize, () => this._createMouseWheelEvent(-1));
this._createArrow({
className: 'left-arrow',
top: scrollbarDelta,
left: arrowDelta,
bottom: void 0,
right: void 0,
bgWidth: options.arrowSize,
bgHeight: options.horizontalScrollbarSize,
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 1, 0)),
});

this._createArrow({
className: 'right-arrow',
top: scrollbarDelta,
left: void 0,
bottom: void 0,
right: arrowDelta,
bgWidth: options.arrowSize,
bgHeight: options.horizontalScrollbarSize,
onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, -1, 0)),
});
}

this._createSlider(Math.floor((options.horizontalScrollbarSize - options.horizontalSliderSize) / 2), 0, null, options.horizontalSliderSize);
}

protected _createMouseWheelEvent(sign: number) {
return new StandardMouseWheelEvent(null, sign, 0);
}

protected _updateSlider(sliderSize: number, sliderPosition: number): void {
this.slider.setWidth(sliderSize);
if (!this._forbidTranslate3dUse && Browser.canUseTranslate3d) {
Expand Down
Loading

0 comments on commit 28438e2

Please sign in to comment.