Skip to content

boxen core: implement modal/focus stack for proper nested-modal behavior #798

Description

@jsavin

Problem

boxen_window_set_modal(win, true) (boxen.c) sets a per-window flag but does NOT maintain a focus or modal stack. When a UserTalk script calls dialog.getString from inside a dialog.getString callback (legal, even if uncommon), the outer modal opens win1+focus, the inner opens win2+focus. When win2 closes, nothing automatically re-focuses win1.

PR #793 mitigated this for the common case (the bridge's restore_focus callback hands focus to the REPL input bar after every close), but that's wrong when an outer modal is still open. The outer's mini-loop re-focuses its own window on the next 100ms tick (via boxen_window_invalidate + present), so the user sees a brief flash where the outer modal looks unfocused.

Fix

Implement a proper modal stack in boxen core:

  1. boxen_window_set_modal(win, true) pushes win onto a per-compositor modal stack.
  2. boxen_window_close(win) pops win from the stack (if present).
  3. Focus + input routing always goes to stack.top().
  4. boxen_window_set_modal(win, false) removes from the stack (rare; typically just close).

The bridge's restore_focus callback then becomes either:

  • A no-op (the stack handles it), or
  • Removed entirely if boxen_window_close does the right thing automatically.

Affected callers to verify:

Context

Acceptance

  • Nested dialog modal scenario: outer + inner, close inner, outer immediately regains focus and accepts keystrokes (no 100ms gap)
  • Existing palette + completion popup behavior unchanged (regression check via tui-tests)
  • Test that drives nested dialogs (probably needs a synthetic UserTalk script)

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority/p2Medium priority - nice to havescope/medium1-2 weeks, requires design worktype/featureNew feature or requestworkstream/verb-portingKernel verb implementation and bindings

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions