@@ -0,0 +1,46 @@
+//! `EditorInfo` โ a read-only View over an `ExampleEditor` entity.
+//!
+//! Demonstrates zero-wiring reactivity: just return the entity from `entity()`,
+//! read from it in `render()`, and caching + invalidation happen automatically.
+//! No observers, no subscriptions, no manual `cx.notify()`.
+
+use gpui::{App, Entity, IntoViewElement, Window, div, hsla, prelude::*, px};
+
+use crate::example_editor::ExampleEditor;
+
+#[derive(Hash, IntoViewElement)]
+pub struct EditorInfo {
+ editor: Entity<ExampleEditor>,
+}
+
+impl EditorInfo {
+ pub fn new(editor: Entity<ExampleEditor>) -> Self {
+ Self { editor }
+ }
+}
+
+impl gpui::View for EditorInfo {
+ type Entity = ExampleEditor;
+
+ fn entity(&self) -> Option<Entity<ExampleEditor>> {
+ Some(self.editor.clone())
+ }
+
+ fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+ let editor = self.editor.read(cx);
+ let char_count = editor.content.len();
+ let cursor = editor.cursor;
+ let is_focused = editor.focus_handle.is_focused(window);
+
+ div()
+ .flex()
+ .gap(px(8.))
+ .text_xs()
+ .text_color(hsla(0., 0., 0.45, 1.))
+ .child(format!("{char_count} chars"))
+ .child("ยท")
+ .child(format!("cursor {cursor}"))
+ .child("ยท")
+ .child(if is_focused { "focused" } else { "unfocused" })
+ }
+}
@@ -17,7 +17,7 @@ use crate::example_editor::ExampleEditor;
use crate::{Backspace, Delete, End, Enter, Home, Left, Right};
pub struct ExampleInputState {
- editor: Entity<ExampleEditor>,
+ pub editor: Entity<ExampleEditor>,
focus_handle: FocusHandle,
is_focused: bool,
flash_count: usize,
@@ -7,13 +7,13 @@
//!
//! Each module has a focused job:
//!
-//! | Module | Layer | Job |
-//! |-----------------|---------|----------------------------------------------------------|
-//! | `editor` | Entity | Owns text, cursor, blink task, `EntityInputHandler` |
-//! | `editor_text` | Element | Shapes text, paints cursor, wires `handle_input` |
-//! | `input` | View | Single-line input โ composes `ExampleEditorText` with styling |
-//! | `text_area` | View | Multi-line text area โ same entity, different layout |
-//! | `main` (here) | Render | Root view โ creates entities with `use_state`, assembles |
+//! | Module | Trait | Job |
+//! |-----------------|---------------|--------------------------------------------------------|
+//! | `editor` | EntityView | Owns text, cursor, blink; renders via ExampleEditorText |
+//! | `input` | View | Single-line input with own state + cached editor child |
+//! | `editor_info` | View | Read-only stats display; zero-wiring, same editor entity |
+//! | `text_area` | ComponentView | Stateless multi-line wrapper; inner editor caches |
+//! | `main` (here) | Render | Root view; creates entities with `use_state`, assembles |
//!
//! ## Running
//!
@@ -28,6 +28,7 @@
//! ```
mod example_editor;
+mod example_editor_info;
mod example_input;
mod example_text_area;
@@ -41,6 +42,7 @@ use gpui::{
use gpui_platform::application;
use example_editor::ExampleEditor;
+use example_editor_info::EditorInfo;
use example_input::{ExampleInput, ExampleInputState};
use example_text_area::ExampleTextArea;
@@ -70,6 +72,7 @@ impl ViewExample {
impl Render for ViewExample {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let input_state = window.use_state(cx, |window, cx| ExampleInputState::new(window, cx));
+ let input_editor = input_state.read(cx).editor.clone();
let textarea_editor = window.use_state(cx, |_window, cx| ExampleEditor::new(cx));
let input_color = self.input_color;
let textarea_color = self.textarea_color;
@@ -87,15 +90,17 @@ impl Render for ViewExample {
.flex_col()
.gap(px(4.))
.child(
- div().text_sm().text_color(hsla(0., 0., 0.3, 1.)).child(
- "Single-line input (Input โ View with own state + cached editor)",
- ),
+ div()
+ .text_sm()
+ .text_color(hsla(0., 0., 0.3, 1.))
+ .child("Single-line input (View with own state + cached editor)"),
)
.child(
ExampleInput::new(input_state)
.width(px(320.))
.color(input_color),
- ),
+ )
+ .child(EditorInfo::new(input_editor)),
)
.child(
div()
@@ -115,13 +120,11 @@ impl Render for ViewExample {
.mt(px(12.))
.text_xs()
.text_color(hsla(0., 0., 0.5, 1.))
- .child("โข ExampleEditor entity owns text, cursor, blink (EntityView)")
- .child("โข ExampleInput is a View with its own state โ caches independently")
- .child(
- "โข ExampleTextArea is a ComponentView โ stateless wrapper, editor caches",
- )
- .child("โข Press Enter in input to flash border (only chrome re-renders)")
- .child("โข Entities created via window.use_state()"),
+ .child("โข ExampleInput: View with own state โ caches independently")
+ .child("โข EditorInfo: View on same editor โ zero-wiring, auto-cached")
+ .child("โข ExampleTextArea: ComponentView โ stateless wrapper")
+ .child("โข Press Enter in input to flash border (EditorInfo stays cached)")
+ .child("โข Type to see both input and info update reactively"),
)
}
}