From 32cbb35eacdb9ffa4d4b943ccf806ed0a3e15f51 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 18 Nov 2015 14:56:33 +0100 Subject: [PATCH] Delay handling of mouse events until view has finished painting --- .../editor/browser/controller/mouseHandler.ts | 68 +++++++++++++++++-- src/vs/editor/browser/editorBrowser.ts | 1 + src/vs/editor/browser/view/viewImpl.ts | 4 ++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 05d7ac9974f..0d7b9cd622b 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -35,6 +35,56 @@ function createMouseMoveEventMerger(mouseTargetFactory:MouseTarget.MouseTargetFa }; } +class EventGateKeeper { + + public handler: (value:T)=>void; + + private _destination: (value:T)=>void; + private _condition: ()=>boolean; + + private _retryTimer: number; + private _retryValue: T; + + constructor(destination:(value:T)=>void, condition:()=>boolean) { + this._destination = destination; + this._condition = condition; + this._retryTimer = -1; + this.handler = (value:T) => this._handle(value); + } + + public dispose(): void { + if (this._retryTimer !== -1) { + clearTimeout(this._retryTimer); + this._retryTimer = -1; + this._retryValue = null; + } + } + + private _handle(value:T): void { + if (this._condition()) { + + if (this._retryTimer !== -1) { + clearTimeout(this._retryTimer); + this._retryTimer = -1; + this._retryValue = null; + } + + this._destination(value); + + } else { + this._retryValue = value; + if (this._retryTimer === -1) { + this._retryTimer = setTimeout(() => { + this._retryTimer = -1; + let tmp = this._retryValue; + this._retryValue = null; + this._handle(tmp); + }, 10); + } + } + } +} + export class MouseHandler extends ViewEventHandler implements Lifecycle.IDisposable { static CLEAR_MOUSE_DOWN_COUNT_TIME = 400; // ms @@ -64,6 +114,9 @@ export class MouseHandler extends ViewEventHandler implements Lifecycle.IDisposa private lastMouseLeaveTime:number; + private _mouseMoveEventHandler: EventGateKeeper; + private _mouseDownThenMoveEventHandler: EventGateKeeper; + constructor(context:EditorBrowser.IViewContext, viewController:EditorBrowser.IViewController, viewHelper:EditorBrowser.IPointerHandlerHelper) { super(); @@ -95,10 +148,15 @@ export class MouseHandler extends ViewEventHandler implements Lifecycle.IDisposa this.listenersToRemove.push(DomUtils.addListener(this.viewHelper.viewDomNode, 'contextmenu', (e: MouseEvent) => this._onContextMenu(e))); + this._mouseMoveEventHandler = new EventGateKeeper((e) => this._onMouseMove(e), () => !this.viewHelper.isDirty()); + this.toDispose.push(this._mouseMoveEventHandler); this.listenersToRemove.push(DomUtils.addThrottledListener(this.viewHelper.viewDomNode, 'mousemove', - (e: Mouse.StandardMouseEvent) => this._onMouseMove(e), + this._mouseMoveEventHandler.handler, createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + this._mouseDownThenMoveEventHandler = new EventGateKeeper((e) => this._onMouseDownThenMove(e), () => !this.viewHelper.isDirty()); + this.toDispose.push(this._mouseDownThenMoveEventHandler); + this.listenersToRemove.push(DomUtils.addListener(this.viewHelper.viewDomNode, 'mouseup', (e: MouseEvent) => this._onMouseUp(e))); @@ -267,15 +325,17 @@ export class MouseHandler extends ViewEventHandler implements Lifecycle.IDisposa this.mouseMoveMonitor.startMonitoring( createMouseMoveEventMerger(null), - (e:Mouse.StandardMouseEvent) => { - this._updateMouse(this.monitoringStartTargetType, e, true); - }, + this._mouseDownThenMoveEventHandler.handler, () => { this._unhook(); } ); } + private _onMouseDownThenMove(e:Mouse.StandardMouseEvent): void { + this._updateMouse(this.monitoringStartTargetType, e, true); + } + private _unhook(): void { if (this.onScrollTimeout !== -1) { window.clearTimeout(this.onScrollTimeout); diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 80f19d33738..ff27c0893ba 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -52,6 +52,7 @@ export interface IPointerHandlerHelper { linesContentDomNode:HTMLElement; focusTextArea(): void; + isDirty(): boolean; getScrollTop(): number; setScrollTop(scrollTop:number): void; diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 77b9527a3b4..a90f06c2e9d 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -292,6 +292,10 @@ export class View extends ViewEventHandler implements EditorBrowser.IView, Lifec this.focus(); }, + isDirty: (): boolean => { + return (this.accumulatedModelEvents.length > 0); + }, + getScrollTop: () => { if (this._isDisposed) { throw new Error('ViewImpl.pointerHandler.getScrollTop: View is disposed');