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