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_render_log;
 34mod example_text_area;
 35
 36#[cfg(test)]
 37mod example_tests;
 38
 39use gpui::{
 40    App, Bounds, Context, Hsla, KeyBinding, Window, WindowBounds, WindowOptions, actions, div,
 41    hsla, prelude::*, px, rgb, size,
 42};
 43use gpui_platform::application;
 44
 45use example_editor::ExampleEditor;
 46use example_editor_info::EditorInfo;
 47use example_input::{ExampleInput, ExampleInputState};
 48use example_render_log::{RenderLog, RenderLogPanel};
 49use example_text_area::ExampleTextArea;
 50
 51actions!(
 52    view_example,
 53    [Backspace, Delete, Left, Right, Home, End, Enter, Quit,]
 54);
 55
 56// ---------------------------------------------------------------------------
 57// ViewExample — the root view using `Render` and `window.use_state()`
 58// ---------------------------------------------------------------------------
 59
 60struct ViewExample {
 61    input_color: Hsla,
 62    textarea_color: Hsla,
 63}
 64
 65impl ViewExample {
 66    fn new() -> Self {
 67        Self {
 68            input_color: hsla(0., 0., 0.1, 1.),
 69            textarea_color: hsla(250. / 360., 0.7, 0.4, 1.),
 70        }
 71    }
 72}
 73
 74impl Render for ViewExample {
 75    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 76        let render_log = window.use_state(cx, |_window, cx| RenderLog::new(cx));
 77        render_log.update(cx, |log, _cx| log.begin_frame());
 78        let input_state = window.use_state(cx, {
 79            let render_log = render_log.clone();
 80            move |window, cx| ExampleInputState::new(render_log, window, cx)
 81        });
 82        let input_editor = input_state.read(cx).editor.clone();
 83        let textarea_editor = window.use_state(cx, |window, cx| ExampleEditor::new(window, cx));
 84        textarea_editor.update(cx, |e, _cx| {
 85            if e.render_log.is_none() {
 86                e.render_log = Some(render_log.clone());
 87            }
 88        });
 89        let input_color = self.input_color;
 90        let textarea_color = self.textarea_color;
 91
 92        div()
 93            .flex()
 94            .flex_col()
 95            .size_full()
 96            .bg(rgb(0xf0f0f0))
 97            .p(px(24.))
 98            .gap(px(20.))
 99            .child(
100                div()
101                    .flex()
102                    .flex_col()
103                    .gap(px(4.))
104                    .child(
105                        div()
106                            .text_sm()
107                            .text_color(hsla(0., 0., 0.3, 1.))
108                            .child("Single-line input (View with own state + cached editor)"),
109                    )
110                    .child(
111                        ExampleInput::new(input_state, render_log.clone())
112                            .width(px(320.))
113                            .color(input_color),
114                    )
115                    .child(EditorInfo::new(input_editor, render_log.clone())),
116            )
117            .child(
118                div()
119                    .flex()
120                    .flex_col()
121                    .gap(px(4.))
122                    .child(div().text_sm().text_color(hsla(0., 0., 0.3, 1.)).child(
123                        "Multi-line text area (TextArea — same entity type, different View)",
124                    ))
125                    .child(
126                        ExampleTextArea::new(textarea_editor, render_log.clone(), 5)
127                            .color(textarea_color),
128                    ),
129            )
130            .child(RenderLogPanel::new(render_log))
131    }
132}
133
134// ---------------------------------------------------------------------------
135// Entry point
136// ---------------------------------------------------------------------------
137
138fn run_example() {
139    application().run(|cx: &mut App| {
140        let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
141        cx.bind_keys([
142            KeyBinding::new("backspace", Backspace, None),
143            KeyBinding::new("delete", Delete, None),
144            KeyBinding::new("left", Left, None),
145            KeyBinding::new("right", Right, None),
146            KeyBinding::new("home", Home, None),
147            KeyBinding::new("end", End, None),
148            KeyBinding::new("enter", Enter, None),
149            KeyBinding::new("cmd-q", Quit, None),
150        ]);
151
152        cx.open_window(
153            WindowOptions {
154                window_bounds: Some(WindowBounds::Windowed(bounds)),
155                ..Default::default()
156            },
157            |_, cx| cx.new(|_| ViewExample::new()),
158        )
159        .unwrap();
160
161        cx.on_action(|_: &Quit, cx| cx.quit());
162        cx.activate(true);
163    });
164}
165
166#[cfg(not(target_family = "wasm"))]
167fn main() {
168    run_example();
169}
170
171#[cfg(target_family = "wasm")]
172#[wasm_bindgen::prelude::wasm_bindgen(start)]
173pub fn start() {
174    gpui_platform::web_init();
175    run_example();
176}