Feed the mouse to the UI and tell the host which events it consumed
akgl_ui_handle_event is the mouse half of the input story -- keyboard and gamepad stay with the control maps, because keyboard focus is something an application declares rather than something a pointer position implies. It handles motion, left-button presses and releases, the wheel and window resizes; everything else, and everything while the subsystem is inert, reports consumed=false and succeeds, the same pass-everything contract akgl_controller_handle_event already has. The documented call order is UI first, control maps second, skipping on consumed. A press, release or wheel is consumed when the pointer is over any UI element. The hit test asks clay against the layout the previous frame retained -- this frame's does not exist while events are polled, and one frame of staleness is the standard model's accepted cost -- with clay's internal full-screen Clay__RootContainer excluded, because being inside the window is not being over the interface (unexcluded, every click anywhere was consumed; the test that pins this caught it). Pointer state is fed to clay per event, as clay documents; the press edge is latched once per frame_begin for the widgets to come, rather than trusting Clay_PointerData's per-SetPointerState edge, which advances per mouse event. frame_begin also drives Clay_UpdateScrollContainers from the accumulated wheel (32 pixels per notch) and a wall-clock dt. Tests pin the whole contract: pass-through before init, no consumption before any layout exists, press/release inside vs beside a panel, right button ignored, motion never consumed, wheel consumed only over the UI, resize reaching the layout engine, and key events left alone. Co-Authored-By: Claude Code (Claude Fable 5, claude-fable-5) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
@@ -38,6 +38,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <akerror.h>
|
||||
#include <clay.h>
|
||||
|
||||
@@ -205,6 +207,50 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_ui_resize(int width, int height);
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_ui_font_register(char *name, uint16_t *fontid);
|
||||
|
||||
/**
|
||||
* @brief Feed one SDL event to the UI, and learn whether the UI claimed it.
|
||||
*
|
||||
* The mouse half of the input story -- keyboard and gamepad stay with the
|
||||
* control maps and the widget helpers, because keyboard focus is something
|
||||
* the application declares, not something a pointer position implies. This
|
||||
* handles mouse motion, left-button presses and releases, the wheel, and
|
||||
* window resizes; every other event, and every event while the subsystem is
|
||||
* not initialized, reports @p consumed false and succeeds, so a host passes
|
||||
* every event through unconditionally -- the same contract
|
||||
* akgl_controller_handle_event() has.
|
||||
*
|
||||
* Call it *before* akgl_controller_handle_event() in the event loop, and skip
|
||||
* the rest of the chain when it consumed:
|
||||
*
|
||||
* while ( SDL_PollEvent(&event) ) {
|
||||
* PASS(errctx, akgl_ui_handle_event(&app, &event, &consumed));
|
||||
* if ( consumed ) { continue; }
|
||||
* PASS(errctx, akgl_controller_handle_event(&app, &event));
|
||||
* }
|
||||
*
|
||||
* A press or release is consumed when the pointer is over any UI element;
|
||||
* motion and resizes never are (the game may care where the mouse is, and
|
||||
* certainly cares about its window). The hit test runs against the layout
|
||||
* the *previous* frame declared, because this frame's does not exist while
|
||||
* events are being polled -- clay retains the last tree for exactly this
|
||||
* purpose, and one frame of staleness is the accepted cost of the standard
|
||||
* model. Before any frame has been laid out, nothing is over anything and
|
||||
* nothing is consumed.
|
||||
*
|
||||
* @param appstate The application state pointer the event loop owns.
|
||||
* Required, though this implementation does not read it --
|
||||
* the parameter exists so the signature matches
|
||||
* akgl_controller_handle_event() and can grow the same way.
|
||||
* @param event The event SDL handed the loop. Required.
|
||||
* @param consumed Receives whether the UI claimed the event. Required.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER If @p appstate, @p event, or @p consumed is
|
||||
* `NULL`.
|
||||
* @throws AKERR_OUTOFBOUNDS If a resize event carries a non-positive size,
|
||||
* from akgl_ui_resize().
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_ui_handle_event(void *appstate, SDL_Event *event, bool *consumed);
|
||||
|
||||
/**
|
||||
* @brief Open the UI frame: clear the error stash and begin the clay layout.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user