Merge pull request #20213 from lab-core/pointer-type

[Editor] A new CurrentPointers class to store current pointers used by the editor
This commit is contained in:
calixteman 2025-11-01 19:17:32 +01:00 committed by GitHub
commit 2cc809ade2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 101 additions and 37 deletions

View File

@ -16,6 +16,7 @@
import { AnnotationEditorParamsType, unreachable } from "../../shared/util.js"; import { AnnotationEditorParamsType, unreachable } from "../../shared/util.js";
import { noContextMenu, stopEvent } from "../display_utils.js"; import { noContextMenu, stopEvent } from "../display_utils.js";
import { AnnotationEditor } from "./editor.js"; import { AnnotationEditor } from "./editor.js";
import { CurrentPointers } from "./tools.js";
class DrawingOptions { class DrawingOptions {
#svgProperties = Object.create(null); #svgProperties = Object.create(null);
@ -81,14 +82,6 @@ class DrawingEditor extends AnnotationEditor {
static #currentDrawingOptions = null; static #currentDrawingOptions = null;
static #currentPointerId = NaN;
static #currentPointerType = null;
static #currentPointerIds = null;
static #currentMoveTimestamp = NaN;
static _INNER_MARGIN = 3; static _INNER_MARGIN = 3;
constructor(params) { constructor(params) {
@ -678,20 +671,15 @@ class DrawingEditor extends AnnotationEditor {
} }
static startDrawing(parent, uiManager, _isLTR, event) { static startDrawing(parent, uiManager, _isLTR, event) {
// The _currentPointerType is set when the user starts an empty drawing // The pointerType of CurrentPointer is set when the user starts an empty
// session. If, in the same drawing session, the user starts using a // drawing session. If, in the same drawing session, the user starts using a
// different type of pointer (e.g. a pen and then a finger), we just return. // different type of pointer (e.g. a pen and then a finger), we just return.
// //
// The _currentPointerId and _currentPointerIds are used to keep track of // If the user starts to draw with a finger and then uses a second finger,
// the pointers with a same type (e.g. two fingers). If the user starts to // we just stop the current drawing and let the user zoom the document.
// draw with a finger and then uses a second finger, we just stop the
// current drawing and let the user zoom the document.
const { target, offsetX: x, offsetY: y, pointerId, pointerType } = event; const { target, offsetX: x, offsetY: y, pointerId, pointerType } = event;
if ( if (CurrentPointers.isInitializedAndDifferentPointerType(pointerType)) {
DrawingEditor.#currentPointerType &&
DrawingEditor.#currentPointerType !== pointerType
) {
return; return;
} }
@ -704,16 +692,13 @@ class DrawingEditor extends AnnotationEditor {
const ac = (DrawingEditor.#currentDrawingAC = new AbortController()); const ac = (DrawingEditor.#currentDrawingAC = new AbortController());
const signal = parent.combinedSignal(ac); const signal = parent.combinedSignal(ac);
DrawingEditor.#currentPointerId ||= pointerId; CurrentPointers.setPointer(pointerType, pointerId);
DrawingEditor.#currentPointerType ??= pointerType;
window.addEventListener( window.addEventListener(
"pointerup", "pointerup",
e => { e => {
if (DrawingEditor.#currentPointerId === e.pointerId) { if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) {
this._endDraw(e); this._endDraw(e);
} else {
DrawingEditor.#currentPointerIds?.delete(e.pointerId);
} }
}, },
{ signal } { signal }
@ -721,10 +706,8 @@ class DrawingEditor extends AnnotationEditor {
window.addEventListener( window.addEventListener(
"pointercancel", "pointercancel",
e => { e => {
if (DrawingEditor.#currentPointerId === e.pointerId) { if (CurrentPointers.isSamePointerIdOrRemove(e.pointerId)) {
this._currentParent.endDrawingSession(); this._currentParent.endDrawingSession();
} else {
DrawingEditor.#currentPointerIds?.delete(e.pointerId);
} }
}, },
{ signal } { signal }
@ -732,14 +715,14 @@ class DrawingEditor extends AnnotationEditor {
window.addEventListener( window.addEventListener(
"pointerdown", "pointerdown",
e => { e => {
if (DrawingEditor.#currentPointerType !== e.pointerType) { if (!CurrentPointers.isSamePointerType(e.pointerType)) {
// For example, we started with a pen and the user // For example, we started with a pen and the user
// is now using a finger. // is now using a finger.
return; return;
} }
// For example, the user is using a second finger. // For example, the user is using a second finger.
(DrawingEditor.#currentPointerIds ||= new Set()).add(e.pointerId); CurrentPointers.initializeAndAddPointerId(e.pointerId);
// The first finger created a first point and a second finger just // The first finger created a first point and a second finger just
// started, so we stop the drawing and remove this only point. // started, so we stop the drawing and remove this only point.
@ -765,7 +748,7 @@ class DrawingEditor extends AnnotationEditor {
target.addEventListener( target.addEventListener(
"touchmove", "touchmove",
e => { e => {
if (e.timeStamp === DrawingEditor.#currentMoveTimestamp) { if (CurrentPointers.isSameTimeStamp(e.timeStamp)) {
// This move event is used to draw so we don't want to scroll. // This move event is used to draw so we don't want to scroll.
stopEvent(e); stopEvent(e);
} }
@ -812,16 +795,16 @@ class DrawingEditor extends AnnotationEditor {
} }
static _drawMove(event) { static _drawMove(event) {
DrawingEditor.#currentMoveTimestamp = -1; CurrentPointers.isSameTimeStamp(event.timeStamp);
if (!DrawingEditor.#currentDraw) { if (!DrawingEditor.#currentDraw) {
return; return;
} }
const { offsetX, offsetY, pointerId } = event; const { offsetX, offsetY, pointerId } = event;
if (DrawingEditor.#currentPointerId !== pointerId) { if (!CurrentPointers.isSamePointerId(pointerId)) {
return; return;
} }
if (DrawingEditor.#currentPointerIds?.size >= 1) { if (CurrentPointers.isUsingMultiplePointers()) {
// The user is using multiple fingers and the first one is moving. // The user is using multiple fingers and the first one is moving.
this._endDraw(event); this._endDraw(event);
return; return;
@ -831,7 +814,7 @@ class DrawingEditor extends AnnotationEditor {
DrawingEditor.#currentDraw.add(offsetX, offsetY) DrawingEditor.#currentDraw.add(offsetX, offsetY)
); );
// We track the timestamp to know if the touchmove event is used to draw. // We track the timestamp to know if the touchmove event is used to draw.
DrawingEditor.#currentMoveTimestamp = event.timeStamp; CurrentPointers.setTimeStamp(event.timeStamp);
stopEvent(event); stopEvent(event);
} }
@ -841,15 +824,14 @@ class DrawingEditor extends AnnotationEditor {
this._currentParent = null; this._currentParent = null;
DrawingEditor.#currentDraw = null; DrawingEditor.#currentDraw = null;
DrawingEditor.#currentDrawingOptions = null; DrawingEditor.#currentDrawingOptions = null;
DrawingEditor.#currentPointerType = null; CurrentPointers.clearPointerType();
DrawingEditor.#currentMoveTimestamp = NaN; CurrentPointers.clearTimeStamp();
} }
if (DrawingEditor.#currentDrawingAC) { if (DrawingEditor.#currentDrawingAC) {
DrawingEditor.#currentDrawingAC.abort(); DrawingEditor.#currentDrawingAC.abort();
DrawingEditor.#currentDrawingAC = null; DrawingEditor.#currentDrawingAC = null;
DrawingEditor.#currentPointerId = NaN; CurrentPointers.clearPointerIds();
DrawingEditor.#currentPointerIds = null;
} }
} }

View File

@ -42,6 +42,87 @@ function bindEvents(obj, element, names) {
} }
} }
/**
* Class to store current pointers used by the editor to be able to handle
* multiple pointers (e.g. two fingers, a pen, a mouse, ...).
*/
class CurrentPointers {
// To manage the pointer events.
// The pointerId and pointerIds are used to keep track of
// the pointers with a same type (e.g. two fingers).
static #pointerId = NaN;
static #pointerIds = null;
// Track the timestamp to know if the touchmove event is used.
static #moveTimestamp = NaN;
// The pointerType is used to know if we are using a mouse, a pen or a touch.
static #pointerType = null;
static initializeAndAddPointerId(pointerId) {
// Store pointer ids. For example, the user is using a second finger.
(CurrentPointers.#pointerIds ||= new Set()).add(pointerId);
}
static setPointer(pointerType, pointerId) {
CurrentPointers.#pointerId ||= pointerId;
CurrentPointers.#pointerType ??= pointerType;
}
static setTimeStamp(timeStamp) {
CurrentPointers.#moveTimestamp = timeStamp;
}
static isSamePointerId(pointerId) {
return CurrentPointers.#pointerId === pointerId;
}
// Check if it's the same pointer id, otherwise remove it from the set.
static isSamePointerIdOrRemove(pointerId) {
if (CurrentPointers.#pointerId === pointerId) {
return true;
}
CurrentPointers.#pointerIds?.delete(pointerId);
return false;
}
static isSamePointerType(pointerType) {
return CurrentPointers.#pointerType === pointerType;
}
static isInitializedAndDifferentPointerType(pointerType) {
return (
CurrentPointers.#pointerType !== null &&
!CurrentPointers.isSamePointerType(pointerType)
);
}
static isSameTimeStamp(timeStamp) {
return CurrentPointers.#moveTimestamp === timeStamp;
}
static isUsingMultiplePointers() {
// Check if the user is using multiple fingers
return CurrentPointers.#pointerIds?.size >= 1;
}
static clearPointerType() {
CurrentPointers.#pointerType = null;
}
static clearPointerIds() {
CurrentPointers.#pointerId = NaN;
CurrentPointers.#pointerIds = null;
}
static clearTimeStamp() {
CurrentPointers.#moveTimestamp = NaN;
}
}
/** /**
* Class to create some unique ids for the different editors. * Class to create some unique ids for the different editors.
*/ */
@ -2801,5 +2882,6 @@ export {
bindEvents, bindEvents,
ColorManager, ColorManager,
CommandManager, CommandManager,
CurrentPointers,
KeyboardManager, KeyboardManager,
}; };