Path: blob/main/src/vs/editor/browser/controller/pointerHandler.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { BrowserFeatures } from '../../../base/browser/canIUse.js';6import * as dom from '../../../base/browser/dom.js';7import { EventType, Gesture, GestureEvent } from '../../../base/browser/touch.js';8import { mainWindow } from '../../../base/browser/window.js';9import { Disposable } from '../../../base/common/lifecycle.js';10import * as platform from '../../../base/common/platform.js';11import { IPointerHandlerHelper, MouseHandler } from './mouseHandler.js';12import { NavigationCommandRevealType } from '../coreCommands.js';13import { IMouseTarget, MouseTargetType } from '../editorBrowser.js';14import { EditorMouseEvent, EditorPointerEventFactory } from '../editorDom.js';15import { ViewController } from '../view/viewController.js';16import { ViewContext } from '../../common/viewModel/viewContext.js';17import { TextAreaSyntethicEvents } from './editContext/textArea/textAreaEditContextInput.js';1819/**20* Currently only tested on iOS 13/ iPadOS.21*/22export class PointerEventHandler extends MouseHandler {23private _lastPointerType: string;24constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {25super(context, viewController, viewHelper);2627this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));28this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));29this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));30this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode), false)));3132this._lastPointerType = 'mouse';3334this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'pointerdown', (e: any) => {35const pointerType = e.pointerType;36if (pointerType === 'mouse') {37this._lastPointerType = 'mouse';38return;39} else if (pointerType === 'touch') {40this._lastPointerType = 'touch';41} else {42this._lastPointerType = 'pen';43}44}));4546// PonterEvents47const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode);4849this._register(pointerEvents.onPointerMove(this.viewHelper.viewDomNode, (e) => this._onMouseMove(e)));50this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));51this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));52this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerId) => this._onMouseDown(e, pointerId)));53}5455private onTap(event: GestureEvent): void {56if (!event.initialTarget || !this.viewHelper.linesContentDomNode.contains(<any>event.initialTarget)) {57return;58}5960event.preventDefault();61this.viewHelper.focusTextArea();62this._dispatchGesture(event, /*inSelectionMode*/false);63}6465private onChange(event: GestureEvent): void {66if (this._lastPointerType === 'touch') {67this._context.viewModel.viewLayout.deltaScrollNow(-event.translationX, -event.translationY);68}69if (this._lastPointerType === 'pen') {70this._dispatchGesture(event, /*inSelectionMode*/true);71}72}7374private _dispatchGesture(event: GestureEvent, inSelectionMode: boolean): void {75const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);76if (target.position) {77this.viewController.dispatchMouse({78position: target.position,79mouseColumn: target.position.column,80startedOnLineNumbers: false,81revealType: NavigationCommandRevealType.Minimal,82mouseDownCount: event.tapCount,83inSelectionMode,84altKey: false,85ctrlKey: false,86metaKey: false,87shiftKey: false,88leftButton: false,89middleButton: false,90onInjectedText: target.type === MouseTargetType.CONTENT_TEXT && target.detail.injectedText !== null91});92}93}9495protected override _onMouseDown(e: EditorMouseEvent, pointerId: number): void {96if ((e.browserEvent as any).pointerType === 'touch') {97return;98}99100super._onMouseDown(e, pointerId);101}102}103104class TouchHandler extends MouseHandler {105106constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {107super(context, viewController, viewHelper);108109this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));110111this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));112this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));113this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode), false)));114}115116private onTap(event: GestureEvent): void {117event.preventDefault();118119this.viewHelper.focusTextArea();120121const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);122123if (target.position) {124// Send the tap event also to the <textarea> (for input purposes)125const event = document.createEvent('CustomEvent');126event.initEvent(TextAreaSyntethicEvents.Tap, false, true);127this.viewHelper.dispatchTextAreaEvent(event);128129this.viewController.moveTo(target.position, NavigationCommandRevealType.Minimal);130}131}132133private onChange(e: GestureEvent): void {134this._context.viewModel.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);135}136}137138export class PointerHandler extends Disposable {139private readonly handler: MouseHandler;140141constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {142super();143const isPhone = platform.isIOS || (platform.isAndroid && platform.isMobile);144if (isPhone && BrowserFeatures.pointerEvents) {145this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper));146} else if (mainWindow.TouchEvent) {147this.handler = this._register(new TouchHandler(context, viewController, viewHelper));148} else {149this.handler = this._register(new MouseHandler(context, viewController, viewHelper));150}151}152153public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null {154return this.handler.getTargetAtClientPoint(clientX, clientY);155}156}157158159