example_text_area.rs

  1//! The `ExampleTextArea` view — a multi-line text area component.
  2//!
  3//! Same `ExampleEditor` entity, different presentation: taller box with configurable
  4//! row count. Demonstrates that the same entity type can back different `View`
  5//! components with different props and layouts.
  6
  7use gpui::{
  8    App, BoxShadow, CursorStyle, Entity, Hsla, IntoViewElement, StyleRefinement, ViewElement,
  9    Window, div, hsla, point, prelude::*, px, white,
 10};
 11
 12use crate::example_editor::ExampleEditor;
 13use crate::{Backspace, Delete, End, Enter, Home, Left, Right};
 14
 15#[derive(Hash, IntoViewElement)]
 16pub struct ExampleTextArea {
 17    editor: Entity<ExampleEditor>,
 18    rows: usize,
 19    color: Option<Hsla>,
 20}
 21
 22impl ExampleTextArea {
 23    pub fn new(editor: Entity<ExampleEditor>, rows: usize) -> Self {
 24        Self {
 25            editor,
 26            rows,
 27            color: None,
 28        }
 29    }
 30
 31    pub fn color(mut self, color: Hsla) -> Self {
 32        self.color = Some(color);
 33        self
 34    }
 35}
 36
 37impl gpui::View for ExampleTextArea {
 38    type Entity = ExampleEditor;
 39
 40    fn entity(&self) -> Option<Entity<ExampleEditor>> {
 41        Some(self.editor.clone())
 42    }
 43
 44    fn cache_style(&mut self, _window: &mut Window, _cx: &mut App) -> Option<StyleRefinement> {
 45        let row_height = px(20.);
 46        let box_height = row_height * self.rows as f32 + px(16.);
 47        let mut style = StyleRefinement::default();
 48        style.size.width = Some(px(400.).into());
 49        style.size.height = Some(box_height.into());
 50        Some(style)
 51    }
 52
 53    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
 54        let focus_handle = self.editor.read(cx).focus_handle.clone();
 55        let is_focused = focus_handle.is_focused(window);
 56        let text_color = self.color.unwrap_or(hsla(0., 0., 0.1, 1.));
 57        let row_height = px(20.);
 58        let box_height = row_height * self.rows as f32 + px(16.);
 59        let editor = self.editor;
 60
 61        div()
 62            .id("text-area")
 63            .key_context("TextInput")
 64            .track_focus(&focus_handle)
 65            .cursor(CursorStyle::IBeam)
 66            .on_action({
 67                let editor = editor.clone();
 68                move |action: &Backspace, _window, cx| {
 69                    editor.update(cx, |state, cx| state.backspace(action, _window, cx));
 70                }
 71            })
 72            .on_action({
 73                let editor = editor.clone();
 74                move |action: &Delete, _window, cx| {
 75                    editor.update(cx, |state, cx| state.delete(action, _window, cx));
 76                }
 77            })
 78            .on_action({
 79                let editor = editor.clone();
 80                move |action: &Left, _window, cx| {
 81                    editor.update(cx, |state, cx| state.left(action, _window, cx));
 82                }
 83            })
 84            .on_action({
 85                let editor = editor.clone();
 86                move |action: &Right, _window, cx| {
 87                    editor.update(cx, |state, cx| state.right(action, _window, cx));
 88                }
 89            })
 90            .on_action({
 91                let editor = editor.clone();
 92                move |action: &Home, _window, cx| {
 93                    editor.update(cx, |state, cx| state.home(action, _window, cx));
 94                }
 95            })
 96            .on_action({
 97                let editor = editor.clone();
 98                move |action: &End, _window, cx| {
 99                    editor.update(cx, |state, cx| state.end(action, _window, cx));
100                }
101            })
102            .on_action({
103                let editor = editor.clone();
104                move |_: &Enter, _window, cx| {
105                    editor.update(cx, |state, cx| state.insert_newline(cx));
106                }
107            })
108            .w(px(400.))
109            .h(box_height)
110            .p(px(8.))
111            .bg(white())
112            .border_1()
113            .border_color(if is_focused {
114                hsla(220. / 360., 0.8, 0.5, 1.)
115            } else {
116                hsla(0., 0., 0.75, 1.)
117            })
118            .when(is_focused, |this| {
119                this.shadow(vec![BoxShadow {
120                    color: hsla(220. / 360., 0.8, 0.5, 0.3),
121                    offset: point(px(0.), px(0.)),
122                    blur_radius: px(4.),
123                    spread_radius: px(1.),
124                }])
125            })
126            .rounded(px(4.))
127            .overflow_hidden()
128            .line_height(row_height)
129            .text_size(px(14.))
130            .text_color(text_color)
131            .child(ViewElement::new(editor))
132    }
133}