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}