gpui: Introduce the View trait - #51030
Closed
mikayla-maki wants to merge 12 commits into
Closed
Conversation
huacnlee
reviewed
Mar 9, 2026
huacnlee
reviewed
Mar 9, 2026
…nd Component models.
…model Introduce View trait with Option<Entity> for stateful/stateless branching, ComponentView trait with blanket View impl for RenderOnce-style components, and ViewElement that handles both cached (reactive boundary) and uncached (type-name isolation) paths. Rename text_views example to view_example with ExampleEditor/Input/TextArea.
- EntityView: View-based replacement for Render on entity state types. Entity<T: EntityView> gets a blanket View impl with reactive boundary. - Use ViewElement::new(entity) at call sites for IntoElement. - Add #[inline] on ComponentView and EntityView bridge methods. - Tighten all public docs in view.rs to be short and consistent. - Update ExampleEditor to use EntityView, remove ExampleEditorView wrapper. - ExampleEditorText reads text_color from inherited text style.
…Area as ComponentView ExampleInput is now a proper View backed by ExampleInputState, which: - Creates and owns its editor entity internally (cx.new in state constructor) - Tracks focus via on_focus/on_blur listeners on its own state entity - Never reads the editor entity during render — only reads its own state - Gets an independent caching boundary from both parent and child editor ExampleTextArea is now a ComponentView (stateless wrapper) where the inner EntityView does the caching. This demonstrates the cheaper pattern for components that don't need their own reactive boundary. The parent (ViewExample) allocates ExampleInputState via use_state, which internally chains to cx.new() for the editor. ExampleInput::new() is a pure constructor taking just the state handle. Design analysis documented in plan.md: - use_state observer is necessary (non-view entities aren't in dispatch tree) - View vs ComponentView vs EntityView taxonomy with clear use cases - Entity reactivity taxonomy (view, component-local, shared-universal, shared-selective) - V2 use_global API concept for theme/settings auto-reactivity
EditorInfo is a View that reads from the same ExampleEditor entity as ExampleInput, demonstrating two completely different renderings of one entity with automatic caching and no manual wiring: - Returns the editor from entity() — that's the entire reactive hookup - Reads editor.content, cursor, focus_handle in render() - Caches automatically: typing updates it, but pressing Enter (flash border on input) leaves it cached since the editor didn't change - 46 lines total, no observers, no subscriptions, no cx.notify() The parent shares the editor handle by reading it from ExampleInputState: let input_editor = input_state.read(cx).editor.clone(); EditorInfo::new(input_editor)
Blink fix: - ExampleEditor no longer starts a blink task unconditionally - Registers on_focus/on_blur listeners in constructor (now takes Window) - start_blink() spawns the timer, stop_blink() drops it - Unfocused editors produce zero re-renders Render log: - New RenderLog entity + RenderLogPanel ComponentView - Each component logs its render() call: ExampleInput, EditorInfo, ExampleTextArea, and ExampleEditor - Panel displays last 20 entries with timestamps, dark terminal style - No cx.notify() in log() — avoids infinite invalidation loop; panel updates passively when parent re-renders - Replaces the static help text at the bottom of the example
…ive frames Rewrote RenderLog to group render() calls by frame: - begin_frame() called at start of root render, finalizes previous frame - log() records component name in current frame - Consecutive frames with identical component sets collapse into one entry with a ×N counter (e.g. animation re-rendering Input+Editor 30 times shows as one line with ×30) - Display shows component names, repeat count, and timestamp Added 6 unit tests for the frame grouping logic: - Groups by frame, consecutive collapse, different frames don't collapse, collapse resumes after interruption, empty frames ignored, dedup within frame Note: tests are plain #[test] (not gpui::test) since RenderLog logic is pure data manipulation. The pre-existing example_tests.rs compilation issue (TestAppContext not available in examples) prevents running them via cargo test currently.
- All 6 render_log tests pass as plain #[test] (pure data logic) - Fixed example_tests to use ExampleInputState (matching new API) - Fixed blink test for focus-driven blink (test via typing, not init) - Fixed test command in doc comment - All 12 tests pass with: cargo test --example view_example -p gpui --features test-support
mikayla-maki
force-pushed
the
gpui-components-and-views
branch
from
March 15, 2026 23:32
8c21970 to
b0facda
Compare
Contributor
|
Any progress? |
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Introduces a new player in GPUI's narrative: The View, a tuple of
(Entity, Component), that makes the Component approach work in GPUI.Release Notes: