Skip to content

Add DockEvent stream + show_inside_with_response#323

Open
enomado wants to merge 1 commit intoanhosh:mainfrom
enomado:feat/dock-events
Open

Add DockEvent stream + show_inside_with_response#323
enomado wants to merge 1 commit intoanhosh:mainfrom
enomado:feat/dock-events

Conversation

@enomado
Copy link
Copy Markdown
Contributor

@enomado enomado commented May 7, 2026

Motivation

Currently DockArea::show_inside returns (), so a host that wants to push an undo entry on layout changes has to diff the serialised state every frame. That diff is non-zero on every frame the user holds and drags a separator, which produces one undo entry per frame instead of one per drag.

What this PR adds

  • DockArea::show_inside_with_response(ui, viewer) -> DockAreaResponse. The existing show_inside is kept as a thin wrapper — no behaviour change for current callers.
  • DockAreaResponse { events: Vec<DockEvent> } with helpers:
    • layout_changed() — any mutation this frame, including in-progress drag.
    • layout_committed() — finalised mutations only; right trigger for undo / disk save.
  • DockEvent enum with two variants today:
    • SeparatorDragging — emitted per frame while the user holds and drags a separator (continuous).
    • LayoutCommitted — finalised: tab close / move / detach, leaf collapse, window minimise, separator drag end / arrow-key nudge / double-click reset, focus change.

Both DockEvent and DockAreaResponse are #[non_exhaustive] so future fine-grained variants (SeparatorDragCommitted { before, after }, TabClosed(TabPath), …) can land without breaking consumers that go through layout_committed().

Drive-by fixes

Two pre-existing bugs surfaced while wiring events in. Clicking an already-active tab title and OnCloseResponse::Focus on the already-active tab were writing leaf.active = tab_index unconditionally — minor on its own but breaks the new event invariant ("LayoutCommitted ⇒ real mutation"). Both now guarded with if leaf.active != tab_index.

Compatibility

Strictly additive on the public API: show_inside keeps the same signature, DockEvent and DockAreaResponse are new types. No semver bump needed.

Test plan

  • cargo test --all-features — 20 passed.
  • cargo build --all-features — clean (only pre-existing warnings).
  • cargo clippy --all-features — no new lints from this PR.
  • Real-world consumer (the project that motivated this) confirms separator drag now produces one undo entry per drag, not per frame.

🤖 Generated with Claude Code

Replace the silent-return `show_inside` with an opt-in
`show_inside_with_response` that returns a `DockAreaResponse` describing
what changed during the render pass. The response exposes a
`Vec<DockEvent>` plus `layout_changed()` / `layout_committed()` helpers.

`DockEvent` distinguishes a continuous `SeparatorDragging` (one per
frame while the user drags a separator) from a finalised
`LayoutCommitted` (tab close/move/detach, leaf collapse, window
minimise, separator drag end / arrow nudge / double-click reset, focus
change). Consumers driving undo/persistence can now record one entry
per completed user action instead of one per frame.

`DockEvent` and `DockAreaResponse` are `#[non_exhaustive]` so future
fine-grained variants can be added without breaking downstream
consumers that go through the helpers. `show_inside` is kept as a
thin wrapper, so existing callers see no behaviour change.

Two pre-existing bugs surfaced and fixed in the same change: clicking
an already-active tab title and `OnCloseResponse::Focus` on the
already-active tab were writing `leaf.active = tab_index`
unconditionally; both now guarded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant