1use alloc::boxed::Box;
8use rlvgl_core::{
9 event::Event,
10 renderer::Renderer,
11 widget::{Rect, Widget},
12};
13use rlvgl_widgets::label::Label;
14
15type ChangeCallback = Box<dyn FnMut(&str)>;
17
18#[allow(clippy::type_complexity)]
20pub struct Input {
21 inner: Label,
22 on_change: Option<ChangeCallback>,
23}
24
25impl Input {
26 pub fn new(text: &str, bounds: Rect) -> Self {
28 Self {
29 inner: Label::new(text, bounds),
30 on_change: None,
31 }
32 }
33
34 pub fn on_change<F: FnMut(&str) + 'static>(mut self, handler: F) -> Self {
36 self.on_change = Some(Box::new(handler));
37 self
38 }
39
40 pub fn style(&self) -> &rlvgl_core::style::Style {
42 &self.inner.style
43 }
44
45 pub fn style_mut(&mut self) -> &mut rlvgl_core::style::Style {
47 &mut self.inner.style
48 }
49
50 pub fn set_text(&mut self, text: &str) {
52 self.inner.set_text(text);
53 if let Some(cb) = self.on_change.as_mut() {
54 cb(self.inner.text());
55 }
56 }
57
58 pub fn text(&self) -> &str {
60 self.inner.text()
61 }
62}
63
64impl Widget for Input {
65 fn bounds(&self) -> Rect {
66 self.inner.bounds()
67 }
68
69 fn draw(&self, renderer: &mut dyn Renderer) {
70 self.inner.draw(renderer);
71 }
72
73 fn handle_event(&mut self, event: &Event) -> bool {
74 self.inner.handle_event(event)
75 }
76}
77
78pub struct Textarea {
80 inner: Input,
81}
82
83impl Textarea {
84 pub fn new(text: &str, bounds: Rect) -> Self {
86 Self {
87 inner: Input::new(text, bounds),
88 }
89 }
90
91 pub fn on_change<F: FnMut(&str) + 'static>(mut self, handler: F) -> Self {
93 self.inner = self.inner.on_change(handler);
94 self
95 }
96
97 pub fn style(&self) -> &rlvgl_core::style::Style {
99 self.inner.style()
100 }
101
102 pub fn style_mut(&mut self) -> &mut rlvgl_core::style::Style {
104 self.inner.style_mut()
105 }
106
107 pub fn set_text(&mut self, text: &str) {
109 self.inner.set_text(text);
110 }
111
112 pub fn text(&self) -> &str {
114 self.inner.text()
115 }
116}
117
118impl Widget for Textarea {
119 fn bounds(&self) -> Rect {
120 self.inner.bounds()
121 }
122
123 fn draw(&self, renderer: &mut dyn Renderer) {
124 self.inner.draw(renderer);
125 }
126
127 fn handle_event(&mut self, event: &Event) -> bool {
128 self.inner.handle_event(event)
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use alloc::rc::Rc;
136 use core::cell::Cell;
137 use rlvgl_core::widget::Rect;
138
139 #[test]
140 fn input_sets_text_and_calls_handler() {
141 let called = Rc::new(Cell::new(false));
142 let flag = called.clone();
143 let mut input = Input::new(
144 "hi",
145 Rect {
146 x: 0,
147 y: 0,
148 width: 10,
149 height: 10,
150 },
151 )
152 .on_change(move |_| flag.set(true));
153 input.set_text("new");
154 assert_eq!(input.text(), "new");
155 assert!(called.get());
156 }
157
158 #[test]
159 fn textarea_wraps_input() {
160 let mut area = Textarea::new(
161 "a",
162 Rect {
163 x: 0,
164 y: 0,
165 width: 10,
166 height: 20,
167 },
168 );
169 area.set_text("b");
170 assert_eq!(area.text(), "b");
171 }
172}