pointer event listener prototyping

This commit is contained in:
Jakob Pinterits
2024-10-04 23:15:12 +02:00
parent 71b19d08dc
commit fdd87fe513
7 changed files with 478 additions and 5 deletions
@@ -39,7 +39,7 @@ export class MouseEventListenerComponent extends ComponentBase {
createElement(): HTMLElement {
let element = document.createElement("div");
element.classList.add("rio-mouse-event-listener");
element.classList.add("rio-pointer-event-listener");
return element;
}
@@ -0,0 +1,215 @@
import { pixelsPerRem } from "../app";
import { ComponentBase, ComponentState } from "./componentBase";
import { DragHandler } from "../eventHandling";
import { tryGetComponentByElement } from "../componentManagement";
import { ComponentId } from "../dataModels";
import { findComponentUnderMouse } from "../utils";
export type PointerEventListenerState = ComponentState & {
_type_: "PointerEventListener-builtin";
content?: ComponentId;
reportPress: boolean;
reportPointerDown: boolean;
reportPointerUp: boolean;
reportPointerMove: boolean;
reportPointerEnter: boolean;
reportPointerLeave: boolean;
reportDragStart: boolean;
reportDragMove: boolean;
reportDragEnd: boolean;
};
export class PointerEventListenerComponent extends ComponentBase {
declare state: Required<PointerEventListenerState>;
private _dragHandler: DragHandler | null = null;
createElement(): HTMLElement {
let element = document.createElement("div");
element.classList.add("rio-pointer-event-listener");
return element;
}
updateElement(
deltaState: PointerEventListenerState,
latentComponents: Set<ComponentBase>
): void {
super.updateElement(deltaState, latentComponents);
this.replaceOnlyChild(latentComponents, deltaState.content);
if (deltaState.reportPress) {
this.element.onclick = (e) => {
this._sendEventToBackend("press", e as PointerEvent, false);
};
} else {
this.element.onclick = null;
}
if (deltaState.reportPointerDown) {
this.element.onpointerdown = (e) => {
this._sendEventToBackend("pointerDown", e, false);
};
} else {
this.element.onpointerdown = null;
}
if (deltaState.reportPointerUp) {
this.element.onpointerup = (e) => {
this._sendEventToBackend("pointerUp", e, false);
};
} else {
this.element.onpointerup = null;
}
if (deltaState.reportPointerMove) {
this.element.onpointermove = (e) => {
this._sendEventToBackend("pointerMove", e, true);
};
} else {
this.element.onpointermove = null;
}
if (deltaState.reportPointerEnter) {
this.element.onpointerenter = (e) => {
this._sendEventToBackend("pointerEnter", e, false);
};
} else {
this.element.onpointerenter = null;
}
if (deltaState.reportPointerLeave) {
this.element.onpointerleave = (e) => {
this._sendEventToBackend("pointerLeave", e, false);
};
} else {
this.element.onpointerleave = null;
}
if (
deltaState.reportDragStart ||
deltaState.reportDragMove ||
deltaState.reportDragEnd
) {
if (this._dragHandler === null) {
this._dragHandler = this.addDragHandler({
element: this.element,
onStart: this._onDragStart.bind(this),
onMove: this._onDragMove.bind(this),
onEnd: this._onDragEnd.bind(this),
});
}
} else {
if (this._dragHandler !== null) {
this._dragHandler.disconnect();
this._dragHandler = null;
}
}
}
private _onDragStart(event: PointerEvent): boolean {
if (this.state.reportDragStart) {
this._sendEventToBackend("dragStart", event, false);
}
return true;
}
private _onDragMove(event: PointerEvent): void {
if (this.state.reportDragMove) {
this._sendEventToBackend("dragMove", event, true);
}
}
private _onDragEnd(event: PointerEvent): void {
if (this.state.reportDragEnd) {
this._sendEventToBackend("dragEnd", event, false);
}
}
/// Serializes a pointer event to the format expected by Python.
///
/// Not all types of events are supported on the Python side. For example, pen
/// input isn't currently handled. If this particular event isn't supported,
/// returns `null`.
serializePointerEvent(event: PointerEvent): object | null {
// Convert the pointer type
if (event.pointerType !== "mouse" && event.pointerType !== "touch") {
return null;
}
let pointerType = event.pointerType;
// Convert the button
if (event.button < 0 || event.button > 2) {
return null;
}
let button = ["left", "middle", "right"][event.button];
// Get the event positions
let elementRect = this.element.getBoundingClientRect();
let windowX = event.clientX / pixelsPerRem;
let windowY = event.clientY / pixelsPerRem;
let componentX = windowX - elementRect.left / pixelsPerRem;
let componentY = windowY - elementRect.top / pixelsPerRem;
// Build the result
return {
pointerType: pointerType,
button: button,
windowX: windowX,
windowY: windowY,
componentX: componentX,
componentY: componentY,
};
}
/// Serializes a pointer event to the format expected by Python. Follows the
/// same semantics as `serializePointerEvent`.
serializePointerMoveEvent(event: PointerEvent): object | null {
// Serialize this as a pointer event
let result = this.serializePointerEvent(event);
// Did the serialization succeed?
if (result === null) {
return null;
}
// Add the relative position
result["relativeX"] = event.movementX / pixelsPerRem;
result["relativeY"] = event.movementY / pixelsPerRem;
// Done
return result;
}
/// Serializes the given event and sends it to the backend. If this type of
/// event isn't supported by the backend (e.g. pen inputs), does nothing.
private _sendEventToBackend(
eventType: string,
event: PointerEvent,
asMoveEvent: boolean
): void {
// Serialize the event
let serialized: object | null;
if (asMoveEvent) {
serialized = this.serializePointerMoveEvent(event);
} else {
serialized = this.serializePointerEvent(event);
}
// Did the serialization succeed?
if (serialized === null) {
return;
}
// Send the event
if (serialized !== null) {
this.sendMessageToBackend({
type: eventType,
...serialized,
});
}
}
}