warpui_core: backend-neutral view hierarchy for a shared responder chain#12413
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
dcb823a to
55a94a6
Compare
vorporeal
left a comment
There was a problem hiding this comment.
code structure suggestion (agent can probably clean this up pretty easily):
might be nice to have a core/backend folder that contains gui and tui subfolders - we then can have parallel gui/view and tui/view hierarchies.
so something like:
crates/warpui_core/src/core/backend.rscrates/warpui_core/src/core/backend/gui.rscrates/warpui_core/src/core/backend/gui/view/context.rs
etc.
037ea72 to
60492af
Compare
60492af to
0a1c519
Compare
afea471 to
d098332
Compare
0a1c519 to
7eb80a2
Compare
cb839d6 to
68de666
Compare
21e266f to
2f3a0e9
Compare
Collapses the former 01 (Backend-trait parameterization, since deleted) and 02 (neutral core + view unification + module structure) into one foundation: the GUI backend is compiled and aliased unconditionally, the tui feature is purely additive, and there is no Backend type parameter. Co-Authored-By: Oz <oz-agent@warp.dev>
2f3a0e9 to
a2e8bfc
Compare
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR moves the WarpUI responder-chain/view-parent hierarchy out of the GUI presenter and into AppContext, with the GUI presenter reporting layout-time embeddings into the shared map. The implementation generally follows the described backend-neutral refactor, and no approved spec context was provided. The supplemental security pass did not identify security-specific issues in the changed lines.
Concerns
transfer_view_to_windownow removes the source-window parent-map entry before validating the target window. If the transfer rolls back because the target window is missing, the view is restored to the source window but its responder-chain ancestry is not, leaving future focus/action ancestor walks incorrect.
Verdict
Found: 0 critical, 1 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
|
|
||
| // The view's parentage in the source window no longer applies; the | ||
| // target window's render pass will report its new embedding. | ||
| if let Some(parents) = self.view_parents.get_mut(&source_window_id) { |
There was a problem hiding this comment.
view_parents without its ancestry, so later responder-chain/focus walks collapse to just the view. Move this after the target-window check or restore the old parent on rollback.
There was a problem hiding this comment.
agreed with the bot here; we should push this logic to run after all early-returns (e.g.: the one right below this)
|
|
||
| // The view's parentage in the source window no longer applies; the | ||
| // target window's render pass will report its new embedding. | ||
| if let Some(parents) = self.view_parents.get_mut(&source_window_id) { |
There was a problem hiding this comment.
agreed with the bot here; we should push this logic to run after all early-returns (e.g.: the one right below this)
| /// child-view embeddings it discovers while laying out a frame | ||
| /// ([`Self::report_view_embeddings`]). Entries are removed when views are | ||
| /// dropped or transferred out of the window, and when the window closes. | ||
| view_parents: HashMap<WindowId, HashMap<EntityId, EntityId>>, |
There was a problem hiding this comment.
is the reason we're keying this on WindowId that it allows us to easily drop all ancestry information for views in a particular window? given that we now support moving views between windows, i wonder if the strict window->view association is still worth keeping. (entity IDs are globally unique, so we don't need to namespace them with a window ID)
i don't think we necessarily need to change this, mostly just curious.
There was a problem hiding this comment.
Yeah, that is the case. previously we had this info on presenter, which is keyed by window. Just cause I think its not that consequential right now + for the sake expediency gonna proceed with this as is
| where | ||
| T: View + TypedActionView, | ||
| F: FnOnce(&mut ViewContext<T>) -> T, | ||
| { | ||
| self.insert_window(options, build_root_view) | ||
| } | ||
|
|
||
| fn insert_window<T, F>( | ||
| &mut self, | ||
| add_window_options: AddWindowOptions, | ||
| build_root_view: F, | ||
| ) -> (WindowId, ViewHandle<T>) | ||
| where | ||
| T: View + TypedActionView, | ||
| F: FnOnce(&mut ViewContext<T>) -> T, | ||
| { | ||
| let (window_id, _root_view_id) = | ||
| self.insert_window_internal(None, add_window_options, |window_id, ctx| { | ||
| self.insert_window_internal(None, options, |window_id, ctx| { |
There was a problem hiding this comment.
i assume all of these changes leaked into this PR from a future one in the stack?
| /// This is useful when transferring a component like a tab that contains | ||
| /// multiple nested views. The view tree is determined by the presenter's | ||
| /// parent-child relationships. | ||
| /// multiple nested views. The view tree is determined by the neutral view |
There was a problem hiding this comment.
lol neutral makes no sense here outside of "this data used to exist in a presenter that we're now scoping to the GUI backend"
(don't need to change, just sighing at AI comment writing)
| /// This operation is destructive: It will clear the caches for both manual and autotracked | ||
| /// invalidations. | ||
| /// | ||
| /// GUI-only for now; the M8 TUI runtime may move this back into shared code |
There was a problem hiding this comment.
M8? is this the milestones in your plan document leaking into doc comments?

Description
First PR in a stack that adds an optional TUI backend to
warpui. This PR is only the backend-neutral groundwork — no TUI code, no behavioral change.Today the view responder chain (focus/blur, action dispatch, ancestor walks) is derived from the GUI presenter's layout-time parent map. This PR moves that ownership into
AppContextso any backend can drive it:AppContext.view_parents— a per-window child→parent view map.record_view_parent) and the active backend's render pass (report_view_embeddings).view_ancestors/view_parent_map; all responder-chain / focus / dispatch sites now read this map instead ofpresenter.ancestors().report_view_embeddings, so GUI behavior is unchanged.This is a behavior-preserving, GUI-only refactor. The TUI presenter feeds the same map later in the stack (#12601).
Reviewing (3 files):
app.rsis the substance — the map, its accessors, and the call-site swaps.presenter.rswires the GUI render pass to report embeddings. The test swapspresenter.ancestors()→ctx.view_ancestors().Stack
master→ #12413 (this) → #12633 → #12634 → #12601Testing
cargo check -p warpui_core -p warp -p warpuiclean;cargo nextest -p warpui_core279 passed / 7 skipped.Agent Mode
CHANGELOG-NONE