提交 58ab86d6 编写于 作者: A Alex Dima

Orchestrate scrollbar rendering (render after editor forced layout)

上级 b5bf0c7f
......@@ -377,6 +377,7 @@ export interface IMouseMoveEventData {
export abstract class AbstractScrollbar extends Widget implements IScrollbar {
protected _forbidTranslate3dUse: boolean;
private _lazyRender: boolean;
private _parent: IParent;
private _scrollbarState: ScrollbarState;
private _visibilityController: VisibilityController;
......@@ -385,13 +386,17 @@ export abstract class AbstractScrollbar extends Widget implements IScrollbar {
public domNode: FastDomNode;
public slider: FastDomNode;
constructor(forbidTranslate3dUse: boolean, parent: IParent, scrollbarState: ScrollbarState, visibility: Visibility, extraScrollbarClassName: string) {
protected _shouldRender: boolean;
constructor(forbidTranslate3dUse: boolean, lazyRender:boolean, parent: IParent, scrollbarState: ScrollbarState, visibility: Visibility, extraScrollbarClassName: string) {
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._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor<IStandardMouseMoveEventData>());
this._shouldRender = true;
}
// ----------------- initialize & clean-up
......@@ -440,26 +445,37 @@ export abstract class AbstractScrollbar extends Widget implements IScrollbar {
// ----------------- Update state
public onElementSize(visibleSize: number) {
public onElementSize(visibleSize: number): boolean {
if (this._scrollbarState.setVisibleSize(visibleSize)) {
this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
this._renderSlider();
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
if (!this._lazyRender) {
this.render();
}
}
return this._shouldRender;
}
public onElementScrollSize(elementScrollSize: number): void {
public onElementScrollSize(elementScrollSize: number): boolean {
if (this._scrollbarState.setScrollSize(elementScrollSize)) {
this._renderSlider();
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
if (!this._lazyRender) {
this.render();
}
}
return this._shouldRender;
}
public onElementScrollPosition(elementScrollPosition: number): void {
public onElementScrollPosition(elementScrollPosition: number): boolean {
if (this._scrollbarState.setScrollPosition(elementScrollPosition)) {
this._renderSlider();
this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded());
this._shouldRender = true;
if (!this._lazyRender) {
this.render();
}
}
return this._shouldRender;
}
// ----------------- rendering
......@@ -472,10 +488,15 @@ export abstract class AbstractScrollbar extends Widget implements IScrollbar {
this._visibilityController.setShouldBeVisible(false);
}
private _renderSlider(): void {
public render(): void {
if (!this._shouldRender) {
return;
}
this._shouldRender = false;
this._renderDomNode(this._scrollbarState.getRectangleLargeSize(), this._scrollbarState.getRectangleSmallSize());
this._updateSlider(this._scrollbarState.getSliderSize(), this._scrollbarState.getArrowSize() + this._scrollbarState.getSliderPosition());
}
// ----------------- DOM events
private _domNodeMouseDown(e: IMouseEvent): void {
......@@ -543,12 +564,18 @@ export abstract class AbstractScrollbar extends Widget implements IScrollbar {
return this._scrollbarState.validateScrollPosition(desiredScrollPosition);
}
public setDesiredScrollPosition(desiredScrollPosition: number): void {
public setDesiredScrollPosition(desiredScrollPosition: number): boolean {
desiredScrollPosition = this.validateScrollPosition(desiredScrollPosition);
let oldScrollPosition = this._getScrollPosition();
this._setScrollPosition(desiredScrollPosition);
this.onElementScrollPosition(desiredScrollPosition);
this._renderSlider();
let newScrollPosition = this._getScrollPosition();
if (oldScrollPosition !== newScrollPosition) {
this.onElementScrollPosition(this._getScrollPosition());
return true;
}
return false;
}
// ----------------- Overwrite these
......
......@@ -21,7 +21,7 @@ export class HorizontalScrollbar extends AbstractScrollbar {
(options.horizontal === Visibility.Hidden ? 0 : options.horizontalScrollbarSize),
(options.vertical === Visibility.Hidden ? 0 : options.verticalScrollbarSize)
);
super(options.forbidTranslate3dUse, parent, s, options.horizontal, 'horizontal');
super(options.forbidTranslate3dUse, options.lazyRender, parent, s, options.horizontal, 'horizontal');
this._scrollable = scrollable;
this._createDomNode();
......
......@@ -12,6 +12,12 @@ export interface IScrollableElementCreationOptions {
*/
forbidTranslate3dUse?: 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.
*/
......@@ -143,6 +149,12 @@ export interface IScrollableElement {
*/
dispose(): void;
/**
* Render / mutate the DOM now.
* Should be used together with the ctor option `lazyRender`.
*/
renderNow(): void;
/**
* Update the class name of the scrollable element.
*/
......@@ -176,15 +188,16 @@ export interface IMouseWheelEvent {
export interface IScrollbar {
domNode: FastDomNode;
dispose(): void;
slider: FastDomNode;
onElementSize(size: number): void;
onElementScrollSize(scrollSize: number): void;
onElementScrollPosition(scrollPosition: number): void;
onElementSize(size: number): boolean;
onElementScrollSize(scrollSize: number): boolean;
onElementScrollPosition(scrollPosition: number): boolean;
beginReveal(): void;
beginHide(): void;
delegateMouseDown(browserEvent: MouseEvent): void;
validateScrollPosition(scrollPosition: number): number;
setDesiredScrollPosition(scrollPosition: number): void;
setDesiredScrollPosition(scrollPosition: number): boolean;
render(): void;
}
export interface IParent {
......@@ -212,6 +225,7 @@ export function visibilityFromString(visibility: string): Visibility {
export interface IScrollableElementOptions {
forbidTranslate3dUse: boolean;
lazyRender: boolean;
className: string;
useShadows: boolean;
handleMouseWheel: boolean;
......
......@@ -19,13 +19,13 @@ import {IDisposable, disposeAll} from 'vs/base/common/lifecycle';
import {IScrollable, DelegateScrollable} from 'vs/base/common/scrollable';
import {Widget} from 'vs/base/browser/ui/widget';
import {TimeoutTimer} from 'vs/base/common/async';
import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
const HIDE_TIMEOUT = 500;
const SCROLL_WHEEL_SENSITIVITY = 50;
export class ScrollableElement extends Widget implements IScrollableElement {
private _originalElement: HTMLElement;
private _options: IScrollableElementOptions;
private _scrollable: DelegateScrollable;
public verticalScrollbarWidth: number;
......@@ -34,9 +34,10 @@ export class ScrollableElement extends Widget implements IScrollableElement {
private _horizontalScrollbar: IScrollbar;
private _domNode: HTMLElement;
private _leftShadowDomNode: HTMLElement;
private _topShadowDomNode: HTMLElement;
private _topLeftShadowDomNode: HTMLElement;
private _leftShadowDomNode: FastDomNode;
private _topShadowDomNode: FastDomNode;
private _topLeftShadowDomNode: FastDomNode;
private _listenOnDomNode: HTMLElement;
private _mouseWheelToDispose: IDisposable[];
......@@ -45,13 +46,12 @@ export class ScrollableElement extends Widget implements IScrollableElement {
private _isDragging: boolean;
private _mouseIsOver: boolean;
private _dimensions: IDimensions;
private _hideTimeout: TimeoutTimer;
private _shouldRender: boolean;
constructor(element: HTMLElement, scrollable:IScrollable, options: IScrollableElementCreationOptions, dimensions: IDimensions = null) {
super();
this._originalElement = element;
this._originalElement.style.overflow = 'hidden';
element.style.overflow = 'hidden';
this._options = this._createOptions(options);
this._scrollable = this._register(new DelegateScrollable(scrollable, () => this._onScroll()));
......@@ -67,24 +67,22 @@ export class ScrollableElement extends Widget implements IScrollableElement {
this._domNode.setAttribute('role', 'presentation');
this._domNode.style.position = 'relative';
this._domNode.style.overflow = 'hidden';
this._domNode.appendChild(this._originalElement);
this._domNode.appendChild(element);
this._domNode.appendChild(this._horizontalScrollbar.domNode.domNode);
this._domNode.appendChild(this._verticalScrollbar.domNode.domNode);
if (this._options.useShadows) {
this._leftShadowDomNode = document.createElement('div');
this._leftShadowDomNode.className = 'shadow';
this._domNode.appendChild(this._leftShadowDomNode);
}
this._leftShadowDomNode = createFastDomNode(document.createElement('div'));
this._leftShadowDomNode.setClassName('shadow');
this._domNode.appendChild(this._leftShadowDomNode.domNode);
if (this._options.useShadows) {
this._topShadowDomNode = document.createElement('div');
this._topShadowDomNode.className = 'shadow';
this._domNode.appendChild(this._topShadowDomNode);
this._topShadowDomNode = createFastDomNode(document.createElement('div'));
this._topShadowDomNode.setClassName('shadow');
this._domNode.appendChild(this._topShadowDomNode.domNode);
this._topLeftShadowDomNode = document.createElement('div');
this._topLeftShadowDomNode.className = 'shadow top-left-corner';
this._domNode.appendChild(this._topLeftShadowDomNode);
this._topLeftShadowDomNode = createFastDomNode(document.createElement('div'));
this._topLeftShadowDomNode.setClassName('shadow top-left-corner');
this._domNode.appendChild(this._topLeftShadowDomNode.domNode);
}
this._listenOnDomNode = this._options.listenOnDomNode || this._domNode;
......@@ -101,8 +99,10 @@ export class ScrollableElement extends Widget implements IScrollableElement {
this._mouseIsOver = false;
this.onElementDimensions(dimensions, true);
this._horizontalScrollbar.onElementScrollSize(this._scrollable.getScrollWidth());
this._verticalScrollbar.onElementScrollSize(this._scrollable.getScrollHeight());
this._shouldRender = true;
this._shouldRender = this._horizontalScrollbar.onElementScrollSize(this._scrollable.getScrollWidth()) || this._shouldRender;
this._shouldRender = this._verticalScrollbar.onElementScrollSize(this._scrollable.getScrollHeight()) || this._shouldRender;
}
public dispose(): void {
......@@ -141,9 +141,10 @@ export class ScrollableElement extends Widget implements IScrollableElement {
height: this._domNode.clientHeight
};
}
this._dimensions = this._computeDimensions(dimensions.width, dimensions.height);
this._verticalScrollbar.onElementSize(this._dimensions.height);
this._horizontalScrollbar.onElementSize(this._dimensions.width);
let width = Math.round(dimensions.width);
let height = Math.round(dimensions.height);
this._shouldRender = this._verticalScrollbar.onElementSize(height) || this._shouldRender;
this._shouldRender = this._horizontalScrollbar.onElementSize(width) || this._shouldRender;
}
public updateClassName(newClassName: string): void {
......@@ -239,11 +240,11 @@ export class ScrollableElement extends Widget implements IScrollableElement {
if (desiredScrollTop !== -1 || desiredScrollLeft !== -1) {
if (desiredScrollTop !== -1) {
this._verticalScrollbar.setDesiredScrollPosition(desiredScrollTop);
this._shouldRender = this._verticalScrollbar.setDesiredScrollPosition(desiredScrollTop) || this._shouldRender;
desiredScrollTop = -1;
}
if (desiredScrollLeft !== -1) {
this._horizontalScrollbar.setDesiredScrollPosition(desiredScrollLeft);
this._shouldRender = this._horizontalScrollbar.setDesiredScrollPosition(desiredScrollLeft) || this._shouldRender;
desiredScrollLeft = -1;
}
}
......@@ -259,33 +260,48 @@ export class ScrollableElement extends Widget implements IScrollableElement {
let scrollWidth = this._scrollable.getScrollWidth();
let scrollLeft = this._scrollable.getScrollLeft();
this._horizontalScrollbar.onElementScrollSize(scrollWidth);
this._verticalScrollbar.onElementScrollSize(scrollHeight);
this._verticalScrollbar.onElementScrollPosition(scrollTop);
this._horizontalScrollbar.onElementScrollPosition(scrollLeft);
this._shouldRender = this._horizontalScrollbar.onElementScrollSize(scrollWidth) || this._shouldRender;
this._shouldRender = this._verticalScrollbar.onElementScrollSize(scrollHeight) || this._shouldRender;
this._shouldRender = this._verticalScrollbar.onElementScrollPosition(scrollTop) || this._shouldRender;
this._shouldRender = this._horizontalScrollbar.onElementScrollPosition(scrollLeft) || this._shouldRender;
if (this._options.useShadows) {
let enableTop = scrollHeight > 0 && scrollTop > 0;
let enableLeft = this._options.useShadows && scrollWidth > 0 && scrollLeft > 0;
this._shouldRender = true;
}
if (this._topShadowDomNode) {
DomUtils.toggleClass(this._topShadowDomNode, 'top', enableTop);
}
this._reveal();
if (this._topLeftShadowDomNode) {
DomUtils.toggleClass(this._topLeftShadowDomNode, 'top', enableTop);
}
if (!this._options.lazyRender) {
this._render();
}
}
if (this._leftShadowDomNode) {
DomUtils.toggleClass(this._leftShadowDomNode, 'left', enableLeft);
}
public renderNow(): void {
if (!this._options.lazyRender) {
throw new Error('Please use `lazyRender` together with `renderNow`!');
}
if (this._topLeftShadowDomNode) {
DomUtils.toggleClass(this._topLeftShadowDomNode, 'left', enableLeft);
}
this._render();
}
private _render(): void {
if (!this._shouldRender) {
return;
}
this._reveal();
this._shouldRender = false;
this._horizontalScrollbar.render();
this._verticalScrollbar.render();
if (this._options.useShadows) {
let enableTop = this._scrollable.getScrollTop() > 0;
let enableLeft = this._scrollable.getScrollLeft() > 0;
this._leftShadowDomNode.setClassName('shadow' + (enableLeft ? ' left' : ''));
this._topShadowDomNode.setClassName('shadow' + (enableTop ? ' top' : ''));
this._topLeftShadowDomNode.setClassName('shadow top-left-corner' + (enableTop ? ' top' : '') + (enableLeft ? ' left' : ''));
}
}
// -------------------- fade in / fade out --------------------
......@@ -329,16 +345,6 @@ export class ScrollableElement extends Widget implements IScrollableElement {
// -------------------- size & layout --------------------
private _computeDimensions(clientWidth: number, clientHeight: number): IDimensions {
let width = clientWidth;
let height = clientHeight;
return {
width: width,
height: height
};
}
private _createOptions(options: IScrollableElementCreationOptions): IScrollableElementOptions {
function ensureValue<V>(source: any, prop: string, value: V) {
......@@ -350,6 +356,7 @@ export class ScrollableElement extends Widget implements IScrollableElement {
let result: IScrollableElementOptions = {
forbidTranslate3dUse: ensureValue(options, 'forbidTranslate3dUse', false),
lazyRender: ensureValue(options, 'lazyRender', false),
className: ensureValue(options, 'className', ''),
useShadows: ensureValue(options, 'useShadows', true),
handleMouseWheel: ensureValue(options, 'handleMouseWheel', true),
......
......@@ -22,7 +22,7 @@ export class VerticalScrollbar extends AbstractScrollbar {
// give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom
0
);
super(options.forbidTranslate3dUse, parent, s, options.vertical, 'vertical');
super(options.forbidTranslate3dUse, options.lazyRender, parent, s, options.vertical, 'vertical');
this._scrollable = scrollable;
this._createDomNode();
......
......@@ -909,6 +909,9 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
viewPart.onDidRender();
}
// Render the scrollbar
this.layoutProvider.renderScrollbar();
t.stop();
}
......
......@@ -249,4 +249,8 @@ export class LayoutProvider extends ViewEventHandler implements IDisposable, ILa
public getScrolledTopFromAbsoluteTop(top:number): number {
return top - this.scrollable.getScrollTop();
}
public renderScrollbar(): void {
this.scrollManager.renderScrollbar();
}
}
\ No newline at end of file
......@@ -47,6 +47,7 @@ export class ScrollManager implements IDisposable {
horizontal: configScrollbarOpts.horizontal,
className: ClassNames.SCROLLABLE_ELEMENT + ' ' + this.configuration.editor.theme,
useShadows: false,
lazyRender: true,
saveLastScrollTimeOnClassName: ClassNames.VIEW_LINE
};
addPropertyIfPresent(configScrollbarOpts, scrollbarOptions, 'verticalHasArrows');
......@@ -105,6 +106,10 @@ export class ScrollManager implements IDisposable {
this.toDispose = disposeAll(this.toDispose);
}
public renderScrollbar(): void {
this.scrollbar.renderNow();
}
public onSizeProviderLayoutChanged(): void {
if (this.scrollbar) {
this.scrollbar.onElementDimensions({
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册