view_example_main.rs

  1#![cfg_attr(target_family = "wasm", no_main)]
  2
  3//! **view_example** — an end-to-end GPUI example demonstrating how Entity,
  4//! Element, View, and Render compose together to build rich text components.
  5//!
  6//! ## Architecture
  7//!
  8//! Each module has a focused job:
  9//!
 10//! | Module          | Trait         | Job                                                    |
 11//! |-----------------|---------------|--------------------------------------------------------|
 12//! | `editor`        | EntityView    | Owns text, cursor, blink; renders via ExampleEditorText |
 13//! | `input`         | View          | Single-line input with own state + cached editor child  |
 14//! | `editor_info`   | View          | Read-only stats display; zero-wiring, same editor entity |
 15//! | `text_area`     | ComponentView | Stateless multi-line wrapper; inner editor caches       |
 16//! | `main` (here)   | Render        | Root view; creates entities with `use_state`, assembles  |
 17//!
 18//! ## Running
 19//!
 20//! ```sh
 21//! cargo run --example view_example -p gpui
 22//! ```
 23//!
 24//! ## Testing
 25//!
 26//! ```sh
 27//! cargo test --example view_example -p gpui
 28//! ```
 29
 30mod example_editor;
 31mod example_editor_info;
 32mod example_input;
 33mod example_text_area;
 34
 35#[cfg(test)]
 36mod example_tests;
 37
 38use gpui::{
 39    App, Bounds, Context, Hsla, KeyBinding, Window, WindowBounds, WindowOptions, actions, div,
 40    hsla, prelude::*, px, rgb, size,
 41};
 42use gpui_platform::application;
 43
 44use example_editor::ExampleEditor;
 45use example_editor_info::EditorInfo;
 46use example_input::{ExampleInput, ExampleInputState};
 47use example_text_area::ExampleTextArea;
 48
 49actions!(
 50    view_example,
 51    [Backspace, Delete, Left, Right, Home, End, Enter, Quit,]
 52);
 53
 54// ---------------------------------------------------------------------------
 55// ViewExample — the root view using `Render` and `window.use_state()`
 56// ---------------------------------------------------------------------------
 57
 58struct ViewExample {
 59    input_color: Hsla,
 60    textarea_color: Hsla,
 61}
 62
 63impl ViewExample {
 64    fn new() -> Self {
 65        Self {
 66            input_color: hsla(0., 0., 0.1, 1.),
 67            textarea_color: hsla(250. / 360., 0.7, 0.4, 1.),
 68        }
 69    }
 70}
 71
 72impl Render for ViewExample {
 73    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 74        let input_state = window.use_state(cx, |window, cx| ExampleInputState::new(window, cx));
 75        let input_editor = input_state.read(cx).editor.clone();
 76        let textarea_editor = window.use_state(cx, |_window, cx| ExampleEditor::new(cx));
 77        let input_color = self.input_color;
 78        let textarea_color = self.textarea_color;
 79
 80        div()
 81            .flex()
 82            .flex_col()
 83            .size_full()
 84            .bg(rgb(0xf0f0f0))
 85            .p(px(24.))
 86            .gap(px(20.))
 87            .child(
 88                div()
 89                    .flex()
 90                    .flex_col()
 91                    .gap(px(4.))
 92                    .child(
 93                        div()
 94                            .text_sm()
 95                            .text_color(hsla(0., 0., 0.3, 1.))
 96                            .child("Single-line input (View with own state + cached editor)"),
 97                    )
 98                    .child(
 99                        ExampleInput::new(input_state)
100                            .width(px(320.))
101                            .color(input_color),
102                    )
103                    .child(EditorInfo::new(input_editor)),
104            )
105            .child(
106                div()
107                    .flex()
108                    .flex_col()
109                    .gap(px(4.))
110                    .child(div().text_sm().text_color(hsla(0., 0., 0.3, 1.)).child(
111                        "Multi-line text area (TextArea — same entity type, different View)",
112                    ))
113                    .child(ExampleTextArea::new(textarea_editor, 5).color(textarea_color)),
114            )
115            .child(
116                div()
117                    .flex()
118                    .flex_col()
119                    .gap(px(2.))
120                    .mt(px(12.))
121                    .text_xs()
122                    .text_color(hsla(0., 0., 0.5, 1.))
123                    .child("• ExampleInput: View with own state — caches independently")
124                    .child("• EditorInfo: View on same editor — zero-wiring, auto-cached")
125                    .child("• ExampleTextArea: ComponentView — stateless wrapper")
126                    .child("• Press Enter in input to flash border (EditorInfo stays cached)")
127                    .child("• Type to see both input and info update reactively"),
128            )
129    }
130}
131
132// ---------------------------------------------------------------------------
133// Entry point
134// ---------------------------------------------------------------------------
135
136fn run_example() {
137    application().run(|cx: &mut App| {
138        let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
139        cx.bind_keys([
140            KeyBinding::new("backspace", Backspace, None),
141            KeyBinding::new("delete", Delete, None),
142            KeyBinding::new("left", Left, None),
143            KeyBinding::new("right", Right, None),
144            KeyBinding::new("home", Home, None),
145            KeyBinding::new("end", End, None),
146            KeyBinding::new("enter", Enter, None),
147            KeyBinding::new("cmd-q", Quit, None),
148        ]);
149
150        cx.open_window(
151            WindowOptions {
152                window_bounds: Some(WindowBounds::Windowed(bounds)),
153                ..Default::default()
154            },
155            |_, cx| cx.new(|_| ViewExample::new()),
156        )
157        .unwrap();
158
159        cx.on_action(|_: &Quit, cx| cx.quit());
160        cx.activate(true);
161    });
162}
163
164#[cfg(not(target_family = "wasm"))]
165fn main() {
166    run_example();
167}
168
169#[cfg(target_family = "wasm")]
170#[wasm_bindgen::prelude::wasm_bindgen(start)]
171pub fn start() {
172    gpui_platform::web_init();
173    run_example();
174}