Property grid: Tab/Shift+Tab traverses to next/prev property (fixes #5697)#6529
Property grid: Tab/Shift+Tab traverses to next/prev property (fixes #5697)#6529heffneil wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds custom Tab/Shift+Tab navigation behavior to xlPropertyGrid so keyboard traversal moves between visible properties (when editing) instead of jumping focus to sibling widgets.
Changes:
- Adds
wxEVT_CHAR_HOOKhandler to intercept Tab/Shift+Tab while an editor is focused. - Implements deferred property selection after committing edits to avoid pointer invalidation after grid rebuilds.
- Includes
wx/propgrid/propgridiface.hto support newly used PropertyGrid APIs.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Bail out if CommitChangesFromEditor() returns false so a validation rejection keeps focus on the current editor instead of silently moving on (Copilot comment xLightsSequencer#2). - Move OnCharHook to private; it is only used as a Bind-target and was never meant to be part of the public API (Copilot comment xLightsSequencer#3). Copilot comment xLightsSequencer#1 (iterator underflow) was investigated and dismissed: wxPropertyGridIteratorBase::Prev() correctly sets m_property = nullptr at the boundary and AtEnd() catches it; the suggested GetNextVisible/GetPrevVisible helpers do not exist on wxPropertyGridInterface in this wx fork. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes xLightsSequencer#5697. Stock wxPropertyGrid handles Tab in an open editor by calling Navigate(IsForward), which moves keyboard focus to the next sibling widget of the grid rather than to the next property within it - so the user had to click their way back into the grid to edit the next field. Override Tab in xlPropertyGrid via wxEVT_CHAR_HOOK: - Plain Tab advances to the next visible property and focuses its editor; Shift+Tab moves backward. - Ctrl+Tab / Alt+Tab and Tab from a non-editor context fall through to wx default traversal (so the user can still exit the grid). - Tab at the first/last visible property also falls through. Committing the current edit fires wxEVT_PG_CHANGED, which can queue a model reload that rebuilds the entire grid and invalidates the property pointers we got from the iterator. To survive that, the focus shift is deferred via CallAfter and the next property is looked up by name (not by stale pointer). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bail out if CommitChangesFromEditor() returns false so a validation rejection keeps focus on the current editor instead of silently moving on (Copilot comment xLightsSequencer#2). - Move OnCharHook to private; it is only used as a Bind-target and was never meant to be part of the public API (Copilot comment xLightsSequencer#3). Copilot comment xLightsSequencer#1 (iterator underflow) was investigated and dismissed: wxPropertyGridIteratorBase::Prev() correctly sets m_property = nullptr at the boundary and AtEnd() catches it; the suggested GetNextVisible/GetPrevVisible helpers do not exist on wxPropertyGridInterface in this wx fork. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nd, wheel no longer edits - Skip category/section headers when tabbing so focus lands on the next editable property - Gate on GetSelection() instead of IsEditorFocused() so combo/choice editors advance instead of dropping focus out of the grid - Wrap around at the ends (last->first, first->last) so Tab cycles - Redirect the mouse wheel from the active editor to the grid so scrolling never changes a spin/combo value (which rebuilt the grid and lost the selection) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9208448 to
c87a21f
Compare
|
@derwin12 — good catch, and you were right that the first cut was nearly useless. Fixed and pushed. The root cause: the handler only advanced when wx reported an editor as focused, which is only true for plain text editors. Click-to-Edit fields (Start Channel, Preview), choice/combo dropdowns, and disabled rows don't report editor focus, so Tab fell straight out of the grid after a few fields — exactly the "5-6 fields then stuck" you saw. What changed:
Tested locally on the model property grid: Tab/Shift+Tab now traverses the entire list end-to-end and back. Give it another run when you get a chance. |
Skip only EXPANDED category headers (descend to their first child); stop on COLLAPSED ones so the user can expand them (Right arrow) and Tab again to enter the section. Otherwise wxPG_ITERATE_VISIBLE hides a collapsed category's children and Tab skips the whole section. Applied to both the forward step and the wrap-around via a shared skippable() lambda. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Major usability win here. Built and tested on macOS 26.3.1. These bits of little polish go a long way in making xLights feel more like the world-class piece of software that it is. |

Summary
Closes #5697.
Stock wxPropertyGrid handles Tab in an open editor by calling
Navigate(IsForward), which moves keyboard focus to the next sibling widget of the grid rather than to the next property within it. The result on the Layout tab's Model pane: type a value (e.g. # Lights Top), press Tab, and focus jumps out to the 3D preview — the user has to click back into the next field every time.Despite a long-standing assumption that this isn't fixable in wxWidgets, wxPropertyGrid does expose enough to override the behavior cleanly.
Fix
Override Tab in our existing
xlPropertyGridsubclass (src-ui-wx/shared/utils/xlPropertyGrid.h) viawxEVT_CHAR_HOOK:Scroll no longer edits values
Spin/choice editors capture the mouse wheel and change their value, which fires the same grid rebuild (below) and bounced the selection back to the top. The active editor's wheel events are now redirected to scroll the grid, so scrolling never mutates a value or loses your place.
Survival across grid rebuilds
Committing the current edit fires
wxEVT_PG_CHANGED, whichLayoutPanel::OnPropertyGridChangetranslates into a model reload that rebuilds the entire grid. That rebuild invalidates thewxPGProperty*pointers we got from the iterator and (without this fix) leaves focus on the layout canvas, because the editor we just focused is destroyed mid-flight.To survive that:
CommitChangesFromEditor()is called explicitly so the value lands before we move on; if it fails (validation rejects the value) we stay put.CallAfter, so it runs after any rebuild queued by the commit has settled.GetPropertyByName(nextName)), not the now-stale pointer.Test plan
13in# Lights Topand press Tab → focus lands in the next field, value 13 is saved🤖 Generated with Claude Code