Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/browser/controller/pointerHandler.ts
3294 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { BrowserFeatures } from '../../../base/browser/canIUse.js';
7
import * as dom from '../../../base/browser/dom.js';
8
import { EventType, Gesture, GestureEvent } from '../../../base/browser/touch.js';
9
import { mainWindow } from '../../../base/browser/window.js';
10
import { Disposable } from '../../../base/common/lifecycle.js';
11
import * as platform from '../../../base/common/platform.js';
12
import { IPointerHandlerHelper, MouseHandler } from './mouseHandler.js';
13
import { NavigationCommandRevealType } from '../coreCommands.js';
14
import { IMouseTarget, MouseTargetType } from '../editorBrowser.js';
15
import { EditorMouseEvent, EditorPointerEventFactory } from '../editorDom.js';
16
import { ViewController } from '../view/viewController.js';
17
import { ViewContext } from '../../common/viewModel/viewContext.js';
18
import { TextAreaSyntethicEvents } from './editContext/textArea/textAreaEditContextInput.js';
19
20
/**
21
* Currently only tested on iOS 13/ iPadOS.
22
*/
23
export class PointerEventHandler extends MouseHandler {
24
private _lastPointerType: string;
25
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
26
super(context, viewController, viewHelper);
27
28
this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));
29
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));
30
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));
31
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode), false)));
32
33
this._lastPointerType = 'mouse';
34
35
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'pointerdown', (e: any) => {
36
const pointerType = e.pointerType;
37
if (pointerType === 'mouse') {
38
this._lastPointerType = 'mouse';
39
return;
40
} else if (pointerType === 'touch') {
41
this._lastPointerType = 'touch';
42
} else {
43
this._lastPointerType = 'pen';
44
}
45
}));
46
47
// PonterEvents
48
const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode);
49
50
this._register(pointerEvents.onPointerMove(this.viewHelper.viewDomNode, (e) => this._onMouseMove(e)));
51
this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));
52
this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));
53
this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerId) => this._onMouseDown(e, pointerId)));
54
}
55
56
private onTap(event: GestureEvent): void {
57
if (!event.initialTarget || !this.viewHelper.linesContentDomNode.contains(<any>event.initialTarget)) {
58
return;
59
}
60
61
event.preventDefault();
62
this.viewHelper.focusTextArea();
63
this._dispatchGesture(event, /*inSelectionMode*/false);
64
}
65
66
private onChange(event: GestureEvent): void {
67
if (this._lastPointerType === 'touch') {
68
this._context.viewModel.viewLayout.deltaScrollNow(-event.translationX, -event.translationY);
69
}
70
if (this._lastPointerType === 'pen') {
71
this._dispatchGesture(event, /*inSelectionMode*/true);
72
}
73
}
74
75
private _dispatchGesture(event: GestureEvent, inSelectionMode: boolean): void {
76
const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);
77
if (target.position) {
78
this.viewController.dispatchMouse({
79
position: target.position,
80
mouseColumn: target.position.column,
81
startedOnLineNumbers: false,
82
revealType: NavigationCommandRevealType.Minimal,
83
mouseDownCount: event.tapCount,
84
inSelectionMode,
85
altKey: false,
86
ctrlKey: false,
87
metaKey: false,
88
shiftKey: false,
89
leftButton: false,
90
middleButton: false,
91
onInjectedText: target.type === MouseTargetType.CONTENT_TEXT && target.detail.injectedText !== null
92
});
93
}
94
}
95
96
protected override _onMouseDown(e: EditorMouseEvent, pointerId: number): void {
97
if ((e.browserEvent as any).pointerType === 'touch') {
98
return;
99
}
100
101
super._onMouseDown(e, pointerId);
102
}
103
}
104
105
class TouchHandler extends MouseHandler {
106
107
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
108
super(context, viewController, viewHelper);
109
110
this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode));
111
112
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e)));
113
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e)));
114
this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode), false)));
115
}
116
117
private onTap(event: GestureEvent): void {
118
event.preventDefault();
119
120
this.viewHelper.focusTextArea();
121
122
const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);
123
124
if (target.position) {
125
// Send the tap event also to the <textarea> (for input purposes)
126
const event = document.createEvent('CustomEvent');
127
event.initEvent(TextAreaSyntethicEvents.Tap, false, true);
128
this.viewHelper.dispatchTextAreaEvent(event);
129
130
this.viewController.moveTo(target.position, NavigationCommandRevealType.Minimal);
131
}
132
}
133
134
private onChange(e: GestureEvent): void {
135
this._context.viewModel.viewLayout.deltaScrollNow(-e.translationX, -e.translationY);
136
}
137
}
138
139
export class PointerHandler extends Disposable {
140
private readonly handler: MouseHandler;
141
142
constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
143
super();
144
const isPhone = platform.isIOS || (platform.isAndroid && platform.isMobile);
145
if (isPhone && BrowserFeatures.pointerEvents) {
146
this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper));
147
} else if (mainWindow.TouchEvent) {
148
this.handler = this._register(new TouchHandler(context, viewController, viewHelper));
149
} else {
150
this.handler = this._register(new MouseHandler(context, viewController, viewHelper));
151
}
152
}
153
154
public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null {
155
return this.handler.getTargetAtClientPoint(clientX, clientY);
156
}
157
}
158
159