1use super::*;
9use crate::{ScrollBar, ScrollBarMsg};
10use kas::event::Scroll;
11use kas::event::components::ScrollComponent;
12use kas::messages::{ReplaceSelectedText, SetValueText};
13use kas::prelude::*;
14use kas::theme::{Background, FrameStyle, TextClass};
15use std::fmt::{Debug, Display};
16use std::ops::{Deref, DerefMut};
17use std::str::FromStr;
18
19#[impl_self]
20mod EditBox {
21 #[autoimpl(Default, Debug where G: trait)]
38 #[widget]
39 pub struct EditBox<G: EditGuard = DefaultGuard<()>> {
40 core: widget_core!(),
41 scroll: ScrollComponent,
42 #[widget]
44 inner: EditField<G>,
45 #[widget(&())]
46 vert_bar: ScrollBar<kas::dir::Down>,
47 frame_offset: Offset,
48 frame_size: Size,
49 frame_offset_ex_margin: Offset,
50 inner_margin: i32,
51 clip_rect: Rect,
52 }
53
54 impl Layout for Self {
55 fn size_rules(&mut self, cx: &mut SizeCx, mut axis: AxisInfo) -> SizeRules {
56 let size = self.frame_size.extract(axis.flipped());
57 axis.map_other(|x| x - size);
58
59 let mut rules = self.inner.size_rules(cx, axis);
60 let bar_rules = self.vert_bar.size_rules(cx, axis);
61 if axis.is_horizontal() && self.multi_line() {
62 self.inner_margin = rules.margins_i32().1.max(bar_rules.margins_i32().0);
63 rules.append(bar_rules);
64 }
65
66 let frame_rules = cx.frame(FrameStyle::EditBox, axis);
67 self.frame_offset_ex_margin
68 .set_component(axis, frame_rules.size());
69 let (rules, offset, size) = frame_rules.surround(rules);
70 self.frame_offset.set_component(axis, offset);
71 self.frame_size.set_component(axis, size);
72 rules
73 }
74
75 fn set_rect(&mut self, cx: &mut SizeCx, outer_rect: Rect, hints: AlignHints) {
76 self.core.set_rect(outer_rect);
77 let mut rect = outer_rect;
78
79 self.clip_rect = Rect {
80 pos: rect.pos + self.frame_offset_ex_margin,
81 size: rect.size - (self.frame_offset_ex_margin * 2).cast(),
82 };
83
84 rect.pos += self.frame_offset;
85 rect.size -= self.frame_size;
86
87 let mut bar_rect = Rect::ZERO;
88 if self.multi_line() {
89 let bar_width = cx.scroll_bar_width();
90 let x1 = rect.pos.0 + rect.size.0;
91 let x0 = x1 - bar_width;
92 bar_rect = Rect::new(Coord(x0, rect.pos.1), Size(bar_width, rect.size.1));
93 rect.size.0 = (rect.size.0 - bar_width - self.inner_margin).max(0);
94 }
95 self.vert_bar.set_rect(cx, bar_rect, AlignHints::NONE);
96
97 self.inner.set_rect(cx, rect, hints);
98 self.update_content_size(cx);
99 }
100
101 fn draw(&self, mut draw: DrawCx) {
102 let mut draw_inner = draw.re();
103 draw_inner.set_id(self.inner.id());
104 let bg = if self.inner.has_error() {
105 Background::Error
106 } else {
107 Background::Default
108 };
109 draw_inner.frame(self.rect(), FrameStyle::EditBox, bg);
110
111 self.inner
112 .draw_with_offset(draw.re(), self.clip_rect, self.scroll.offset());
113
114 if self.scroll.max_offset().1 > 0 {
115 self.vert_bar.draw(draw.re());
116 }
117 }
118 }
119
120 impl Tile for Self {
121 #[inline]
122 fn tooltip(&self) -> Option<&str> {
123 self.deref().tooltip()
124 }
125
126 fn role(&self, _: &mut dyn RoleCx) -> Role<'_> {
127 Role::ScrollRegion {
128 offset: self.scroll.offset(),
129 max_offset: self.scroll.max_offset(),
130 }
131 }
132
133 fn translation(&self, index: usize) -> Offset {
134 if index == widget_index!(self.inner) {
135 self.scroll.offset()
136 } else {
137 Offset::ZERO
138 }
139 }
140 }
141
142 impl Events for Self {
143 type Data = G::Data;
144
145 fn probe(&self, coord: Coord) -> Id {
146 if self.scroll.max_offset().1 > 0 {
147 if let Some(id) = self.vert_bar.try_probe(coord) {
148 return id;
149 }
150 }
151
152 self.inner.id()
155 }
156
157 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
158 let rect = Rect {
159 pos: self.rect().pos + self.frame_offset,
160 size: self.rect().size - self.frame_size,
161 };
162 let used = self.scroll.scroll_by_event(cx, event, self.id(), rect);
163 self.update_content_size(cx);
164 used
165 }
166
167 fn handle_messages(&mut self, cx: &mut EventCx<'_>, data: &G::Data) {
168 let action = if cx.last_child() == Some(widget_index![self.vert_bar])
169 && let Some(ScrollBarMsg(y)) = cx.try_pop()
170 {
171 let offset = Offset(self.scroll.offset().0, y);
172 self.scroll.set_offset(offset)
173 } else if let Some(kas::messages::SetScrollOffset(offset)) = cx.try_pop() {
174 self.scroll.set_offset(offset)
175 } else if self.is_editable()
176 && let Some(SetValueText(string)) = cx.try_pop()
177 {
178 self.pre_commit();
179 self.set_string(cx, string);
180 self.inner.call_guard_edit(cx, data);
181 return;
182 } else if let Some(&ReplaceSelectedText(_)) = cx.try_peek() {
183 self.inner.handle_messages(cx, data);
184 return;
185 } else {
186 return;
187 };
188
189 if let Some(moved) = action {
190 cx.action_moved(moved);
191 self.update_scroll_offset(cx);
192 }
193 }
194
195 fn handle_resize(&mut self, cx: &mut ConfigCx, _: &Self::Data) -> Option<ActionResize> {
196 let size = self.inner.rect().size;
197 let axis = AxisInfo::new(false, Some(size.1));
198 let mut resize = self.inner.size_rules(&mut cx.size_cx(), axis).min_size() > size.0;
199 let axis = AxisInfo::new(true, Some(size.0));
200 resize |= self.inner.size_rules(&mut cx.size_cx(), axis).min_size() > size.1;
201 self.update_content_size(cx);
202 resize.then_some(ActionResize)
203 }
204
205 fn handle_scroll(&mut self, cx: &mut EventCx<'_>, _: &G::Data, scroll: Scroll) {
206 let rect = self.inner.rect();
207 self.scroll.scroll(cx, self.id(), rect, scroll);
208 self.update_scroll_offset(cx);
209 }
210 }
211
212 impl Self {
213 #[inline]
215 pub fn new(guard: G) -> Self {
216 EditBox {
217 core: Default::default(),
218 scroll: Default::default(),
219 inner: EditField::new(guard),
220 vert_bar: Default::default(),
221 frame_offset: Default::default(),
222 frame_size: Default::default(),
223 frame_offset_ex_margin: Default::default(),
224 inner_margin: Default::default(),
225 clip_rect: Default::default(),
226 }
227 }
228
229 fn update_content_size(&mut self, cx: &mut EventState) {
230 if !self.core.status.is_sized() {
231 return;
232 }
233 let size = self.inner.rect().size;
234 let _ = self.scroll.set_sizes(size, self.inner.content_size());
235 let max_offset = self.scroll.max_offset().1;
236 self.vert_bar.set_limits(cx, max_offset, size.1);
237 self.update_scroll_offset(cx);
238 }
239
240 fn update_scroll_offset(&mut self, cx: &mut EventState) {
241 self.vert_bar.set_value(cx, self.scroll.offset().1);
242 }
243
244 #[inline]
246 pub fn clear(&mut self, cx: &mut EventState) {
247 self.inner.clear(cx);
248 }
249
250 #[inline]
255 pub fn pre_commit(&mut self) {
256 self.inner.pre_commit();
257 }
258
259 #[inline]
264 pub fn set_str(&mut self, cx: &mut EventState, text: &str) {
265 if self.inner.set_str(cx, text) {
266 self.update_content_size(cx);
267 }
268 }
269
270 #[inline]
277 pub fn set_string(&mut self, cx: &mut EventState, text: String) {
278 if self.inner.set_string(cx, text) {
279 self.update_content_size(cx);
280 }
281 }
282
283 #[inline]
288 pub fn replace_selected_text(&mut self, cx: &mut EventState, text: &str) {
289 if self.inner.replace_selected_text(cx, text) {
290 self.update_content_size(cx);
291 }
292 }
293
294 #[inline]
296 pub fn guard(&self) -> &G {
297 &self.inner.guard
298 }
299
300 #[inline]
302 pub fn guard_mut(&mut self) -> &mut G {
303 &mut self.inner.guard
304 }
305 }
306}
307
308impl<G: EditGuard> Deref for EditBox<G> {
309 type Target = Editor;
310
311 fn deref(&self) -> &Editor {
312 self.inner.deref()
313 }
314}
315
316impl<G: EditGuard> DerefMut for EditBox<G> {
317 fn deref_mut(&mut self) -> &mut Editor {
318 self.inner.deref_mut()
319 }
320}
321
322impl<A: 'static> EditBox<DefaultGuard<A>> {
323 #[inline]
325 pub fn text<S: ToString>(text: S) -> Self {
326 EditBox {
327 inner: EditField::text(text),
328 ..Default::default()
329 }
330 }
331
332 #[inline]
334 pub fn string(value_fn: impl Fn(&A) -> String + 'static) -> EditBox<StringGuard<A>> {
335 EditBox::new(StringGuard::new(value_fn)).with_editable(false)
336 }
337
338 #[inline]
352 pub fn parser<T: Debug + Display + FromStr, M: Debug + 'static>(
353 value_fn: impl Fn(&A) -> T + 'static,
354 msg_fn: impl Fn(T) -> M + 'static,
355 ) -> EditBox<ParseGuard<A, T>> {
356 EditBox::new(ParseGuard::new(value_fn, msg_fn))
357 }
358
359 pub fn instant_parser<T: Debug + Display + FromStr, M: Debug + 'static>(
369 value_fn: impl Fn(&A) -> T + 'static,
370 msg_fn: impl Fn(T) -> M + 'static,
371 ) -> EditBox<InstantParseGuard<A, T>> {
372 EditBox::new(InstantParseGuard::new(value_fn, msg_fn))
373 }
374}
375
376impl<A: 'static> EditBox<StringGuard<A>> {
377 #[must_use]
384 pub fn with_msg<M>(mut self, msg_fn: impl Fn(&str) -> M + 'static) -> Self
385 where
386 M: Debug + 'static,
387 {
388 self.inner.guard = self.inner.guard.with_msg(msg_fn);
389 self.inner.set_editable(true);
390 self
391 }
392}
393
394impl<G: EditGuard> EditBox<G> {
395 #[inline]
399 #[must_use]
400 pub fn with_text(mut self, text: impl ToString) -> Self {
401 self.inner = self.inner.with_text(text);
402 self
403 }
404
405 #[inline]
407 #[must_use]
408 pub fn with_editable(mut self, editable: bool) -> Self {
409 self.inner = self.inner.with_editable(editable);
410 self
411 }
412
413 #[inline]
421 #[must_use]
422 pub fn with_multi_line(mut self, multi_line: bool) -> Self {
423 self.inner = self.inner.with_multi_line(multi_line);
424 self
425 }
426
427 #[inline]
429 #[must_use]
430 pub fn with_class(mut self, class: TextClass) -> Self {
431 self.inner = self.inner.with_class(class);
432 self
433 }
434
435 #[inline]
437 pub fn set_lines(&mut self, min_lines: f32, ideal_lines: f32) {
438 self.inner.set_lines(min_lines, ideal_lines);
439 }
440
441 #[inline]
443 #[must_use]
444 pub fn with_lines(mut self, min_lines: f32, ideal_lines: f32) -> Self {
445 self.set_lines(min_lines, ideal_lines);
446 self
447 }
448
449 #[inline]
451 pub fn set_width_em(&mut self, min_em: f32, ideal_em: f32) {
452 self.inner.set_width_em(min_em, ideal_em);
453 }
454
455 #[inline]
457 #[must_use]
458 pub fn with_width_em(mut self, min_em: f32, ideal_em: f32) -> Self {
459 self.set_width_em(min_em, ideal_em);
460 self
461 }
462}