Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/mouseEvent.ts
5221 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 * as browser from './browser.js';
7
import { IframeUtils } from './iframe.js';
8
import * as platform from '../common/platform.js';
9
10
export interface IMouseEvent {
11
readonly browserEvent: MouseEvent;
12
readonly leftButton: boolean;
13
readonly middleButton: boolean;
14
readonly rightButton: boolean;
15
readonly buttons: number;
16
readonly target: HTMLElement;
17
readonly detail: number;
18
readonly posx: number;
19
readonly posy: number;
20
readonly ctrlKey: boolean;
21
readonly shiftKey: boolean;
22
readonly altKey: boolean;
23
readonly metaKey: boolean;
24
readonly timestamp: number;
25
readonly defaultPrevented: boolean;
26
27
preventDefault(): void;
28
stopPropagation(): void;
29
}
30
31
export class StandardMouseEvent implements IMouseEvent {
32
33
public readonly browserEvent: MouseEvent;
34
35
public readonly leftButton: boolean;
36
public readonly middleButton: boolean;
37
public readonly rightButton: boolean;
38
public readonly buttons: number;
39
public readonly target: HTMLElement;
40
public detail: number;
41
public readonly posx: number;
42
public readonly posy: number;
43
public readonly ctrlKey: boolean;
44
public readonly shiftKey: boolean;
45
public readonly altKey: boolean;
46
public readonly metaKey: boolean;
47
public readonly timestamp: number;
48
public readonly defaultPrevented: boolean;
49
50
constructor(targetWindow: Window, e: MouseEvent) {
51
this.timestamp = Date.now();
52
this.browserEvent = e;
53
this.leftButton = e.button === 0;
54
this.middleButton = e.button === 1;
55
this.rightButton = e.button === 2;
56
this.buttons = e.buttons;
57
this.defaultPrevented = e.defaultPrevented;
58
59
this.target = <HTMLElement>e.target;
60
61
this.detail = e.detail || 1;
62
if (e.type === 'dblclick') {
63
this.detail = 2;
64
}
65
this.ctrlKey = e.ctrlKey;
66
this.shiftKey = e.shiftKey;
67
this.altKey = e.altKey;
68
this.metaKey = e.metaKey;
69
70
if (typeof e.pageX === 'number') {
71
this.posx = e.pageX;
72
this.posy = e.pageY;
73
} else {
74
// Probably hit by MSGestureEvent
75
this.posx = e.clientX + this.target.ownerDocument.body.scrollLeft + this.target.ownerDocument.documentElement.scrollLeft;
76
this.posy = e.clientY + this.target.ownerDocument.body.scrollTop + this.target.ownerDocument.documentElement.scrollTop;
77
}
78
79
// Find the position of the iframe this code is executing in relative to the iframe where the event was captured.
80
const iframeOffsets = IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(targetWindow, e.view);
81
this.posx -= iframeOffsets.left;
82
this.posy -= iframeOffsets.top;
83
}
84
85
public preventDefault(): void {
86
this.browserEvent.preventDefault();
87
}
88
89
public stopPropagation(): void {
90
this.browserEvent.stopPropagation();
91
}
92
}
93
94
export class DragMouseEvent extends StandardMouseEvent {
95
96
public readonly dataTransfer: DataTransfer;
97
98
constructor(targetWindow: Window, e: MouseEvent) {
99
super(targetWindow, e);
100
// eslint-disable-next-line local/code-no-any-casts
101
this.dataTransfer = (<any>e).dataTransfer;
102
}
103
}
104
105
export interface IMouseWheelEvent extends MouseEvent {
106
readonly wheelDelta: number;
107
readonly wheelDeltaX: number;
108
readonly wheelDeltaY: number;
109
110
readonly deltaX: number;
111
readonly deltaY: number;
112
readonly deltaZ: number;
113
readonly deltaMode: number;
114
}
115
116
interface IWebKitMouseWheelEvent {
117
wheelDeltaY: number;
118
wheelDeltaX: number;
119
}
120
121
interface IGeckoMouseWheelEvent {
122
HORIZONTAL_AXIS: number;
123
VERTICAL_AXIS: number;
124
axis: number;
125
detail: number;
126
}
127
128
export class StandardWheelEvent {
129
130
public readonly browserEvent: IMouseWheelEvent | null;
131
public readonly deltaY: number;
132
public readonly deltaX: number;
133
public readonly target: Node;
134
135
constructor(e: IMouseWheelEvent | null, deltaX: number = 0, deltaY: number = 0) {
136
137
this.browserEvent = e || null;
138
// eslint-disable-next-line local/code-no-any-casts
139
this.target = e ? (e.target || (<any>e).targetNode || e.srcElement) : null;
140
141
this.deltaY = deltaY;
142
this.deltaX = deltaX;
143
144
let shouldFactorDPR: boolean = false;
145
if (browser.isChrome) {
146
// Chrome version >= 123 contains the fix to factor devicePixelRatio into the wheel event.
147
// See https://chromium.googlesource.com/chromium/src.git/+/be51b448441ff0c9d1f17e0f25c4bf1ab3f11f61
148
const chromeVersionMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
149
const chromeMajorVersion = chromeVersionMatch ? parseInt(chromeVersionMatch[1]) : 123;
150
shouldFactorDPR = chromeMajorVersion <= 122;
151
}
152
153
if (e) {
154
// Old (deprecated) wheel events
155
// eslint-disable-next-line local/code-no-any-casts
156
const e1 = <IWebKitMouseWheelEvent><any>e;
157
// eslint-disable-next-line local/code-no-any-casts
158
const e2 = <IGeckoMouseWheelEvent><any>e;
159
const devicePixelRatio = e.view?.devicePixelRatio || 1;
160
161
// vertical delta scroll
162
if (typeof e1.wheelDeltaY !== 'undefined') {
163
if (shouldFactorDPR) {
164
// Refs https://github.com/microsoft/vscode/issues/146403#issuecomment-1854538928
165
this.deltaY = e1.wheelDeltaY / (120 * devicePixelRatio);
166
} else {
167
this.deltaY = e1.wheelDeltaY / 120;
168
}
169
} else if (typeof e2.VERTICAL_AXIS !== 'undefined' && e2.axis === e2.VERTICAL_AXIS) {
170
this.deltaY = -e2.detail / 3;
171
} else if (e.type === 'wheel') {
172
// Modern wheel event
173
// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
174
const ev = <WheelEvent><unknown>e;
175
176
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
177
// the deltas are expressed in lines
178
if (browser.isFirefox && !platform.isMacintosh) {
179
this.deltaY = -e.deltaY / 3;
180
} else {
181
this.deltaY = -e.deltaY;
182
}
183
} else {
184
this.deltaY = -e.deltaY / 40;
185
}
186
}
187
188
// horizontal delta scroll
189
if (typeof e1.wheelDeltaX !== 'undefined') {
190
if (browser.isSafari && platform.isWindows) {
191
this.deltaX = - (e1.wheelDeltaX / 120);
192
} else if (shouldFactorDPR) {
193
// Refs https://github.com/microsoft/vscode/issues/146403#issuecomment-1854538928
194
this.deltaX = e1.wheelDeltaX / (120 * devicePixelRatio);
195
} else {
196
this.deltaX = e1.wheelDeltaX / 120;
197
}
198
} else if (typeof e2.HORIZONTAL_AXIS !== 'undefined' && e2.axis === e2.HORIZONTAL_AXIS) {
199
this.deltaX = -e.detail / 3;
200
} else if (e.type === 'wheel') {
201
// Modern wheel event
202
// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
203
const ev = <WheelEvent><unknown>e;
204
205
if (ev.deltaMode === ev.DOM_DELTA_LINE) {
206
// the deltas are expressed in lines
207
if (browser.isFirefox && !platform.isMacintosh) {
208
this.deltaX = -e.deltaX / 3;
209
} else {
210
this.deltaX = -e.deltaX;
211
}
212
} else {
213
this.deltaX = -e.deltaX / 40;
214
}
215
}
216
217
// Assume a vertical scroll if nothing else worked
218
if (this.deltaY === 0 && this.deltaX === 0 && e.wheelDelta) {
219
if (shouldFactorDPR) {
220
// Refs https://github.com/microsoft/vscode/issues/146403#issuecomment-1854538928
221
this.deltaY = e.wheelDelta / (120 * devicePixelRatio);
222
} else {
223
this.deltaY = e.wheelDelta / 120;
224
}
225
}
226
}
227
}
228
229
public preventDefault(): void {
230
this.browserEvent?.preventDefault();
231
}
232
233
public stopPropagation(): void {
234
this.browserEvent?.stopPropagation();
235
}
236
}
237
238