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