pub struct Interaction<Id> { /* private fields */ }Expand description
Ties Pointer, HitTester, and FocusRing together into the one
piece of state a draw pass needs to make its widgets interactive.
§Frame lifecycle
interaction.begin_frame(); // 1
for event in poll_events() {
interaction.handle_event(&event); // 2
}
draw(&mut term, &mut interaction, &state); // 3 -- calls interaction.interact(...)
interaction.end_frame(); // 4begin_framesnapshots which id (if any) is under the pointer, and whether it pressed/released/scrolled, using last frame’s hit registrations and pointer events: this frame’s registrations aren’t complete until step 3 finishes, and this frame’s events haven’t arrived yet (they’re step 2), so everyResponsein a given frame is one frame stale relative to what’s being drawn/fed in this frame – uniformly for hover, press, release, click, and scroll, all resolved from that one snapshot. At typical redraw rates this is imperceptible; it’s the same kind of trade-offListState::ensure_visibledocuments for a different reason (only the caller knows the current viewport height), applied here because only the previous frame knows the full hit list and the pointer’s position as of the input that’s about to be processed.draggingandResponse::heldare exceptions: both re-check the pointer’s live position (viaPointer::pos/Pointer::is_down) rather than the frame-stale snapshot, because a drag-in-progress or a press-cancel needs to react the instant the pointer moves, not one frame later. Keyboard focus is the remaining exception:Response::focusedand Enter/Space activation readFocusRing’scurrentlive, since it’s plain level state with no hit-testing involved – no staleness to trade off.handle_eventupdates pointer position/buttons and, by default, cycles focus on Tab/Shift+Tab.- Each widget calls
interactwith its rect, a caller-chosen id, and aSensedescribing what it cares about; it gets back aResponseand, as a side effect, registers itself for step 1 of the next frame. end_framereleases the active widget if step 1 saw the pointer go up.
One consequence worth knowing: a full press-then-release gesture that
arrives as two events in the same handle_event
batch (both fed in during step 2 of one frame, e.g. a synthetic test
firing them back to back) takes an extra frame to resolve versus a
realistic press and release arriving in separate frames, because step
1’s hover snapshot for that frame still reflects the pointer’s
position from before those events. Real input rarely lands this way
(a physical click’s down and up are milliseconds apart, i.e. several
frames at typical redraw rates), so this only tends to show up in tests.
§Why Id is a type parameter, not a hash
Immediate-mode toolkits like egui derive a widget’s identity from its
call-site source location (optionally salted with data) hashed down to
an opaque integer – flexible, but it means two widgets can collide onto
the same id at runtime with no compile-time signal, and the id carries
no meaning a debugger can show you. Interaction<Id> instead asks the
app for whatever id type it already has lying around – typically a
small Copy enum like the hand-rolled hit-target enum an app would
otherwise define anyway. Collisions become unrepresentable if the enum
is exhaustive, and {:?}-printing an id tells you exactly which widget
it is. The cost is one generic parameter; Id: Copy + PartialEq is all
any of this module asks for.
Consistently with that: everything here holds its state in a plain,
explicitly-owned struct threaded through &mut self, the same
convention ListState uses, rather than the
interior-mutability/global-context pattern egui’s Memory relies on to
keep its implicit ids from needing to be threaded everywhere.
Implementations§
Source§impl<Id> Interaction<Id>
impl<Id> Interaction<Id>
Sourcepub const fn with_drag_threshold(self, cells: u16) -> Self
pub const fn with_drag_threshold(self, cells: u16) -> Self
Override how far (in cells) the pointer must move from its press
origin before a Sense::DRAG widget reports
Response::dragging rather than a click-in-progress. Defaults to
DEFAULT_DRAG_THRESHOLD.
Sourcepub const fn pointer(&self) -> &Pointer
pub const fn pointer(&self) -> &Pointer
Read access to the pointer’s current position/button/scroll state, e.g. to draw a custom cursor glyph.
Source§impl<Id: Copy + PartialEq> Interaction<Id>
impl<Id: Copy + PartialEq> Interaction<Id>
Sourcepub fn begin_frame(&mut self)
pub fn begin_frame(&mut self)
Resolve hover/press against last frame’s registrations, finalize the
focus order, and clear the hit registry for this frame’s
interact calls. Call once per frame, before
processing input or drawing.
Sourcepub fn handle_event(&mut self, event: &Event)
pub fn handle_event(&mut self, event: &Event)
Feed a raw input event: updates the pointer, and (by default) Tab
cycles focus – see FocusRing::handle_event if you need to
override that.
Trait Implementations§
Source§impl<Id: Clone> Clone for Interaction<Id>
impl<Id: Clone> Clone for Interaction<Id>
Source§fn clone(&self) -> Interaction<Id>
fn clone(&self) -> Interaction<Id>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more