mirror of
https://github.com/rio-labs/rio.git
synced 2026-02-11 08:10:29 -06:00
add consume_events to PointerEventListener and MouseEventListener; move event listeners to capture phase
- Frontend
- pointerEventListener.ts: install click/pointer* handlers with addEventListener({ capture: true }); create/remove bound handlers lazily; honor consume_events by marking events handled in parent handler.
- mouseEventListener.ts: same capture-phase install and lazy handler refs; consume_events respected.
- Tests
- Add test_mouse_event_listener.py for consume_events and capture-phase propagation.
- Update test_pointer_event_listener.py to reflect new capture behavior.
Behavior change:
- PointerEventListener propagation semantics changed: with capture-phase parent listeners, events now reach the parent first; if the parent’s handler consumes the event, the child won’t see it (previously a child consuming could prevent the parent from receiving it). This ensures the handler decides whether to consume.
This commit is contained in:
@@ -4,6 +4,7 @@ import { DragHandler } from "../eventHandling";
|
||||
import { ComponentId } from "../dataModels";
|
||||
import { findComponentUnderMouse } from "../utils";
|
||||
import { ComponentStatesUpdateContext } from "../componentManagement";
|
||||
import { markEventAsHandled } from "../eventHandling";
|
||||
|
||||
function eventMouseButtonToString(event: MouseEvent): object {
|
||||
return {
|
||||
@@ -30,10 +31,18 @@ export type MouseEventListenerState = ComponentState & {
|
||||
reportDragStart: boolean;
|
||||
reportDragMove: boolean;
|
||||
reportDragEnd: boolean;
|
||||
consume_events: boolean;
|
||||
};
|
||||
|
||||
export class MouseEventListenerComponent extends ComponentBase<MouseEventListenerState> {
|
||||
private _dragHandler: DragHandler | null = null;
|
||||
// Handler refs created on install to keep structure minimal
|
||||
private _onClickBound: ((e: MouseEvent) => void) | null = null;
|
||||
private _onMouseDownBound: ((e: MouseEvent) => void) | null = null;
|
||||
private _onMouseUpBound: ((e: MouseEvent) => void) | null = null;
|
||||
private _onMouseMoveBound: ((e: MouseEvent) => void) | null = null;
|
||||
private _onMouseEnterBound: ((e: MouseEvent) => void) | null = null;
|
||||
private _onMouseLeaveBound: ((e: MouseEvent) => void) | null = null;
|
||||
|
||||
createElement(context: ComponentStatesUpdateContext): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
@@ -49,73 +58,167 @@ export class MouseEventListenerComponent extends ComponentBase<MouseEventListene
|
||||
|
||||
this.replaceOnlyChild(context, deltaState.content);
|
||||
|
||||
if (deltaState.reportPress) {
|
||||
this.element.onclick = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "press",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onclick = null;
|
||||
if (deltaState.reportPress !== undefined) {
|
||||
if (this.state.reportPress) {
|
||||
if (this._onClickBound === null) {
|
||||
this._onClickBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "press",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener("click", this._onClickBound, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this._onClickBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"click",
|
||||
this._onClickBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onClickBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportMouseDown) {
|
||||
this.element.onmousedown = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "mouseDown",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onmousedown = null;
|
||||
if (deltaState.reportMouseDown !== undefined) {
|
||||
if (this.state.reportMouseDown) {
|
||||
if (this._onMouseDownBound === null) {
|
||||
this._onMouseDownBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "mouseDown",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"mousedown",
|
||||
this._onMouseDownBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onMouseDownBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"mousedown",
|
||||
this._onMouseDownBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onMouseDownBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportMouseUp) {
|
||||
this.element.onmouseup = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "mouseUp",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onmouseup = null;
|
||||
if (deltaState.reportMouseUp !== undefined) {
|
||||
if (this.state.reportMouseUp) {
|
||||
if (this._onMouseUpBound === null) {
|
||||
this._onMouseUpBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "mouseUp",
|
||||
...eventMouseButtonToString(e),
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"mouseup",
|
||||
this._onMouseUpBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onMouseUpBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"mouseup",
|
||||
this._onMouseUpBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onMouseUpBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportMouseMove) {
|
||||
this.element.onmousemove = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "mouseMove",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onmousemove = null;
|
||||
if (deltaState.reportMouseMove !== undefined) {
|
||||
if (this.state.reportMouseMove) {
|
||||
if (this._onMouseMoveBound === null) {
|
||||
this._onMouseMoveBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "mouseMove",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"mousemove",
|
||||
this._onMouseMoveBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onMouseMoveBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"mousemove",
|
||||
this._onMouseMoveBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onMouseMoveBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportMouseEnter) {
|
||||
this.element.onmouseenter = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "mouseEnter",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onmouseenter = null;
|
||||
if (deltaState.reportMouseEnter !== undefined) {
|
||||
if (this.state.reportMouseEnter) {
|
||||
if (this._onMouseEnterBound === null) {
|
||||
this._onMouseEnterBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "mouseEnter",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"mouseenter",
|
||||
this._onMouseEnterBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onMouseEnterBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"mouseenter",
|
||||
this._onMouseEnterBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onMouseEnterBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportMouseLeave) {
|
||||
this.element.onmouseleave = (e) => {
|
||||
this.sendMessageToBackend({
|
||||
type: "mouseLeave",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
} else {
|
||||
this.element.onmouseleave = null;
|
||||
if (deltaState.reportMouseLeave !== undefined) {
|
||||
if (this.state.reportMouseLeave) {
|
||||
if (this._onMouseLeaveBound === null) {
|
||||
this._onMouseLeaveBound = (e: MouseEvent) => {
|
||||
this._sendMessageToBackend(e, {
|
||||
type: "mouseLeave",
|
||||
...eventMousePositionToString(e),
|
||||
});
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"mouseleave",
|
||||
this._onMouseLeaveBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onMouseLeaveBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"mouseleave",
|
||||
this._onMouseLeaveBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onMouseLeaveBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -159,7 +262,7 @@ export class MouseEventListenerComponent extends ComponentBase<MouseEventListene
|
||||
}
|
||||
|
||||
private _sendDragEvent(eventType: string, event: MouseEvent): void {
|
||||
this.sendMessageToBackend({
|
||||
this._sendMessageToBackend(event, {
|
||||
type: eventType,
|
||||
...eventMouseButtonToString(event),
|
||||
x: event.clientX / pixelsPerRem,
|
||||
@@ -167,4 +270,11 @@ export class MouseEventListenerComponent extends ComponentBase<MouseEventListene
|
||||
component: findComponentUnderMouse(event),
|
||||
});
|
||||
}
|
||||
|
||||
private _sendMessageToBackend(event: MouseEvent, message: object): void {
|
||||
// Mark the event as handled if needed
|
||||
if (this.state.consume_events) markEventAsHandled(event);
|
||||
|
||||
this.sendMessageToBackend(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export type PointerEventListenerState = ComponentState & {
|
||||
reportDragStart: boolean;
|
||||
reportDragMove: boolean;
|
||||
reportDragEnd: boolean;
|
||||
consume_events: boolean;
|
||||
};
|
||||
|
||||
const DOUBLE_CLICK_TIMEOUT = 300;
|
||||
@@ -28,6 +29,13 @@ export class PointerEventListenerComponent extends ComponentBase<PointerEventLis
|
||||
private _doubleClickTimeoutByButton: {
|
||||
[button: number]: number | undefined;
|
||||
} = {};
|
||||
// Handler references created on-demand where installed
|
||||
private _onClickBound: (e: MouseEvent) => void | null = null;
|
||||
private _onPointerDownBound: ((e: PointerEvent) => void) | null = null;
|
||||
private _onPointerUpBound: ((e: PointerEvent) => void) | null = null;
|
||||
private _onPointerMoveBound: ((e: PointerEvent) => void) | null = null;
|
||||
private _onPointerEnterBound: ((e: PointerEvent) => void) | null = null;
|
||||
private _onPointerLeaveBound: ((e: PointerEvent) => void) | null = null;
|
||||
|
||||
createElement(context: ComponentStatesUpdateContext): HTMLElement {
|
||||
let element = document.createElement("div");
|
||||
@@ -52,58 +60,148 @@ export class PointerEventListenerComponent extends ComponentBase<PointerEventLis
|
||||
deltaState.reportDoublePress ?? this.state.reportDoublePress;
|
||||
|
||||
if (reportPress || reportDoublePress) {
|
||||
this.element.onclick = this._onClick.bind(this);
|
||||
if (this._onClickBound === null) {
|
||||
this._onClickBound = this._onClick.bind(this);
|
||||
this.element.addEventListener("click", this._onClickBound, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.element.onclick = null;
|
||||
if (this._onClickBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"click",
|
||||
this._onClickBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onClickBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportPointerDown !== undefined) {
|
||||
if (deltaState.reportPointerDown.length > 0) {
|
||||
this.element.onpointerdown = (e) => {
|
||||
if (eventMatchesButton(e, deltaState.reportPointerDown!)) {
|
||||
this._sendEventToBackend("pointerDown", e, false);
|
||||
}
|
||||
};
|
||||
if ((this.state.reportPointerDown?.length ?? 0) > 0) {
|
||||
if (this._onPointerDownBound === null) {
|
||||
this._onPointerDownBound = (e: PointerEvent) => {
|
||||
if (
|
||||
eventMatchesButton(e, this.state.reportPointerDown)
|
||||
) {
|
||||
this._sendEventToBackend("pointerDown", e, false);
|
||||
}
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"pointerdown",
|
||||
this._onPointerDownBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.element.onpointerdown = null;
|
||||
if (this._onPointerDownBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"pointerdown",
|
||||
this._onPointerDownBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onPointerDownBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportPointerUp !== undefined) {
|
||||
if (deltaState.reportPointerUp.length > 0) {
|
||||
this.element.onpointerup = (e) => {
|
||||
if (eventMatchesButton(e, deltaState.reportPointerUp!)) {
|
||||
this._sendEventToBackend("pointerUp", e, false);
|
||||
}
|
||||
};
|
||||
if ((this.state.reportPointerUp?.length ?? 0) > 0) {
|
||||
if (this._onPointerUpBound === null) {
|
||||
this._onPointerUpBound = (e: PointerEvent) => {
|
||||
if (eventMatchesButton(e, this.state.reportPointerUp)) {
|
||||
this._sendEventToBackend("pointerUp", e, false);
|
||||
}
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"pointerup",
|
||||
this._onPointerUpBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.element.onpointerup = null;
|
||||
if (this._onPointerUpBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"pointerup",
|
||||
this._onPointerUpBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onPointerUpBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportPointerMove) {
|
||||
this.element.onpointermove = (e) => {
|
||||
this._sendEventToBackend("pointerMove", e, true);
|
||||
};
|
||||
} else {
|
||||
this.element.onpointermove = null;
|
||||
if (deltaState.reportPointerMove !== undefined) {
|
||||
if (this.state.reportPointerMove) {
|
||||
if (this._onPointerMoveBound === null) {
|
||||
this._onPointerMoveBound = (e: PointerEvent) => {
|
||||
this._sendEventToBackend("pointerMove", e, true);
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"pointermove",
|
||||
this._onPointerMoveBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onPointerMoveBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"pointermove",
|
||||
this._onPointerMoveBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onPointerMoveBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportPointerEnter) {
|
||||
this.element.onpointerenter = (e) => {
|
||||
this._sendEventToBackend("pointerEnter", e, false);
|
||||
};
|
||||
} else {
|
||||
this.element.onpointerenter = null;
|
||||
if (deltaState.reportPointerEnter !== undefined) {
|
||||
if (this.state.reportPointerEnter) {
|
||||
if (this._onPointerEnterBound === null) {
|
||||
this._onPointerEnterBound = (e: PointerEvent) => {
|
||||
this._sendEventToBackend("pointerEnter", e, false);
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"pointerenter",
|
||||
this._onPointerEnterBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onPointerEnterBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"pointerenter",
|
||||
this._onPointerEnterBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onPointerEnterBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaState.reportPointerLeave) {
|
||||
this.element.onpointerleave = (e) => {
|
||||
this._sendEventToBackend("pointerLeave", e, false);
|
||||
};
|
||||
} else {
|
||||
this.element.onpointerleave = null;
|
||||
if (deltaState.reportPointerLeave !== undefined) {
|
||||
if (this.state.reportPointerLeave) {
|
||||
if (this._onPointerLeaveBound === null) {
|
||||
this._onPointerLeaveBound = (e: PointerEvent) => {
|
||||
this._sendEventToBackend("pointerLeave", e, false);
|
||||
};
|
||||
this.element.addEventListener(
|
||||
"pointerleave",
|
||||
this._onPointerLeaveBound,
|
||||
{ capture: true }
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (this._onPointerLeaveBound !== null) {
|
||||
this.element.removeEventListener(
|
||||
"pointerleave",
|
||||
this._onPointerLeaveBound,
|
||||
{ capture: true } as AddEventListenerOptions
|
||||
);
|
||||
this._onPointerLeaveBound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -290,8 +388,8 @@ export class PointerEventListenerComponent extends ComponentBase<PointerEventLis
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the event as handled
|
||||
markEventAsHandled(event);
|
||||
// Mark the event as handled if needed
|
||||
if (this.state.consume_events) markEventAsHandled(event);
|
||||
|
||||
// Send the event
|
||||
this.sendMessageToBackend({
|
||||
|
||||
@@ -248,6 +248,8 @@ class MouseEventListener(FundamentalComponent):
|
||||
mouse button.
|
||||
|
||||
`on_drag_end`: Triggered when the user stops dragging the mouse.
|
||||
|
||||
`consume_events`: Consume events after they are processed.
|
||||
"""
|
||||
|
||||
content: rio.Component
|
||||
@@ -261,6 +263,7 @@ class MouseEventListener(FundamentalComponent):
|
||||
on_drag_start: rio.EventHandler[DragStartEvent] = None
|
||||
on_drag_move: rio.EventHandler[DragMoveEvent] = None
|
||||
on_drag_end: rio.EventHandler[DragEndEvent] = None
|
||||
consume_events: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
deprecations.warn(
|
||||
|
||||
@@ -171,6 +171,8 @@ class PointerEventListener(FundamentalComponent):
|
||||
leaves the component.
|
||||
|
||||
`on_drag_end`: Triggered when the user stops dragging the pointer.
|
||||
|
||||
`consume_events`: Consume events after they are processed.
|
||||
"""
|
||||
|
||||
content: rio.Component
|
||||
@@ -191,6 +193,7 @@ class PointerEventListener(FundamentalComponent):
|
||||
on_drag_start: rio.EventHandler[PointerEvent] = None
|
||||
on_drag_move: rio.EventHandler[PointerMoveEvent] = None
|
||||
on_drag_end: rio.EventHandler[PointerEvent] = None
|
||||
consume_events: bool = True
|
||||
|
||||
def _custom_serialize_(self) -> JsonDoc:
|
||||
return {
|
||||
|
||||
34
tests/test_frontend/test_mouse_event_listener.py
Normal file
34
tests/test_frontend/test_mouse_event_listener.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import pytest
|
||||
|
||||
import rio
|
||||
from rio.testing import BrowserClient
|
||||
|
||||
|
||||
@pytest.mark.parametrize("consume_events", [True, False])
|
||||
async def test_specific_button_events(consume_events: bool) -> None:
|
||||
down_events: list[rio.MouseDownEvent] = []
|
||||
up_events: list[rio.MouseUpEvent] = []
|
||||
|
||||
def on_mouse_down(e: rio.MouseDownEvent):
|
||||
down_events.append(e)
|
||||
|
||||
def on_mouse_up(e: rio.MouseUpEvent):
|
||||
up_events.append(e)
|
||||
|
||||
def build():
|
||||
return rio.MouseEventListener(
|
||||
rio.MouseEventListener(
|
||||
rio.Spacer(),
|
||||
on_mouse_down=on_mouse_down,
|
||||
on_mouse_up=on_mouse_up,
|
||||
),
|
||||
on_mouse_down=on_mouse_down,
|
||||
on_mouse_up=on_mouse_up,
|
||||
consume_events=consume_events,
|
||||
)
|
||||
|
||||
async with BrowserClient(build) as client:
|
||||
await client.click(0.5, 0.5, sleep=0.5)
|
||||
|
||||
assert len(down_events) == 1 + (not consume_events)
|
||||
assert len(up_events) == 1 + (not consume_events)
|
||||
@@ -43,7 +43,7 @@ async def test_on_double_press_event(
|
||||
|
||||
def build():
|
||||
return rio.PointerEventListener(
|
||||
rio.Spacer(), on_double_press=on_double_press
|
||||
rio.TextInput(), on_double_press=on_double_press
|
||||
)
|
||||
|
||||
async with BrowserClient(build) as client:
|
||||
@@ -70,36 +70,45 @@ async def test_on_double_press_event(
|
||||
ids=lambda buttons: "/".join(buttons),
|
||||
)
|
||||
@pytest.mark.parametrize("pressed_button", ["left", "middle", "right"])
|
||||
@pytest.mark.parametrize("consume_events", [True, False])
|
||||
async def test_specific_button_events(
|
||||
event_buttons: t.Sequence[t.Literal["left", "middle", "right"]],
|
||||
pressed_button: t.Literal["left", "middle", "right"],
|
||||
consume_events: bool,
|
||||
) -> None:
|
||||
down_event: rio.PointerEvent | None = None
|
||||
up_event: rio.PointerEvent | None = None
|
||||
down_events: list[rio.PointerEvent] = []
|
||||
up_events: list[rio.PointerEvent] = []
|
||||
|
||||
def on_pointer_down(e: rio.PointerEvent):
|
||||
nonlocal down_event
|
||||
down_event = e
|
||||
down_events.append(e)
|
||||
|
||||
def on_pointer_up(e: rio.PointerEvent):
|
||||
nonlocal up_event
|
||||
up_event = e
|
||||
up_events.append(e)
|
||||
|
||||
def build():
|
||||
return rio.PointerEventListener(
|
||||
rio.Spacer(),
|
||||
rio.PointerEventListener(
|
||||
rio.Spacer(),
|
||||
on_pointer_down={
|
||||
button: on_pointer_down for button in event_buttons
|
||||
},
|
||||
on_pointer_up={
|
||||
button: on_pointer_up for button in event_buttons
|
||||
},
|
||||
),
|
||||
on_pointer_down={
|
||||
button: on_pointer_down for button in event_buttons
|
||||
},
|
||||
on_pointer_up={button: on_pointer_up for button in event_buttons},
|
||||
consume_events=consume_events,
|
||||
)
|
||||
|
||||
async with BrowserClient(build) as client:
|
||||
await client.click(0.5, 0.5, button=pressed_button, sleep=0.5)
|
||||
|
||||
if pressed_button in event_buttons:
|
||||
assert down_event is not None
|
||||
assert up_event is not None
|
||||
assert len(down_events) == 1 + (not consume_events)
|
||||
assert len(up_events) == 1 + (not consume_events)
|
||||
else:
|
||||
assert down_event is None
|
||||
assert up_event is None
|
||||
assert len(down_events) == 0
|
||||
assert len(up_events) == 0
|
||||
|
||||
Reference in New Issue
Block a user