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          | Layer   | Job                                                      |
 11//! |-----------------|---------|----------------------------------------------------------|
 12//! | `editor`        | Entity  | Owns text, cursor, blink task, `EntityInputHandler`      |
 13//! | `editor_text`   | Element | Shapes text, paints cursor, wires `handle_input`         |
 14//! | `input`         | View    | Single-line input — composes `ExampleEditorText` with styling   |
 15//! | `text_area`     | View    | Multi-line text area — same entity, different layout      |
 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_input;
 32mod example_text_area;
 33
 34#[cfg(test)]
 35mod example_tests;
 36
 37use gpui::{
 38    App, Bounds, Context, Hsla, KeyBinding, Window, WindowBounds, WindowOptions, actions, div,
 39    hsla, prelude::*, px, rgb, size,
 40};
 41use gpui_platform::application;
 42
 43use example_editor::ExampleEditor;
 44use example_input::{ExampleInput, ExampleInputState};
 45use example_text_area::ExampleTextArea;
 46
 47actions!(
 48    view_example,
 49    [Backspace, Delete, Left, Right, Home, End, Enter, Quit,]
 50);
 51
 52// ---------------------------------------------------------------------------
 53// ViewExample — the root view using `Render` and `window.use_state()`
 54// ---------------------------------------------------------------------------
 55
 56struct ViewExample {
 57    input_color: Hsla,
 58    textarea_color: Hsla,
 59}
 60
 61impl ViewExample {
 62    fn new() -> Self {
 63        Self {
 64            input_color: hsla(0., 0., 0.1, 1.),
 65            textarea_color: hsla(250. / 360., 0.7, 0.4, 1.),
 66        }
 67    }
 68}
 69
 70impl Render for ViewExample {
 71    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 72        let input_state = window.use_state(cx, |window, cx| ExampleInputState::new(window, cx));
 73        let textarea_editor = window.use_state(cx, |_window, cx| ExampleEditor::new(cx));
 74        let input_color = self.input_color;
 75        let textarea_color = self.textarea_color;
 76
 77        div()
 78            .flex()
 79            .flex_col()
 80            .size_full()
 81            .bg(rgb(0xf0f0f0))
 82            .p(px(24.))
 83            .gap(px(20.))
 84            .child(
 85                div()
 86                    .flex()
 87                    .flex_col()
 88                    .gap(px(4.))
 89                    .child(
 90                        div().text_sm().text_color(hsla(0., 0., 0.3, 1.)).child(
 91                            "Single-line input (Input — View with own state + cached editor)",
 92                        ),
 93                    )
 94                    .child(
 95                        ExampleInput::new(input_state)
 96                            .width(px(320.))
 97                            .color(input_color),
 98                    ),
 99            )
100            .child(
101                div()
102                    .flex()
103                    .flex_col()
104                    .gap(px(4.))
105                    .child(div().text_sm().text_color(hsla(0., 0., 0.3, 1.)).child(
106                        "Multi-line text area (TextArea — same entity type, different View)",
107                    ))
108                    .child(ExampleTextArea::new(textarea_editor, 5).color(textarea_color)),
109            )
110            .child(
111                div()
112                    .flex()
113                    .flex_col()
114                    .gap(px(2.))
115                    .mt(px(12.))
116                    .text_xs()
117                    .text_color(hsla(0., 0., 0.5, 1.))
118                    .child("• ExampleEditor entity owns text, cursor, blink (EntityView)")
119                    .child("• ExampleInput is a View with its own state — caches independently")
120                    .child(
121                        "• ExampleTextArea is a ComponentView — stateless wrapper, editor caches",
122                    )
123                    .child("• Press Enter in input to flash border (only chrome re-renders)")
124                    .child("• Entities created via window.use_state()"),
125            )
126    }
127}
128
129// ---------------------------------------------------------------------------
130// Entry point
131// ---------------------------------------------------------------------------
132
133fn run_example() {
134    application().run(|cx: &mut App| {
135        let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
136        cx.bind_keys([
137            KeyBinding::new("backspace", Backspace, None),
138            KeyBinding::new("delete", Delete, None),
139            KeyBinding::new("left", Left, None),
140            KeyBinding::new("right", Right, None),
141            KeyBinding::new("home", Home, None),
142            KeyBinding::new("end", End, None),
143            KeyBinding::new("enter", Enter, None),
144            KeyBinding::new("cmd-q", Quit, None),
145        ]);
146
147        cx.open_window(
148            WindowOptions {
149                window_bounds: Some(WindowBounds::Windowed(bounds)),
150                ..Default::default()
151            },
152            |_, cx| cx.new(|_| ViewExample::new()),
153        )
154        .unwrap();
155
156        cx.on_action(|_: &Quit, cx| cx.quit());
157        cx.activate(true);
158    });
159}
160
161#[cfg(not(target_family = "wasm"))]
162fn main() {
163    run_example();
164}
165
166#[cfg(target_family = "wasm")]
167#[wasm_bindgen::prelude::wasm_bindgen(start)]
168pub fn start() {
169    gpui_platform::web_init();
170    run_example();
171}