McRogueFace/src
John McCardle 4bb1966b97 fix(input): dispatch hover-exit when the cursor leaves the window
closes #363

Hover was driven exclusively by sf::Event::MouseMoved -> PyScene::do_mouse_hover.
Nothing anywhere handled sf::Event::MouseLeft, so when the cursor left the window
while over a drawable, no further MouseMoved arrived and the hover state simply
froze: on_exit / on_cell_exit never fired, and Grid.hovered_cell kept reporting a
cell the mouse was nowhere near. Any hover-driven affordance (tile highlight,
tooltip, range preview) stayed stranded in its hovered visual until the player
re-entered the window AND crossed a boundary.

Pre-existing gap in the input layer -- it hit Frame.on_exit too. #355 only made it
observable, by giving grids hover callbacks that could get stuck.

The fix reuses the machinery rather than adding a parallel teardown path.
do_mouse_hover's walk becomes dispatch_hover(pos, in_window); a window-leave is
just that walk with hit_allowed=false at the root, which is precisely "the mouse is
over nothing": every hovered drawable takes its ordinary exit path, and none can
enter. So grid cells, frames, and grid overlay children all clear through the code
that already knows how to clear them.

sf::Event::MouseLeft carries no coordinates, so exit callbacks get the last
position seen by do_mouse_hover (the convention the DOM's mouseleave uses). There
is no more recent position, and inventing one would be a lie.

All three backends feed this: desktop SFML emits MouseLeft natively, SDL2Renderer
already translated SDL_WINDOWEVENT_LEAVE to it (and had nobody listening), and
headless reaches it via the new automation.mouseLeave(), which takes no position
for the same reason the event carries none.

Test asserts the cell exit fires, hovered_cell goes None, a redundant leave does
not double-fire, and re-entering the SAME cell fires on_cell_enter again -- that
last one is what distinguishes a real clear from a faked one.

api_surface golden re-baselined: +1 line, automation.mouseLeave.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 21:12:58 -04:00
..
3d Phase 1: Safety & performance foundation for Grid/Entity overhaul 2026-03-15 21:48:24 -04:00
audio Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
ldtk Fast path for hot property getters: stop importing mcrfpy per call; closes #331 2026-07-08 21:58:50 -04:00
platform SDL key scancode fixes (7DRL 2026 hotfix) 2026-03-07 10:08:59 -05:00
scripts Remove camelCase module functions (setScale, findAll, getMetrics, setDevConsole), closes #304 2026-04-10 01:07:22 -04:00
scripts_demo Fix demo game fog layer accumulation and web IDBFS mkdir race 2026-04-10 02:19:19 -04:00
scripts_playground opacity + animation fixes 2026-02-03 12:18:21 -05:00
tiled Fast path for hot property getters: stop importing mcrfpy per call; closes #331 2026-07-08 21:58:50 -04:00
ActionCode.h Add MCRF_HEADLESS compile-time build option for #158 2026-01-30 23:09:07 -05:00
Animation.cpp perf(animation): scalar-float fast path skipping variant visits; closes #342 2026-07-10 22:59:28 -04:00
Animation.h perf(animation): scalar-float fast path skipping variant visits; closes #342 2026-07-10 22:59:28 -04:00
BenchmarkLogger.cpp feat: Add work_time_ms to benchmark logging for load analysis 2025-11-28 16:13:40 -05:00
BenchmarkLogger.h feat: Add work_time_ms to benchmark logging for load analysis 2025-11-28 16:13:40 -05:00
CommandLineParser.cpp Add build plumbing for libFuzzer+ASan fuzz build, addresses #283 2026-04-10 10:35:44 -04:00
CommandLineParser.h Squashed commit of the following: [interpreter_mode] 2025-07-05 17:23:09 -04:00
Common.h Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL 2026-01-31 11:13:15 -05:00
DiscreteMap.cpp Windowed perspective writeback in UIEntity::updateVisibility; closes #316 2026-06-21 09:40:05 -04:00
DiscreteMap.h Windowed perspective writeback in UIEntity::updateVisibility; closes #316 2026-06-21 09:40:05 -04:00
emscripten_pre.js Add Emscripten shell and pre-JS for browser compatibility 2026-01-31 14:36:22 -05:00
EntityBehavior.cpp refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
EntityBehavior.h Refactor EntityBehavior SEEK/FLEE to use PathProvider strategy; refs #315 2026-04-18 09:19:05 -04:00
GameEngine.cpp fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
GameEngine.h Animation and Scene clean up functions. Playground build target 2026-02-01 21:17:29 -05:00
GridData.cpp fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
GridData.h fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
GridLayers.cpp feat(grid): numpy buffer views for ColorLayer/TileLayer via layer.edit(); closes #335 2026-07-11 02:04:42 -04:00
GridLayers.h feat(grid): numpy buffer views for ColorLayer/TileLayer via layer.edit(); closes #335 2026-07-11 02:04:42 -04:00
HeadlessRenderer.cpp Squashed commit of the following: [interpreter_mode] 2025-07-05 17:23:09 -04:00
HeadlessRenderer.h Add MCRF_HEADLESS compile-time build option for #158 2026-01-30 23:09:07 -05:00
ImGuiConsole.cpp Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL 2026-01-31 11:13:15 -05:00
ImGuiConsole.h Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL 2026-01-31 11:13:15 -05:00
ImGuiSceneExplorer.cpp fix(imgui): scene explorer can expand grid children and entities 2026-07-12 18:02:11 -04:00
ImGuiSceneExplorer.h Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL 2026-01-31 11:13:15 -05:00
IndexTexture.cpp Refactor: Python 3.12, build libtcod & SFML from source. Cmake build. Directory cleanup 2024-02-24 22:48:39 -05:00
IndexTexture.h Refactor: Python 3.12, build libtcod & SFML from source. Cmake build. Directory cleanup 2024-02-24 22:48:39 -05:00
main.cpp Add MCRF_HEADLESS compile-time build option for #158 2026-01-30 23:09:07 -05:00
MapOps.h DiscreteMap class - mask for operations or uint8 tile data 2026-02-03 20:36:42 -05:00
McRFPy_API.cpp fix(grid): find()/findAll() descend into grids again 2026-07-12 18:02:04 -04:00
McRFPy_API.h Animation and Scene clean up functions. Playground build target 2026-02-01 21:17:29 -05:00
McRFPy_Automation.cpp fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
McRFPy_Automation.h fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
McRFPy_Doc.h F15: convert frozen binding docstrings to MCRF_* macros (#314) 2026-06-21 01:20:55 -04:00
McRogueFaceConfig.h feat: Exit on first Python callback exception (closes #133) 2025-11-26 10:26:30 -05:00
McRogueFaceVersion.h Set version 0.2.8 for release; fix Linux package libtcod SONAME 2026-06-30 19:41:58 -04:00
PathProvider.cpp refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
PathProvider.h Refactor EntityBehavior SEEK/FLEE to use PathProvider strategy; refs #315 2026-04-18 09:19:05 -04:00
Profiler.cpp feat: Add comprehensive profiling system with F3 overlay 2025-10-25 00:45:44 -04:00
Profiler.h feat: Add comprehensive profiling system with F3 overlay 2025-10-25 00:45:44 -04:00
ProfilerOverlay.cpp feat: Add comprehensive profiling system with F3 overlay 2025-10-25 00:45:44 -04:00
PyAlignment.cpp Alignment: reactive or automatically calculated repositioning of UIDrawables on their parent 2026-01-13 20:40:34 -05:00
PyAlignment.h Alignment: reactive or automatically calculated repositioning of UIDrawables on their parent 2026-01-13 20:40:34 -05:00
PyAnimation.cpp Grid/GridView API unification: mcrfpy.Grid now returns GridView, closes #252 2026-04-04 04:34:11 -04:00
PyAnimation.h Remove deprecated sprite_number property from Sprite and Entity, closes #305 2026-04-09 22:18:20 -04:00
PyBehavior.cpp Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
PyBehavior.h Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
PyBSP.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
PyBSP.h Add MCRF_HEADLESS compile-time build option for #158 2026-01-30 23:09:07 -05:00
PyCallable.cpp perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
PyCallable.h Simplify on_enter/on_exit callbacks to position-only signature 2026-01-28 17:36:02 -05:00
PyColor.cpp Add Color.__eq__/__ne__ for value comparison, closes #307 2026-04-09 21:18:47 -04:00
PyColor.h Freeze value semantics + record 1.0 compatibility policy 2026-07-02 20:23:11 -04:00
PyDiscreteMap.cpp feat(discretemap): buffer protocol for zero-copy numpy views; closes #334 2026-07-11 01:00:53 -04:00
PyDiscreteMap.h feat(discretemap): buffer protocol for zero-copy numpy views; closes #334 2026-07-11 01:00:53 -04:00
PyDrawable.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
PyDrawable.h Squashed commit of 53 Commits: [alpha_streamline_2] 2025-07-09 22:41:15 -04:00
PyEasing.cpp animation loop parameter 2026-02-27 22:11:29 -05:00
PyEasing.h Easing functions as enum 2026-01-04 12:59:28 -05:00
PyFont.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PyFont.h Improve terse docstrings on Vector, Font, Texture, GridPoint, GridPointState 2026-04-09 21:18:33 -04:00
PyFOV.cpp feat: Implement FOV enum and layer draw_fov for #114 and #113 2025-12-01 15:18:10 -05:00
PyFOV.h feat: Implement FOV enum and layer draw_fov for #114 and #113 2025-12-01 15:18:10 -05:00
PyHeightMap.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PyHeightMap.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyHeuristic.cpp Extend pathfinding API with heuristics, multi-root Dijkstra, and FLEE primitives; refs #315 2026-04-18 09:18:49 -04:00
PyHeuristic.h Extend pathfinding API with heuristics, multi-root Dijkstra, and FLEE primitives; refs #315 2026-04-18 09:18:49 -04:00
PyInputState.cpp perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
PyInputState.h perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
PyKey.cpp perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
PyKey.h perf(input): cache Key/InputState enum members per event; closes #344 2026-07-10 22:59:47 -04:00
PyKeyboard.cpp feat: Add Sound/Music classes, keyboard state, version (#66, #160, #164) 2025-12-29 16:24:27 -05:00
PyKeyboard.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyLock.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
PyLock.h Fix #219: Add threading support with mcrfpy.lock() context manager 2026-01-19 23:37:49 -05:00
PyMouse.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PyMouse.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyMouseButton.cpp Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
PyMouseButton.h Remove legacy string enum comparisons from InputState/Key/MouseButton, closes #306 2026-04-09 22:19:02 -04:00
PyMusic.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PyMusic.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyNoiseSource.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PyNoiseSource.h Noise, combination, and sampling: first pass at #207, #208, #194, #209 2026-01-12 19:01:20 -05:00
PyPerspective.cpp Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294 2026-04-17 23:04:27 -04:00
PyPerspective.h Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294 2026-04-17 23:04:27 -04:00
PyPositionHelper.h Positions are always mcrfpy.Vector, Vector/tuple/iterables expected as inputs, and for position-only inputs we permit x,y args to prevent requiring double-parens 2026-01-05 10:16:16 -05:00
PyScene.cpp fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
PyScene.h fix(input): dispatch hover-exit when the cursor leaves the window 2026-07-12 21:12:58 -04:00
PySceneObject.cpp perf(scene): skip per-frame update() lookup for non-subclassed scenes; closes #343 2026-07-10 23:00:04 -04:00
PySceneObject.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyShader.cpp Shaders 2026-01-25 21:04:01 -05:00
PyShader.h Shaders 2026-01-25 21:04:01 -05:00
PySound.cpp Replace PyObject_GetAttrString with direct type references 2026-03-07 23:18:42 -05:00
PySound.h Add SoundBuffer type: procedural audio, sfxr synthesis, DSP effects 2026-02-19 18:58:11 -05:00
PySoundBuffer.cpp Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
PySoundBuffer.h Audio fixes: gain() DSP effect, sfxr phase wrap, SDL2 backend compat 2026-02-20 23:17:41 -05:00
PyTexture.cpp Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
PyTexture.h Add texture display bounds for non-uniform sprite content, closes #235 2026-04-10 02:57:41 -04:00
PythonObjectCache.cpp Fix PythonObjectCache race and document die() iteration (closes #269, closes #273) 2026-03-07 23:33:05 -05:00
PythonObjectCache.h Squashed commit of the following: [alpha_presentable] 2025-07-15 21:30:49 -04:00
PyTimer.cpp Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
PyTimer.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
PyTransition.cpp scene transitions via Scene object 2026-01-03 13:53:18 -05:00
PyTransition.h scene transitions via Scene object 2026-01-03 13:53:18 -05:00
PyTrigger.cpp Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
PyTrigger.h Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
PyUniformBinding.cpp Shaders 2026-01-25 21:04:01 -05:00
PyUniformBinding.h Shaders 2026-01-25 21:04:01 -05:00
PyUniformCollection.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
PyUniformCollection.h Fix UniformCollection owner validity check (closes #272) 2026-03-08 17:07:14 -04:00
PyVector.cpp Reject non-finite floats before int casts; closes #323, closes #324, closes #325 2026-06-26 23:53:02 -04:00
PyVector.h Freeze value semantics + record 1.0 compatibility policy 2026-07-02 20:23:11 -04:00
PyWindow.cpp Add MCRF_HEADLESS compile-time build option for #158 2026-01-30 23:09:07 -05:00
PyWindow.h Migrate static PyTypeObject to inline, delete PyTypeCache workarounds 2026-02-16 20:58:09 -05:00
Resources.cpp Adding text functionality to Caption (why was that missing???) 2024-03-07 12:35:07 -05:00
Resources.h Adding text functionality to Caption (why was that missing???) 2024-03-07 12:35:07 -05:00
Scene.cpp feat: Implement Phase A UI hierarchy foundations (closes #122, #102, #116, #118) 2025-11-27 16:33:17 -05:00
Scene.h feat: Implement Phase A UI hierarchy foundations (closes #122, #102, #116, #118) 2025-11-27 16:33:17 -05:00
SceneTransition.cpp Mobile-"ish" emscripten support 2026-02-09 08:40:34 -05:00
SceneTransition.h Mobile-"ish" emscripten support 2026-02-09 08:40:34 -05:00
shell.html Cross-platform persistent save directory (IDBFS on WASM, filesystem on desktop) 2026-02-25 20:42:44 -05:00
shell_game.html Cross-platform persistent save directory (IDBFS on WASM, filesystem on desktop) 2026-02-25 20:42:44 -05:00
SpatialHash.cpp Add multi-tile entity support with tile_width/tile_height, closes #236 2026-04-10 02:57:47 -04:00
SpatialHash.h Phase 2: Entity data model extensions for behavior system 2026-03-15 22:05:06 -04:00
Timer.cpp Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
Timer.h Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
UI.h Squashed commit of the following: [break_up_ui_h] 2024-04-20 10:32:04 -04:00
UIArc.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
UIArc.h Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
UIBase.h fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UICaption.cpp Add read-only Caption.font getter; addresses #320 2026-06-21 12:18:53 -04:00
UICaption.h Add read-only Caption.font getter; addresses #320 2026-06-21 12:18:53 -04:00
UICircle.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
UICircle.h Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
UICollection.cpp fix(grid): UICollection.repr names Grids again instead of "UIDrawable" 2026-07-12 20:26:32 -04:00
UICollection.h Python API improvements: Vectors, bounds, window singleton, hidden types 2026-01-05 23:00:48 -05:00
UIDrawable.cpp Fast path for hot property getters: stop importing mcrfpy per call; closes #331 2026-07-08 21:58:50 -04:00
UIDrawable.h fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UIEntity.cpp fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UIEntity.h Windowed perspective writeback in UIEntity::updateVisibility; closes #316 2026-06-21 09:40:05 -04:00
UIEntityCollection.cpp Back EntityCollection with std::vector for O(1) indexing; closes #329 2026-07-02 20:16:04 -04:00
UIEntityCollection.h Back EntityCollection with std::vector for O(1) indexing; closes #329 2026-07-02 20:16:04 -04:00
UIEntityPyMethods.h Fix UniformCollection owner validity check (closes #272) 2026-03-08 17:07:14 -04:00
UIFrame.cpp docs(grid): document grid-world child coordinates and the overlay pattern 2026-07-12 18:02:29 -04:00
UIFrame.h Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
UIGrid.cpp fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UIGrid.h fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UIGridPathfinding.cpp F15: convert frozen binding docstrings to MCRF_* macros (#314) 2026-06-21 01:20:55 -04:00
UIGridPathfinding.h Extend pathfinding API with heuristics, multi-root Dijkstra, and FLEE primitives; refs #315 2026-04-18 09:18:49 -04:00
UIGridPoint.cpp refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
UIGridPoint.h refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
UIGridPyMethods.cpp fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UIGridPyProperties.cpp docs(grid): document grid-world child coordinates and the overlay pattern 2026-07-12 18:02:29 -04:00
UIGridView.cpp fix(grid): ~UIGridView unregisters from the GridData view registry 2026-07-12 20:27:28 -04:00
UIGridView.h fix(grid): revive Grid input — move cell callbacks and hit-testing to GridView; N-view registry 2026-07-12 18:01:40 -04:00
UILine.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
UILine.h Fix callback/timer GC: prevent premature destruction of Python callbacks 2026-02-19 20:53:50 -05:00
UISprite.cpp F15: correct docstring accuracy from adversarial verify pass (#314) 2026-06-21 06:43:47 -04:00
UISprite.h Add compound Color and Vector animation targets (pos, fill_color), closes #218 2026-04-10 02:05:55 -04:00
UITestScene.cpp refactor(grid): GridData SoA -- dense uint8 planes for walkable/transparent; closes #332 2026-07-11 01:43:30 -04:00
UITestScene.h Refactor: remove "s" prefix from "sRender" method ( -> "render") Closes #44 2024-04-20 14:16:14 -04:00