mirror of
https://github.com/Microsoft/vscode
synced 2024-09-19 10:40:41 +00:00
commit
9069bdc91f
|
@ -179,13 +179,16 @@ class DomListener implements IDisposable {
|
|||
private _node: Element | Window | Document;
|
||||
private readonly _type: string;
|
||||
private readonly _useCapture: boolean;
|
||||
private readonly _passive: boolean;
|
||||
|
||||
constructor(node: Element | Window | Document, type: string, handler: (e: any) => void, useCapture: boolean) {
|
||||
constructor(node: Element | Window | Document, type: string, handler: (e: any) => void, useCapture: boolean, passive: boolean) {
|
||||
this._node = node;
|
||||
this._type = type;
|
||||
this._handler = handler;
|
||||
this._useCapture = (useCapture || false);
|
||||
this._node.addEventListener(this._type, this._handler, this._useCapture);
|
||||
this._passive = passive;
|
||||
// TODO@Isidor remove any cast once we update our lib.d.ts
|
||||
this._node.addEventListener(this._type, this._handler, <any>{ capture: this._useCapture, passive: this._passive });
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
|
@ -202,8 +205,8 @@ class DomListener implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
return new DomListener(node, type, handler, useCapture);
|
||||
export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean, passive?: boolean): IDisposable {
|
||||
return new DomListener(node, type, handler, useCapture, passive);
|
||||
}
|
||||
|
||||
export interface IAddStandardDisposableListenerSignature {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import arrays = require('vs/base/common/arrays');
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import DomUtils = require('vs/base/browser/dom');
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
|
||||
export namespace EventType {
|
||||
export const Tap = '-monaco-gesturetap';
|
||||
|
@ -35,8 +36,6 @@ export interface GestureEvent extends MouseEvent {
|
|||
pageY: number;
|
||||
}
|
||||
|
||||
export const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0;
|
||||
|
||||
interface Touch {
|
||||
identifier: number;
|
||||
screenX: number;
|
||||
|
@ -65,54 +64,53 @@ interface TouchEvent extends Event {
|
|||
changedTouches: TouchList;
|
||||
}
|
||||
|
||||
|
||||
export class Gesture implements IDisposable {
|
||||
|
||||
private static readonly HOLD_DELAY = 700;
|
||||
private static readonly SCROLL_FRICTION = -0.005;
|
||||
private static INSTANCE: Gesture;
|
||||
private static HOLD_DELAY = 700;
|
||||
|
||||
private targetElement: HTMLElement;
|
||||
private callOnTarget: IDisposable[];
|
||||
private targets: HTMLElement[];
|
||||
private toDispose: IDisposable[];
|
||||
private handle: IDisposable;
|
||||
|
||||
private activeTouches: { [id: number]: TouchData; };
|
||||
|
||||
constructor(target: HTMLElement) {
|
||||
this.callOnTarget = [];
|
||||
private constructor() {
|
||||
this.toDispose = [];
|
||||
this.activeTouches = {};
|
||||
this.target = target;
|
||||
this.handle = null;
|
||||
this.targets = [];
|
||||
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchstart', (e) => this.onTouchStart(e), false, false));
|
||||
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchend', (e) => this.onTouchEnd(e), false, false));
|
||||
this.toDispose.push(DomUtils.addDisposableListener(document, 'touchmove', (e) => this.onTouchMove(e), false, false));
|
||||
}
|
||||
|
||||
public static addTarget(element: HTMLElement): void {
|
||||
if (!Gesture.isTouchDevice()) {
|
||||
return;
|
||||
}
|
||||
if (!Gesture.INSTANCE) {
|
||||
Gesture.INSTANCE = new Gesture();
|
||||
}
|
||||
|
||||
Gesture.INSTANCE.targets.push(element);
|
||||
}
|
||||
|
||||
@memoize
|
||||
private static isTouchDevice(): boolean {
|
||||
return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.target = null;
|
||||
if (this.handle) {
|
||||
this.handle.dispose();
|
||||
dispose(this.toDispose);
|
||||
this.handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
public set target(element: HTMLElement) {
|
||||
this.callOnTarget = dispose(this.callOnTarget);
|
||||
|
||||
this.activeTouches = {};
|
||||
|
||||
this.targetElement = element;
|
||||
|
||||
if (!this.targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.callOnTarget.push(DomUtils.addDisposableListener(this.targetElement, 'touchstart', (e) => this.onTouchStart(e)));
|
||||
this.callOnTarget.push(DomUtils.addDisposableListener(this.targetElement, 'touchend', (e) => this.onTouchEnd(e)));
|
||||
this.callOnTarget.push(DomUtils.addDisposableListener(this.targetElement, 'touchmove', (e) => this.onTouchMove(e)));
|
||||
}
|
||||
|
||||
private static newGestureEvent(type: string): GestureEvent {
|
||||
let event = <GestureEvent>(<any>document.createEvent('CustomEvent'));
|
||||
event.initEvent(type, false, true);
|
||||
return event;
|
||||
}
|
||||
|
||||
private onTouchStart(e: TouchEvent): void {
|
||||
let timestamp = Date.now(); // use Date.now() because on FF e.timeStamp is not epoch based.
|
||||
e.preventDefault();
|
||||
|
@ -136,10 +134,10 @@ export class Gesture implements IDisposable {
|
|||
rollingPageY: [touch.pageY]
|
||||
};
|
||||
|
||||
let evt = Gesture.newGestureEvent(EventType.Start);
|
||||
let evt = this.newGestureEvent(EventType.Start, touch.target);
|
||||
evt.pageX = touch.pageX;
|
||||
evt.pageY = touch.pageY;
|
||||
this.targetElement.dispatchEvent(evt);
|
||||
this.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,21 +164,19 @@ export class Gesture implements IDisposable {
|
|||
&& Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30
|
||||
&& Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) {
|
||||
|
||||
let evt = Gesture.newGestureEvent(EventType.Tap);
|
||||
evt.initialTarget = data.initialTarget;
|
||||
let evt = this.newGestureEvent(EventType.Tap, data.initialTarget);
|
||||
evt.pageX = arrays.tail(data.rollingPageX);
|
||||
evt.pageY = arrays.tail(data.rollingPageY);
|
||||
this.targetElement.dispatchEvent(evt);
|
||||
this.dispatchEvent(evt);
|
||||
|
||||
} else if (holdTime >= Gesture.HOLD_DELAY
|
||||
&& Math.abs(data.initialPageX - arrays.tail(data.rollingPageX)) < 30
|
||||
&& Math.abs(data.initialPageY - arrays.tail(data.rollingPageY)) < 30) {
|
||||
|
||||
let evt = Gesture.newGestureEvent(EventType.Contextmenu);
|
||||
evt.initialTarget = data.initialTarget;
|
||||
let evt = this.newGestureEvent(EventType.Contextmenu, data.initialTarget);
|
||||
evt.pageX = arrays.tail(data.rollingPageX);
|
||||
evt.pageY = arrays.tail(data.rollingPageY);
|
||||
this.targetElement.dispatchEvent(evt);
|
||||
this.dispatchEvent(evt);
|
||||
|
||||
} else if (activeTouchCount === 1) {
|
||||
let finalX = arrays.tail(data.rollingPageX);
|
||||
|
@ -190,7 +186,9 @@ export class Gesture implements IDisposable {
|
|||
let deltaX = finalX - data.rollingPageX[0];
|
||||
let deltaY = finalY - data.rollingPageY[0];
|
||||
|
||||
this.inertia(timestamp, // time now
|
||||
// We need to get all the dispatch targets on the start of the inertia event
|
||||
const dispatchTo = this.targets.filter(t => data.initialTarget instanceof Node && t.contains(data.initialTarget));
|
||||
this.inertia(dispatchTo, timestamp, // time now
|
||||
Math.abs(deltaX) / deltaT, // speed
|
||||
deltaX > 0 ? 1 : -1, // x direction
|
||||
finalX, // x now
|
||||
|
@ -205,7 +203,22 @@ export class Gesture implements IDisposable {
|
|||
}
|
||||
}
|
||||
|
||||
private inertia(t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void {
|
||||
private newGestureEvent(type: string, intialTarget?: EventTarget): GestureEvent {
|
||||
let event = <GestureEvent>(<any>document.createEvent('CustomEvent'));
|
||||
event.initEvent(type, false, true);
|
||||
event.initialTarget = intialTarget;
|
||||
return event;
|
||||
}
|
||||
|
||||
private dispatchEvent(event: GestureEvent): void {
|
||||
this.targets.forEach(target => {
|
||||
if (event.initialTarget instanceof Node && target.contains(event.initialTarget)) {
|
||||
target.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private inertia(dispatchTo: EventTarget[], t1: number, vX: number, dirX: number, x: number, vY: number, dirY: number, y: number): void {
|
||||
this.handle = DomUtils.scheduleAtNextAnimationFrame(() => {
|
||||
let now = Date.now();
|
||||
|
||||
|
@ -228,13 +241,13 @@ export class Gesture implements IDisposable {
|
|||
}
|
||||
|
||||
// dispatch translation event
|
||||
let evt = Gesture.newGestureEvent(EventType.Change);
|
||||
let evt = this.newGestureEvent(EventType.Change);
|
||||
evt.translationX = delta_pos_x;
|
||||
evt.translationY = delta_pos_y;
|
||||
this.targetElement.dispatchEvent(evt);
|
||||
dispatchTo.forEach(d => d.dispatchEvent(evt));
|
||||
|
||||
if (!stopped) {
|
||||
this.inertia(now, vX, dirX, x + delta_pos_x, vY, dirY, y + delta_pos_y);
|
||||
this.inertia(dispatchTo, now, vX, dirX, x + delta_pos_x, vY, dirY, y + delta_pos_y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -255,12 +268,12 @@ export class Gesture implements IDisposable {
|
|||
|
||||
let data = this.activeTouches[touch.identifier];
|
||||
|
||||
let evt = Gesture.newGestureEvent(EventType.Change);
|
||||
let evt = this.newGestureEvent(EventType.Change, data.initialTarget);
|
||||
evt.translationX = touch.pageX - arrays.tail(data.rollingPageX);
|
||||
evt.translationY = touch.pageY - arrays.tail(data.rollingPageY);
|
||||
evt.pageX = touch.pageX;
|
||||
evt.pageY = touch.pageY;
|
||||
this.targetElement.dispatchEvent(evt);
|
||||
this.dispatchEvent(evt);
|
||||
|
||||
// only keep a few data points, to average the final speed
|
||||
if (data.rollingPageX.length > 3) {
|
||||
|
|
|
@ -14,7 +14,7 @@ import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
|||
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner, IRunEvent } from 'vs/base/common/actions';
|
||||
import DOM = require('vs/base/browser/dom');
|
||||
import types = require('vs/base/common/types');
|
||||
import { Gesture, EventType, isTouchDevice } from 'vs/base/browser/touch';
|
||||
import { EventType, Gesture } from 'vs/base/browser/touch';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
|
@ -41,7 +41,6 @@ export class BaseActionItem implements IActionItem {
|
|||
public _context: any;
|
||||
public _action: IAction;
|
||||
|
||||
private gesture: Gesture;
|
||||
private _actionRunner: IActionRunner;
|
||||
|
||||
constructor(context: any, action: IAction, protected options?: IBaseActionItemOptions) {
|
||||
|
@ -106,16 +105,14 @@ export class BaseActionItem implements IActionItem {
|
|||
|
||||
public render(container: HTMLElement): void {
|
||||
this.builder = $(container);
|
||||
this.gesture = new Gesture(container);
|
||||
Gesture.addTarget(container);
|
||||
|
||||
const enableDragging = this.options && this.options.draggable;
|
||||
if (enableDragging) {
|
||||
container.draggable = true;
|
||||
}
|
||||
|
||||
if (isTouchDevice) {
|
||||
this.builder.on(EventType.Tap, e => this.onClick(e));
|
||||
}
|
||||
this.builder.on(EventType.Tap, e => this.onClick(e));
|
||||
|
||||
this.builder.on(DOM.EventType.MOUSE_DOWN, (e) => {
|
||||
if (!enableDragging) {
|
||||
|
@ -203,11 +200,6 @@ export class BaseActionItem implements IActionItem {
|
|||
this.builder = null;
|
||||
}
|
||||
|
||||
if (this.gesture) {
|
||||
this.gesture.dispose();
|
||||
this.gesture = null;
|
||||
}
|
||||
|
||||
this._callOnDispose = lifecycle.dispose(this._callOnDispose);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ export class BaseDropdown extends ActionRunner {
|
|||
this._toDispose.push(cleanupFn);
|
||||
}
|
||||
|
||||
this._toDispose.push(new Gesture(this.$label.getHTMLElement()));
|
||||
Gesture.addTarget(this.$label.getHTMLElement());
|
||||
}
|
||||
|
||||
public get toDispose(): IDisposable[] {
|
||||
|
|
|
@ -106,7 +106,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
|
|||
|
||||
this.rowsContainer = document.createElement('div');
|
||||
this.rowsContainer.className = 'monaco-list-rows';
|
||||
this.gesture = new Gesture(this.rowsContainer);
|
||||
Gesture.addTarget(this.rowsContainer);
|
||||
|
||||
this.scrollableElement = new ScrollableElement(this.rowsContainer, {
|
||||
alwaysConsumeMouseWheel: true,
|
||||
|
|
|
@ -378,7 +378,6 @@ export class TreeView extends HeightMap {
|
|||
private styleElement: HTMLStyleElement;
|
||||
private rowsContainer: HTMLElement;
|
||||
private scrollableElement: ScrollableElement;
|
||||
private wrapperGesture: Touch.Gesture;
|
||||
private msGesture: MSGesture;
|
||||
private lastPointerType: string;
|
||||
private lastClickTimeStamp: number = 0;
|
||||
|
@ -475,7 +474,7 @@ export class TreeView extends HeightMap {
|
|||
this.wrapper.style.msTouchAction = 'none';
|
||||
this.wrapper.style.msContentZooming = 'none';
|
||||
} else {
|
||||
this.wrapperGesture = new Touch.Gesture(this.wrapper);
|
||||
Touch.Gesture.addTarget(this.wrapper);
|
||||
}
|
||||
|
||||
this.rowsContainer = document.createElement('div');
|
||||
|
@ -1642,11 +1641,6 @@ export class TreeView extends HeightMap {
|
|||
}
|
||||
this.domNode = null;
|
||||
|
||||
if (this.wrapperGesture) {
|
||||
this.wrapperGesture.dispose();
|
||||
this.wrapperGesture = null;
|
||||
}
|
||||
|
||||
if (this.context.cache) {
|
||||
this.context.cache.dispose();
|
||||
this.context.cache = null;
|
||||
|
|
|
@ -188,12 +188,10 @@ class StandardPointerHandler extends MouseHandler implements IDisposable {
|
|||
|
||||
class TouchHandler extends MouseHandler {
|
||||
|
||||
private gesture: Gesture;
|
||||
|
||||
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
|
||||
super(context, viewController, viewHelper);
|
||||
|
||||
this.gesture = new Gesture(this.viewHelper.linesContentDomNode);
|
||||
Gesture.addTarget(this.viewHelper.linesContentDomNode);
|
||||
|
||||
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));
|
||||
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));
|
||||
|
@ -202,7 +200,6 @@ class TouchHandler extends MouseHandler {
|
|||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.gesture.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/brow
|
|||
export class NoTabsTitleControl extends TitleControl {
|
||||
private titleContainer: HTMLElement;
|
||||
private editorLabel: ResourceLabel;
|
||||
private titleTouchSupport: Gesture;
|
||||
|
||||
public setContext(group: IEditorGroup): void {
|
||||
super.setContext(group);
|
||||
|
@ -32,7 +31,7 @@ export class NoTabsTitleControl extends TitleControl {
|
|||
this.titleContainer = parent;
|
||||
|
||||
// Gesture Support
|
||||
this.titleTouchSupport = new Gesture(this.titleContainer);
|
||||
Gesture.addTarget(this.titleContainer);
|
||||
|
||||
// Pin on double click
|
||||
this.toUnbind.push(DOM.addDisposableListener(this.titleContainer, DOM.EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e)));
|
||||
|
@ -162,10 +161,4 @@ export class NoTabsTitleControl extends TitleControl {
|
|||
default: return Verbosity.MEDIUM;
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this.titleTouchSupport.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -519,7 +519,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
DOM.addClass(tabContainer, 'tab');
|
||||
|
||||
// Gesture Support
|
||||
const gestureSupport = new Gesture(tabContainer);
|
||||
Gesture.addTarget(tabContainer);
|
||||
|
||||
// Tab Editor Label
|
||||
const editorLabel = this.instantiationService.createInstance(ResourceLabel, tabContainer, void 0);
|
||||
|
@ -536,7 +536,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
// Eventing
|
||||
const disposable = this.hookTabListeners(tabContainer, index);
|
||||
|
||||
this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel, gestureSupport]));
|
||||
this.tabDisposeables.push(combinedDisposable([disposable, bar, editorLabel]));
|
||||
|
||||
return tabContainer;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue