Skip to repository content119 lines · 3.8 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:31:06.539Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
example_text_area.rs
1//! `TextArea` — a multi-line text box. Same `Editor` workhorse, taller chrome,
2//! and `Enter` inserts a newline instead of being ignored. Constructible from a
3//! string or an editor, exactly like [`Input`](crate::example_input::Input).
4
5use gpui::{
6 App, BoxShadow, CursorStyle, Entity, EntityId, Hsla, IntoElement, StyleRefinement, Window, div,
7 hsla, point, prelude::*, px, white,
8};
9
10use crate::Enter;
11use crate::example_editor::{Editor, standard_actions};
12
13enum Source {
14 Value(Entity<String>),
15 Editor(Entity<Editor>),
16}
17
18#[derive(IntoElement)]
19pub struct TextArea {
20 source: Source,
21 rows: usize,
22 color: Option<Hsla>,
23}
24
25impl TextArea {
26 pub fn new(value: Entity<String>, rows: usize) -> Self {
27 Self {
28 source: Source::Value(value),
29 rows,
30 color: None,
31 }
32 }
33
34 pub fn editor(editor: Entity<Editor>, rows: usize) -> Self {
35 Self {
36 source: Source::Editor(editor),
37 rows,
38 color: None,
39 }
40 }
41
42 pub fn color(mut self, color: Hsla) -> Self {
43 self.color = Some(color);
44 self
45 }
46}
47
48impl gpui::View for TextArea {
49 fn entity_id(&self) -> Option<EntityId> {
50 Some(match &self.source {
51 Source::Value(value) => value.entity_id(),
52 Source::Editor(editor) => editor.entity_id(),
53 })
54 }
55
56 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
57 let editor = match self.source {
58 Source::Value(value) => {
59 window.use_state(cx, move |window, cx| Editor::over(value, window, cx))
60 }
61 Source::Editor(editor) => editor,
62 };
63
64 let focus_handle = editor.read(cx).focus_handle.clone();
65 let is_focused = focus_handle.is_focused(window);
66 let text_color = self.color.unwrap_or(hsla(0., 0., 0.1, 1.));
67 let row_height = px(20.);
68 let box_height = row_height * self.rows as f32 + px(16.);
69
70 let border = if is_focused {
71 hsla(220. / 360., 0.8, 0.5, 1.)
72 } else {
73 hsla(0., 0., 0.75, 1.)
74 };
75
76 div()
77 .id("text-area")
78 .key_context("TextInput")
79 .track_focus(&focus_handle)
80 .cursor(CursorStyle::IBeam)
81 .map(standard_actions(editor.clone()))
82 // Enter is the one binding that differs from a single-line input.
83 .on_action({
84 let editor = editor.clone();
85 move |_: &Enter, _window, cx| editor.update(cx, |e, cx| e.insert_newline(cx))
86 })
87 .w(px(400.))
88 .h(box_height)
89 .p(px(8.))
90 .bg(white())
91 .border_1()
92 .border_color(border)
93 .when(is_focused, |this| {
94 this.shadow(vec![BoxShadow {
95 color: hsla(220. / 360., 0.8, 0.5, 0.3),
96 offset: point(px(0.), px(0.)),
97 blur_radius: px(4.),
98 spread_radius: px(1.),
99 inset: false,
100 }])
101 })
102 .rounded(px(4.))
103 .overflow_hidden()
104 .line_height(row_height)
105 .text_size(px(14.))
106 .text_color(text_color)
107 // The cache style is computed from the `rows` prop: change `rows` and
108 // the editor's cached bounds change, busting its cache and re-laying
109 // out the text. (`Input` just uses `size_full()` — nothing to vary.)
110 .child(
111 editor.cached(
112 StyleRefinement::default()
113 .w_full()
114 .h(row_height * self.rows as f32),
115 ),
116 )
117 }
118}
119