[PR #502] [MERGED] core: debounce hover recheck after hit-grid changes #1363

Closed
opened 2026-03-14 09:32:47 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/anomalyco/opentui/pull/502
Author: @simonklee
Created: 1/8/2026
Status: Merged
Merged: 1/12/2026
Merged by: @kommander

Base: mainHead: debounce-hover


📝 Commits (8)

  • c727c0f core: debounce hover recheck after hit-grid changes
  • 35594df fix(renderer): fix debounced hover rechecks and z-index hit/hover updates
  • aa7bdb2 Merge branch 'main' into debounce-hover
  • ae61993 check hitgrid dirty in native before swapping
  • 2dd819e add back last ponter modifiers
  • c402472 Merge branch 'main' into debounce-hover
  • 9454e5a remove dead code
  • cc746a1 add examples to index

📊 Changes

7 files changed (+616 additions, -4 deletions)

View changed files

📝 packages/core/src/Renderable.ts (+6 -2)
📝 packages/core/src/examples/index.ts (+14 -0)
📝 packages/core/src/renderer.ts (+53 -0)
📝 packages/core/src/tests/scrollbox-hitgrid.test.ts (+518 -2)
📝 packages/core/src/zig.ts (+9 -0)
📝 packages/core/src/zig/lib.zig (+4 -0)
📝 packages/core/src/zig/renderer.zig (+12 -0)

📄 Description

So, the core issue was that scrolling or animating elements left the hover state stale because we were only running hit tests on actual mouse movement. If the element moved but the mouse didn't, we wouldn't catch it.

To fix this without tanking performance during scroll, we implemented a dirty-flag + debounce system:

  1. Dirty Tracking: I added a hitGridDirty flag to the renderer. Now, whenever a Renderable changes a property that affects hit testing (translateX/Y, zIndex, visible, overflow, or layout shifts), it calls markHitGridDirty().

  2. Debounced Recheck: At the end of the render loop, if the grid is dirty, we schedule a debounced hover recheck. This groups many updates into a single check instead of running hit logic every frame.

  3. Synthetic Events: The recheck uses the last known pointer position. If the target under the cursor changes, we fire out and over events to update the UI.

I cancel this check if the user moves the mouse, since that triggers an immediate test. It's also skipped during drags to avoid messing with drop targets.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/anomalyco/opentui/pull/502 **Author:** [@simonklee](https://github.com/simonklee) **Created:** 1/8/2026 **Status:** ✅ Merged **Merged:** 1/12/2026 **Merged by:** [@kommander](https://github.com/kommander) **Base:** `main` ← **Head:** `debounce-hover` --- ### 📝 Commits (8) - [`c727c0f`](https://github.com/anomalyco/opentui/commit/c727c0fdcc579cfc484ec1959c4c5b52fd46a68d) core: debounce hover recheck after hit-grid changes - [`35594df`](https://github.com/anomalyco/opentui/commit/35594dfe0bff4c8a6d97ada94b3aef8f339e9870) fix(renderer): fix debounced hover rechecks and z-index hit/hover updates - [`aa7bdb2`](https://github.com/anomalyco/opentui/commit/aa7bdb2b50bfe6382b6ce1fa3c0a0522fcaa182d) Merge branch 'main' into debounce-hover - [`ae61993`](https://github.com/anomalyco/opentui/commit/ae61993d30e5fb50b5b317d10ef9d5d6e70ba3f1) check hitgrid dirty in native before swapping - [`2dd819e`](https://github.com/anomalyco/opentui/commit/2dd819efe62329658fbad8248690f6acb2464089) add back last ponter modifiers - [`c402472`](https://github.com/anomalyco/opentui/commit/c40247207d98c8776ff311647d7d3d146470d404) Merge branch 'main' into debounce-hover - [`9454e5a`](https://github.com/anomalyco/opentui/commit/9454e5a8c075873108961c5bf528e7895cbfd989) remove dead code - [`cc746a1`](https://github.com/anomalyco/opentui/commit/cc746a11a897f3f0b9445f2b19e904c2d0764215) add examples to index ### 📊 Changes **7 files changed** (+616 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `packages/core/src/Renderable.ts` (+6 -2) 📝 `packages/core/src/examples/index.ts` (+14 -0) 📝 `packages/core/src/renderer.ts` (+53 -0) 📝 `packages/core/src/tests/scrollbox-hitgrid.test.ts` (+518 -2) 📝 `packages/core/src/zig.ts` (+9 -0) 📝 `packages/core/src/zig/lib.zig` (+4 -0) 📝 `packages/core/src/zig/renderer.zig` (+12 -0) </details> ### 📄 Description So, the core issue was that scrolling or animating elements left the hover state stale because we were only running hit tests on actual mouse movement. If the element moved but the mouse didn't, we wouldn't catch it. To fix this without tanking performance during scroll, we implemented a dirty-flag + debounce system: 1. Dirty Tracking: I added a hitGridDirty flag to the renderer. Now, whenever a Renderable changes a property that affects hit testing (translateX/Y, zIndex, visible, overflow, or layout shifts), it calls markHitGridDirty(). 2. Debounced Recheck: At the end of the render loop, if the grid is dirty, we schedule a debounced hover recheck. This groups many updates into a single check instead of running hit logic every frame. 3. Synthetic Events: The recheck uses the last known pointer position. If the target under the cursor changes, we fire out and over events to update the UI. I cancel this check if the user moves the mouse, since that triggers an immediate test. It's also skipped during drags to avoid messing with drop targets. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-03-14 09:32:47 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#1363
No description provided.