vizia_core/views/
textbox.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use crate::prelude::*;
5
6use crate::text::{
7    Direction, EditableText, Movement, PreeditBackup, Selection, VerticalMovement, apply_movement,
8    enforce_text_bounds, ensure_visible, offset_for_delete_backwards,
9};
10use accesskit::{ActionData, ActionRequest, TextDirection, TextPosition, TextSelection};
11use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
12use skia_safe::{Paint, PaintStyle, Rect};
13use unicode_segmentation::UnicodeSegmentation;
14
15/// Events for modifying a textbox.
16pub enum TextEvent {
17    /// Insert a string of text into the textbox.
18    InsertText(String),
19    /// Update the preedit text of the textbox (for IME input).
20    UpdatePreedit(String, Option<(usize, usize)>),
21    /// Clear the preedit text of the textbox.
22    ClearPreedit,
23    /// Reset the text of the textbox to the bound data.
24    Clear,
25    /// Delete a section of text, determined by the `Movement`.
26    DeleteText(Movement),
27    /// Move the cursor and selection.
28    MoveCursor(Movement, bool),
29    /// Select all text.
30    SelectAll,
31    /// Select the word at the current cursor position.
32    SelectWord,
33    /// Select the paragraph at the current cursor position.
34    SelectParagraph,
35    /// Toggle the textbox to allow text input.
36    StartEdit,
37    /// Toggle the textbox to *not* allow text input.
38    EndEdit,
39    /// Trigger the `on_submit` callback with the current text.
40    Submit(bool),
41    /// Specify the 'hit' position of the mouse cursor.
42    Hit(f32, f32, bool),
43    /// Specify the 'drag' position of the mouse cursor.
44    Drag(f32, f32),
45    /// Specify the scroll offset of the textbox.
46    Scroll(f32, f32),
47    /// Copy the textbox buffer to the clipboard.
48    Copy,
49    /// Paste the clipboard buffer into the textbox.
50    Paste,
51    /// Cut the textbox text and place it in the clipboard.
52    Cut,
53    /// Set the placeholder text of the textbox.
54    SetPlaceholder(String),
55    /// Trigger the `on_blur` callback.
56    Blur,
57    /// Toggle the visibility of the text Caret.
58    ToggleCaret,
59}
60
61/// The `Textbox` view provides an input control for editing a value as a string.
62///
63/// The textbox takes a lens to some value, which must be a type which can convert to and from a `String`,
64/// as determined by the `ToString` and `FromStr` traits. The value type is used for validation and returned by
65/// the `on_submit` callback, which is triggered when the textbox is submitted with the enter key or when the textbox
66/// loses keyboard focus.
67#[derive(Lens)]
68pub struct Textbox<L: Lens> {
69    lens: L,
70    #[lens(ignore)]
71    kind: TextboxKind,
72    edit: bool,
73    transform: Rc<RefCell<(f32, f32)>>,
74    on_edit: Option<Box<dyn Fn(&mut EventContext, String) + Send + Sync>>,
75    on_submit: Option<Box<dyn Fn(&mut EventContext, L::Target, bool) + Send + Sync>>,
76    on_blur: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
77    on_cancel: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
78    validate: Option<Box<dyn Fn(&L::Target) -> bool>>,
79    placeholder: String,
80    show_placeholder: bool,
81    show_caret: bool,
82    caret_timer: Timer,
83    selection: Selection,
84    preedit_backup: Option<PreeditBackup>,
85    text_overflow: Option<TextOverflow>,
86}
87
88// Determines whether the enter key submits the text or inserts a new line.
89#[derive(Copy, Clone, PartialEq, Eq)]
90enum TextboxKind {
91    SingleLine,
92    MultiLineUnwrapped,
93    MultiLineWrapped,
94}
95
96impl<L> Textbox<L>
97where
98    L: Lens<Target: Data + Clone + ToStringLocalized + std::str::FromStr>,
99{
100    /// Creates a new single-line textbox.
101    ///
102    /// # Example
103    /// ```rust
104    /// # use vizia_core::prelude::*;
105    /// #
106    /// # #[derive(Lens)]
107    /// # struct AppData {
108    /// #     text: String,
109    /// # }
110    /// #
111    /// # impl Model for AppData {}
112    /// #
113    /// # let cx = &mut Context::default();
114    /// #
115    /// # AppData { text: String::from("Hello World") }.build(cx);
116    /// #
117    /// Textbox::new(cx, AppData::text);
118    /// ```
119    pub fn new(cx: &mut Context, lens: L) -> Handle<Self> {
120        Self::new_core(cx, lens, TextboxKind::SingleLine)
121    }
122
123    /// Creates a new multi-line textbox.
124    ///
125    /// The `wrap` parameter determines whether text which is too long for the textbox
126    /// should soft-wrap onto multiple lines. If false, then only hard-wraps from line breaks
127    /// will cause the text to span multiple lines.
128    ///
129    /// # Example
130    /// ```rust
131    /// # use vizia_core::prelude::*;
132    /// #
133    /// # #[derive(Lens)]
134    /// # struct AppData {
135    /// #     text: String,
136    /// # }
137    /// #
138    /// # impl Model for AppData {}
139    /// #
140    /// # let cx = &mut Context::default();
141    /// #
142    /// # AppData { text: String::from("Hello World") }.build(cx);
143    /// #
144    /// Textbox::new_multiline(cx, AppData::text, true);
145    /// ```
146    pub fn new_multiline(cx: &mut Context, lens: L, wrap: bool) -> Handle<Self> {
147        Self::new_core(
148            cx,
149            lens,
150            if wrap { TextboxKind::MultiLineWrapped } else { TextboxKind::MultiLineUnwrapped },
151        )
152    }
153
154    fn new_core(cx: &mut Context, lens: L, kind: TextboxKind) -> Handle<Self> {
155        let caret_timer = cx.environment().caret_timer;
156
157        Self {
158            lens,
159            kind,
160            edit: false,
161            transform: Rc::new(RefCell::new((0.0, 0.0))),
162            on_edit: None,
163            on_submit: None,
164            on_blur: None,
165            on_cancel: None,
166            validate: None,
167            placeholder: String::from(""),
168            show_placeholder: true,
169            show_caret: true,
170            caret_timer,
171            selection: Selection::new(0, 0),
172            preedit_backup: None,
173            text_overflow: None,
174        }
175        .build(cx, move |cx| {
176            cx.add_listener(move |textbox: &mut Self, cx, event| {
177                let flag: bool = textbox.edit;
178                event.map(|window_event, meta| match window_event {
179                    WindowEvent::MouseDown(_) => {
180                        if flag && meta.origin != cx.current() && cx.hovered() != cx.current() {
181                            cx.emit(TextEvent::Blur);
182                        }
183                    }
184
185                    _ => {}
186                });
187            });
188        })
189        .toggle_class("multiline", kind == TextboxKind::MultiLineWrapped)
190        .text_wrap(kind == TextboxKind::MultiLineWrapped)
191        .navigable(true)
192        .role(if kind == TextboxKind::SingleLine {
193            Role::TextInput
194        } else {
195            Role::MultilineTextInput
196        })
197        .text_value(lens)
198        .toggle_class("caret", Self::show_caret)
199        .text(lens)
200        .placeholder_shown(Self::show_placeholder)
201        .bind(lens, |handle, lens| {
202            let mut text = lens.get(&handle).to_string_local(handle.cx);
203            let flag = text.is_empty();
204            if flag {
205                text = Self::placeholder.get(&handle).to_string_local(handle.cx);
206            }
207            handle
208                .modify(|textbox| {
209                    textbox.show_placeholder = flag;
210                })
211                .text(text);
212        })
213        .bind(Self::show_placeholder, |handle, show_placeholder| {
214            let flag = show_placeholder.get(&handle);
215            if flag {
216                let placeholder = Self::placeholder.get(&handle).to_string_local(handle.cx);
217                handle.text(placeholder);
218            }
219        })
220        .bind(Self::placeholder, |handle, placeholder| {
221            let placeholder = placeholder.get(&handle).to_string_local(handle.cx);
222            let flag = Self::show_placeholder.get(&handle);
223            if flag {
224                handle.text(placeholder);
225            }
226        })
227    }
228
229    fn insert_text(&mut self, cx: &mut EventContext, txt: &str) {
230        if let Some(text) = cx.style.text.get_mut(cx.current) {
231            if self.show_placeholder && !txt.is_empty() {
232                text.clear();
233                self.show_placeholder = false;
234            }
235
236            text.edit(self.selection.range(), txt);
237
238            self.selection = Selection::caret(self.selection.min() + txt.len());
239
240            self.show_placeholder = text.is_empty();
241            cx.style.needs_text_update(cx.current);
242            cx.style.needs_access_update(cx.current);
243        }
244    }
245
246    fn update_preedit(
247        &mut self,
248        cx: &mut EventContext,
249        preedit_txt: &str,
250        cursor: Option<(usize, usize)>,
251    ) {
252        if preedit_txt.is_empty() || cursor.is_none() {
253            return;
254        }
255
256        if let Some(text) = cx.style.text.get_mut(cx.current) {
257            if self.show_placeholder {
258                text.clear();
259                self.show_placeholder = false;
260            }
261
262            if !self.selection.is_caret() {
263                let start = self.selection.min();
264                let end = self.selection.max();
265
266                if end > start && end <= text.len() {
267                    text.replace_range(start..end, "");
268                }
269                self.selection = Selection::caret(start);
270            }
271
272            let preedit_backup = self
273                .preedit_backup
274                .get_or_insert_with(|| PreeditBackup::new(String::new(), self.selection));
275
276            let original_selection = preedit_backup.original_selection;
277            let prev_preedit_text = &preedit_backup.prev_preedit;
278
279            if prev_preedit_text == preedit_txt {
280                // Move the cursor only
281                let new_selection = Selection::caret(original_selection.min() + cursor.unwrap().0);
282                self.selection = new_selection;
283            } else {
284                // Bytes index
285                let start = original_selection.min();
286                let end = start + prev_preedit_text.chars().map(|c| c.len_utf8()).sum::<usize>();
287
288                // Delete old preedit text
289                if end > start && end <= text.len() {
290                    text.replace_range(start..end, "");
291                }
292
293                text.insert_str(start, preedit_txt);
294
295                if let Some((cursor_index, _)) = cursor {
296                    let new_caret = original_selection.min() + cursor_index;
297                    self.selection = Selection::caret(new_caret);
298                } else {
299                    // If there is no valid cursor, the default behavior is to move to the end of the text.
300                    let new_caret = original_selection.min() + preedit_txt.chars().count();
301                    self.selection = Selection::caret(new_caret);
302                }
303
304                self.preedit_backup.as_mut().unwrap().set_prev_preedit(preedit_txt.to_string());
305            }
306
307            cx.style.needs_text_update(cx.current);
308        }
309    }
310
311    fn clear_preedit(&mut self, cx: &mut EventContext) {
312        if let Some(text) = cx.style.text.get_mut(cx.current) {
313            if let Some(preedit_backup) = self.preedit_backup.as_ref() {
314                let original_selection = preedit_backup.original_selection;
315                let prev_preedit_text = preedit_backup.prev_preedit.clone();
316
317                let start = original_selection.min();
318                let end = start + prev_preedit_text.chars().map(|c| c.len_utf8()).sum::<usize>();
319
320                text.replace_range(start..end, "");
321
322                self.selection = original_selection;
323
324                self.preedit_backup = None;
325            }
326        }
327    }
328
329    fn delete_text(&mut self, cx: &mut EventContext, movement: Movement) {
330        if self.show_placeholder {
331            return;
332        }
333
334        if self.preedit_backup.is_some() {
335            return;
336        }
337
338        if self.selection.is_caret() {
339            if movement == Movement::Grapheme(Direction::Upstream) {
340                if self.selection.active == 0 {
341                    return;
342                }
343                if let Some(text) = cx.style.text.get_mut(cx.current) {
344                    let del_offset = offset_for_delete_backwards(&self.selection, text);
345                    let del_range = del_offset..self.selection.active;
346
347                    self.selection = Selection::caret(del_range.start);
348
349                    text.edit(del_range, "");
350
351                    cx.style.needs_text_update(cx.current);
352                    cx.style.needs_access_update(cx.current);
353                }
354            } else if let Some(text) = cx.style.text.get_mut(cx.current) {
355                if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
356                    let to_delete = apply_movement(movement, self.selection, text, paragraph, true);
357                    self.selection = to_delete;
358                    let new_cursor_pos = self.selection.min();
359
360                    text.edit(to_delete.range(), "");
361                    self.selection = Selection::caret(new_cursor_pos);
362
363                    cx.style.needs_text_update(cx.current);
364                    cx.style.needs_access_update(cx.current);
365                }
366            }
367        } else if let Some(text) = cx.style.text.get_mut(cx.current) {
368            let del_range = self.selection.range();
369
370            self.selection = Selection::caret(del_range.start);
371
372            text.edit(del_range, "");
373
374            cx.style.needs_text_update(cx.current);
375            cx.style.needs_access_update(cx.current);
376        }
377
378        if let Some(text) = cx.style.text.get_mut(cx.current) {
379            self.show_placeholder = text.is_empty();
380        }
381    }
382
383    fn reset_text(&mut self, cx: &mut EventContext) {
384        if let Some(text) = cx.style.text.get_mut(cx.current) {
385            text.clear();
386            self.selection = Selection::caret(0);
387            self.show_placeholder = true;
388            *text = self.placeholder.clone();
389            cx.style.needs_text_update(cx.current);
390            cx.style.needs_access_update(cx.current);
391        }
392    }
393
394    /// When IME is enabled, the cursor movement logic will be controlled by [`update_preedit`].
395    ///
396    /// [`update_preedit`]: Textbox::update_preedit
397    fn move_cursor(&mut self, cx: &mut EventContext, movement: Movement, selection: bool) {
398        if let Some(text) = cx.style.text.get_mut(cx.current) {
399            if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
400                let new_selection =
401                    apply_movement(movement, self.selection, text, paragraph, selection);
402                self.selection = new_selection;
403                cx.needs_redraw();
404                cx.style.needs_access_update(cx.current);
405            }
406        }
407    }
408
409    fn select_all(&mut self, cx: &mut EventContext) {
410        if self.show_placeholder {
411            return;
412        }
413        if let Some(text) = cx.style.text.get(cx.current) {
414            self.selection.anchor = 0;
415            self.selection.active = text.len();
416            cx.needs_redraw();
417            cx.style.needs_access_update(cx.current);
418        }
419    }
420
421    fn select_word(&mut self, cx: &mut EventContext) {
422        if self.show_placeholder {
423            return;
424        }
425        self.move_cursor(cx, Movement::Word(Direction::Upstream), false);
426        self.move_cursor(cx, Movement::Word(Direction::Downstream), true);
427    }
428
429    fn select_paragraph(&mut self, cx: &mut EventContext) {
430        if self.show_placeholder {
431            return;
432        }
433        self.move_cursor(cx, Movement::ParagraphStart, false);
434        self.move_cursor(cx, Movement::ParagraphEnd, true);
435    }
436
437    fn deselect(&mut self) {
438        self.selection = Selection::caret(self.selection.active);
439    }
440
441    /// These input coordinates should be physical coordinates, i.e. what the mouse events provide.
442    /// The output text coordinates will also be physical, but relative to the top of the text
443    /// glyphs, appropriate for passage to cosmic.
444    fn coordinates_global_to_text(&self, cx: &EventContext, x: f32, y: f32) -> (f32, f32) {
445        let bounds = cx.bounds();
446
447        if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
448            let padding_left = cx.style.padding_left.get(cx.current).copied().unwrap_or_default();
449            let padding_top = cx.style.padding_top.get(cx.current).copied().unwrap_or_default();
450            let _padding_right =
451                cx.style.padding_right.get(cx.current).copied().unwrap_or_default();
452            let padding_bottom =
453                cx.style.padding_bottom.get(cx.current).copied().unwrap_or_default();
454
455            let logical_parent_width = cx.physical_to_logical(bounds.w);
456            let logical_parent_height = cx.physical_to_logical(bounds.h);
457
458            let padding_left = padding_left.to_px(logical_parent_width, 0.0) * cx.scale_factor();
459            let padding_top = padding_top.to_px(logical_parent_height, 0.0) * cx.scale_factor();
460            let padding_bottom =
461                padding_bottom.to_px(logical_parent_height, 0.0) * cx.scale_factor();
462
463            let (mut top, _) = match cx.style.alignment.get(cx.current).copied().unwrap_or_default()
464            {
465                Alignment::TopLeft => (0.0, 0.0),
466                Alignment::TopCenter => (0.0, 0.5),
467                Alignment::TopRight => (0.0, 1.0),
468                Alignment::Left => (0.5, 0.0),
469                Alignment::Center => (0.5, 0.5),
470                Alignment::Right => (0.5, 1.0),
471                Alignment::BottomLeft => (1.0, 0.0),
472                Alignment::BottomCenter => (1.0, 0.5),
473                Alignment::BottomRight => (1.0, 1.0),
474            };
475
476            top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
477
478            let x = x - bounds.x - padding_left;
479            let y = y - bounds.y - padding_top - top;
480
481            (x, y)
482        } else {
483            (x, y)
484        }
485    }
486
487    /// This function takes window-global physical coordinates.
488    fn hit(&mut self, cx: &mut EventContext, x: f32, y: f32, selection: bool) {
489        if let Some(text) = cx.style.text.get(cx.current) {
490            if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
491                let x = x - self.transform.borrow().0;
492                let y = y - self.transform.borrow().1;
493                let gp = paragraph
494                    .get_glyph_position_at_coordinate(self.coordinates_global_to_text(cx, x, y));
495                let num_graphemes = text.graphemes(true).count();
496                let pos = (gp.position as usize).min(num_graphemes);
497                let mut cursor = text.len();
498                for (i, (j, _)) in text.grapheme_indices(true).enumerate() {
499                    if pos == i {
500                        cursor = j;
501                        break;
502                    }
503                }
504
505                if selection {
506                    self.selection.active = cursor;
507                } else {
508                    self.selection = Selection::caret(cursor);
509                }
510
511                cx.needs_redraw();
512                cx.style.needs_access_update(cx.current);
513            }
514        }
515    }
516
517    /// This function takes window-global physical coordinates.
518    fn drag(&mut self, cx: &mut EventContext, x: f32, y: f32) {
519        if let Some(text) = cx.style.text.get(cx.current) {
520            if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
521                let x = x - self.transform.borrow().0;
522                let y = y - self.transform.borrow().1;
523                let gp = paragraph
524                    .get_glyph_position_at_coordinate(self.coordinates_global_to_text(cx, x, y));
525                let num_graphemes = text.graphemes(true).count();
526                let pos = (gp.position as usize).min(num_graphemes);
527
528                let mut cursor = text.len();
529                for (i, (j, _)) in text.grapheme_indices(true).enumerate() {
530                    if pos == i {
531                        cursor = j;
532                        break;
533                    }
534                }
535
536                self.selection.active = cursor;
537
538                cx.needs_redraw();
539                cx.style.needs_access_update(cx.current);
540            }
541        }
542    }
543
544    // /// This function takes window-global physical dimensions.
545    // fn scroll(&mut self, cx: &mut EventContext, x: f32, y: f32) {}
546
547    #[cfg(feature = "clipboard")]
548    fn clone_selected(&self, cx: &mut EventContext) -> Option<String> {
549        if let Some(text) = cx.style.text.get(cx.current) {
550            let substring = &text[self.selection.range()];
551            return Some(substring.to_string());
552        }
553
554        None
555    }
556
557    fn clone_text(&self, cx: &mut EventContext) -> String {
558        if self.show_placeholder {
559            return String::new();
560        }
561
562        if let Some(text) = cx.style.text.get(cx.current) { text.clone() } else { String::new() }
563    }
564
565    fn reset_caret_timer(&mut self, cx: &mut EventContext) {
566        cx.stop_timer(self.caret_timer);
567        if !cx.is_read_only() {
568            self.show_caret = true;
569            cx.start_timer(self.caret_timer);
570        }
571    }
572
573    fn reset_ime_position(&mut self, cx: &mut EventContext) {
574        // TODO: Make the position of IME follow the cursor.
575        cx.event_queue.push_back(
576            Event::new(WindowEvent::SetImeCursorArea(
577                (cx.bounds().x as u32, cx.bounds().y as u32),
578                ((cx.bounds().width()) as u32, cx.bounds().height() as u32),
579            ))
580            .target(cx.current),
581        );
582    }
583
584    fn draw_selection(&self, cx: &mut DrawContext, canvas: &Canvas) {
585        if !self.selection.is_caret() {
586            if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
587                if let Some(text) = cx.style.text.get(cx.current) {
588                    let min = text.current_grapheme_offset(self.selection.min());
589                    let max = text.current_grapheme_offset(self.selection.max());
590
591                    let cursor_rects = paragraph.get_rects_for_range(
592                        min..max,
593                        RectHeightStyle::Tight,
594                        RectWidthStyle::Tight,
595                    );
596
597                    for cursor_rect in cursor_rects {
598                        let bounds = cx.bounds();
599
600                        let alignment = cx.alignment();
601
602                        let (mut top, left) = match alignment {
603                            Alignment::TopLeft => (0.0, 0.0),
604                            Alignment::TopCenter => (0.0, 0.5),
605                            Alignment::TopRight => (0.0, 1.0),
606                            Alignment::Left => (0.5, 0.0),
607                            Alignment::Center => (0.5, 0.5),
608                            Alignment::Right => (0.5, 1.0),
609                            Alignment::BottomLeft => (1.0, 0.0),
610                            Alignment::BottomCenter => (1.0, 0.5),
611                            Alignment::BottomRight => (1.0, 1.0),
612                        };
613
614                        let padding_top = match cx.padding_top() {
615                            Units::Pixels(val) => val,
616                            _ => 0.0,
617                        };
618
619                        let padding_bottom = match cx.padding_bottom() {
620                            Units::Pixels(val) => val,
621                            _ => 0.0,
622                        };
623
624                        top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
625
626                        let padding_left = match cx.padding_left() {
627                            Units::Pixels(val) => val,
628                            _ => 0.0,
629                        };
630
631                        let x = bounds.x + padding_left + cursor_rect.rect.left + left;
632                        let y = bounds.y + padding_top + cursor_rect.rect.top + top;
633
634                        let x2 = x + (cursor_rect.rect.right - cursor_rect.rect.left);
635                        let y2 = y + (cursor_rect.rect.bottom - cursor_rect.rect.top);
636
637                        let mut paint = Paint::default();
638                        paint.set_anti_alias(true);
639                        paint.set_style(PaintStyle::Fill);
640                        paint.set_color(cx.selection_color());
641
642                        canvas.draw_rect(Rect::new(x, y, x2, y2), &paint);
643                    }
644                }
645            }
646        }
647    }
648
649    /// Draw text caret for the current view.
650    pub fn draw_text_caret(&self, cx: &mut DrawContext, canvas: &Canvas) {
651        if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
652            if let Some(text) = cx.style.text.get(cx.current) {
653                let bounds = cx.bounds();
654
655                let current = text.current_grapheme_offset(self.selection.active);
656
657                let rects = paragraph.get_rects_for_range(
658                    current..current + 1,
659                    RectHeightStyle::Tight,
660                    RectWidthStyle::Tight,
661                );
662
663                let cursor_rect = rects.first().unwrap();
664
665                let alignment = cx.alignment();
666
667                let (mut top, _) = match alignment {
668                    Alignment::TopLeft => (0.0, 0.0),
669                    Alignment::TopCenter => (0.0, 0.5),
670                    Alignment::TopRight => (0.0, 1.0),
671                    Alignment::Left => (0.5, 0.0),
672                    Alignment::Center => (0.5, 0.5),
673                    Alignment::Right => (0.5, 1.0),
674                    Alignment::BottomLeft => (1.0, 0.0),
675                    Alignment::BottomCenter => (1.0, 0.5),
676                    Alignment::BottomRight => (1.0, 1.0),
677                };
678
679                let padding_top = match cx.padding_top() {
680                    Units::Pixels(val) => val,
681                    _ => 0.0,
682                };
683
684                let padding_bottom = match cx.padding_bottom() {
685                    Units::Pixels(val) => val,
686                    _ => 0.0,
687                };
688
689                top *= bounds.height() - padding_top - padding_bottom - paragraph.height();
690
691                let padding_left = match cx.padding_left() {
692                    Units::Pixels(val) => val,
693                    _ => 0.0,
694                };
695
696                let padding_right = match cx.padding_right() {
697                    Units::Pixels(val) => val,
698                    _ => 0.0,
699                };
700
701                let x = (bounds.x + padding_left + cursor_rect.rect.left).round();
702                let y = (bounds.y + padding_top + cursor_rect.rect.top + top).round();
703
704                let x2 = x + 1.0;
705                let y2 = y + (cursor_rect.rect.bottom - cursor_rect.rect.top);
706
707                let mut paint = Paint::default();
708                paint.set_anti_alias(true);
709                paint.set_style(PaintStyle::Fill);
710                paint.set_color(cx.caret_color());
711
712                canvas.draw_rect(Rect::new(x, y, x2, y2), &paint);
713
714                let mut transform = self.transform.borrow_mut();
715
716                let mut text_bounds = cx.text_context.text_bounds.get(cx.current).copied().unwrap();
717
718                let mut bounds = cx.bounds();
719
720                text_bounds.x += bounds.x;
721                text_bounds.y += bounds.y;
722
723                bounds =
724                    bounds.shrink_sides(padding_left, padding_top, padding_right, padding_bottom);
725
726                let (tx, ty) =
727                    enforce_text_bounds(&text_bounds, &bounds, (transform.0, transform.1));
728
729                let caret_box = BoundingBox::from_min_max(x, y, x2, y2);
730
731                let (new_tx, new_ty) = ensure_visible(&caret_box, &bounds, (tx, ty));
732
733                if new_tx != transform.0 || new_ty != transform.1 {
734                    *transform = (new_tx, new_ty);
735                    cx.needs_redraw();
736                }
737            }
738        }
739    }
740}
741
742impl<L: Lens> Handle<'_, Textbox<L>> {
743    /// Sets the callback triggered when a textbox is edited, i.e. text is inserted/deleted.
744    ///
745    /// Callback provides the current text of the textbox.
746    pub fn on_edit<F>(self, callback: F) -> Self
747    where
748        F: 'static + Fn(&mut EventContext, String) + Send + Sync,
749    {
750        self.modify(|textbox: &mut Textbox<L>| textbox.on_edit = Some(Box::new(callback)))
751    }
752
753    /// Sets the callback triggered when a textbox is submitted,
754    /// i.e. when the enter key is pressed with a single-line textbox or the textbox loses focus.
755    ///
756    /// Callback provides the text of the textbox and a flag to indicate if the submit was due to a key press or a loss of focus.
757    pub fn on_submit<F>(self, callback: F) -> Self
758    where
759        F: 'static + Fn(&mut EventContext, L::Target, bool) + Send + Sync,
760    {
761        self.modify(|textbox: &mut Textbox<L>| textbox.on_submit = Some(Box::new(callback)))
762    }
763
764    /// Sets the callback triggered when a textbox is blurred, i.e. the mouse is pressed outside of the textbox.
765    pub fn on_blur<F>(self, callback: F) -> Self
766    where
767        F: 'static + Fn(&mut EventContext) + Send + Sync,
768    {
769        self.modify(|textbox: &mut Textbox<L>| textbox.on_blur = Some(Box::new(callback)))
770    }
771
772    /// Sets the callback triggered when a textbox edit is cancelled, i.e. the escape key is pressed while editing.
773    pub fn on_cancel<F>(self, callback: F) -> Self
774    where
775        F: 'static + Fn(&mut EventContext) + Send + Sync,
776    {
777        self.modify(|textbox: &mut Textbox<L>| textbox.on_cancel = Some(Box::new(callback)))
778    }
779
780    /// Sets a validation closure which is called when the textbox is edited and sets the validity attribute to the output of the closure.
781    ///
782    /// If a textbox is modified with the validate modifier then the `on_submit` will not be called if the text is invalid.
783    pub fn validate<F>(self, is_valid: F) -> Self
784    where
785        F: 'static + Fn(&L::Target) -> bool + Send + Sync,
786    {
787        self.modify(|textbox| textbox.validate = Some(Box::new(is_valid)))
788    }
789
790    /// Sets the placeholder text that appears when the textbox has no value.
791    pub fn placeholder<P: ToStringLocalized>(self, text: impl Res<P>) -> Self {
792        text.set_or_bind(self.cx, self.entity, move |cx, val| {
793            let txt = val.get(cx).to_string_local(cx);
794            cx.emit(TextEvent::SetPlaceholder(txt));
795            cx.needs_relayout();
796            cx.needs_redraw(self.entity);
797        });
798
799        self
800    }
801}
802
803/// Converts a byte offset (relative to line start) into a character index
804/// within the `character_lengths` array for AccessKit text positioning.
805fn byte_offset_to_char_index(character_lengths: &[u8], byte_offset: usize) -> usize {
806    let mut cumulative = 0;
807    for (i, &len) in character_lengths.iter().enumerate() {
808        cumulative += len as usize;
809        if byte_offset < cumulative {
810            return i;
811        }
812    }
813    character_lengths.len()
814}
815
816impl<L> View for Textbox<L>
817where
818    L: Lens<Target: Data + ToStringLocalized + std::str::FromStr>,
819{
820    fn element(&self) -> Option<&'static str> {
821        Some("textbox")
822    }
823
824    fn accessibility(&self, cx: &mut AccessContext, node: &mut AccessNode) {
825        if !self.placeholder.is_empty() {
826            node.set_placeholder(self.placeholder.clone());
827        }
828
829        let node_id = node.node_id();
830
831        let selection = self.selection;
832
833        let mut selection_active_line = None;
834        let mut selection_anchor_line = None;
835        let mut selection_active_cursor = 0;
836        let mut selection_anchor_cursor = 0;
837        let mut first_line_node_id = None;
838
839        let text = if self.show_placeholder {
840            ""
841        } else {
842            cx.style.text.get(cx.current).map(|t| t.as_str()).unwrap_or("")
843        };
844        // build_paragraph() appends a zero-width space (\u{200B}, 3 UTF-8 bytes)
845        // to every paragraph, so skia's line metrics include indices beyond the
846        // actual text. We use text.len() as the upper bound for all slicing.
847        let text_len = text.len();
848
849        if let Some(paragraph) = cx.text_context.text_paragraphs.get(cx.current) {
850            let line_metrics = paragraph.get_line_metrics();
851            for line in line_metrics.iter() {
852                // Skip lines that start beyond the actual text (i.e., the ZWS-only line)
853                if line.start_index >= text_len && text_len > 0 {
854                    continue;
855                }
856
857                // We need a child node per line
858                let mut line_node = AccessNode::new_from_parent(node_id, line.line_number);
859                line_node.set_role(Role::TextRun);
860                // TODO: Support bidirectional text.
861                line_node.set_text_direction(TextDirection::LeftToRight);
862                line_node.set_bounds(BoundingBox {
863                    x: line.left as f32,
864                    y: (line.baseline - line.ascent) as f32,
865                    w: line.width as f32,
866                    h: line.height as f32,
867                });
868
869                // Only iterate over glyphs within the actual text range
870                let glyph_end = line.end_index.min(text_len);
871                let estimated_chars = glyph_end - line.start_index;
872                let mut character_lengths: Vec<u8> = Vec::with_capacity(estimated_chars);
873                let mut character_positions: Vec<f32> = Vec::with_capacity(estimated_chars);
874                let mut character_widths: Vec<f32> = Vec::with_capacity(estimated_chars);
875                let mut glyph_pos = line.start_index;
876
877                while glyph_pos < glyph_end {
878                    if let Some(cluster_info) = paragraph.get_glyph_cluster_at(glyph_pos) {
879                        let length = cluster_info.text_range.end - cluster_info.text_range.start;
880                        if length == 0 {
881                            break;
882                        }
883
884                        character_lengths.push(length as u8);
885                        character_positions.push(cluster_info.bounds.left());
886                        character_widths.push(cluster_info.bounds.width());
887
888                        glyph_pos += length;
889                    } else {
890                        break;
891                    }
892                }
893
894                // Include the newline character for hard breaks, as AccessKit needs it
895                let line_end = if line.hard_break {
896                    line.end_including_newline.min(text_len)
897                } else {
898                    glyph_end
899                };
900                let line_text = text.get(line.start_index..line_end).unwrap_or("").to_owned();
901
902                if line.hard_break && line.end_including_newline <= text_len {
903                    character_lengths.push(1);
904                    character_positions.push(line.width as f32);
905                    character_widths.push(0.0);
906                }
907
908                let word_lengths: Vec<u8> = line_text
909                    .unicode_words()
910                    .map(|word| word.graphemes(true).count() as u8)
911                    .collect();
912
913                if first_line_node_id.is_none() {
914                    first_line_node_id = Some(line_node.node_id());
915                }
916
917                // Check if this line contains the selection active (focus) position
918                if selection.active >= line.start_index && selection.active <= line_end {
919                    selection_active_line = Some(line_node.node_id());
920                    selection_active_cursor = byte_offset_to_char_index(
921                        &character_lengths,
922                        selection.active - line.start_index,
923                    );
924                }
925
926                // Check if this line contains the selection anchor position
927                if selection.anchor >= line.start_index && selection.anchor <= line_end {
928                    selection_anchor_line = Some(line_node.node_id());
929                    selection_anchor_cursor = byte_offset_to_char_index(
930                        &character_lengths,
931                        selection.anchor - line.start_index,
932                    );
933                }
934
935                line_node.set_value(line_text.into_boxed_str());
936                line_node.set_character_lengths(character_lengths.into_boxed_slice());
937                line_node.set_character_positions(character_positions.into_boxed_slice());
938                line_node.set_character_widths(character_widths.into_boxed_slice());
939                line_node.set_word_lengths(word_lengths.into_boxed_slice());
940
941                node.add_child(line_node);
942            }
943        }
944
945        if let Some(fallback) = first_line_node_id {
946            node.set_text_selection(TextSelection {
947                anchor: TextPosition {
948                    node: selection_anchor_line.unwrap_or(fallback),
949                    character_index: selection_anchor_cursor,
950                },
951                focus: TextPosition {
952                    node: selection_active_line.unwrap_or(fallback),
953                    character_index: selection_active_cursor,
954                },
955            });
956        }
957    }
958
959    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
960        // Window Events
961        event.map(|window_event, meta| match window_event {
962            WindowEvent::MouseDown(MouseButton::Left) => {
963                if meta.origin == cx.current {
964                    return;
965                }
966
967                if cx.is_over() {
968                    if !cx.is_disabled() {
969                        cx.focus_with_visibility(false);
970                        cx.capture();
971                        cx.set_checked(true);
972                        cx.lock_cursor_icon();
973
974                        if !self.edit {
975                            cx.emit(TextEvent::StartEdit);
976                        }
977                        self.reset_caret_timer(cx);
978                        cx.emit(TextEvent::Hit(
979                            cx.mouse.cursor_x,
980                            cx.mouse.cursor_y,
981                            cx.modifiers.shift(),
982                        ));
983                    }
984                } else {
985                    cx.emit(TextEvent::Submit(false));
986                    cx.release();
987                    cx.set_checked(false);
988
989                    // Forward event to hovered
990                    cx.event_queue.push_back(
991                        Event::new(WindowEvent::MouseDown(MouseButton::Left)).target(cx.hovered()),
992                    );
993                    cx.event_queue.push_back(
994                        Event::new(WindowEvent::PressDown { mouse: true }).target(cx.hovered()),
995                    );
996                }
997            }
998
999            WindowEvent::FocusIn => {
1000                if cx.mouse.left.pressed != cx.current()
1001                    || cx.mouse.left.state == MouseButtonState::Released
1002                {
1003                    cx.emit(TextEvent::StartEdit);
1004                }
1005            }
1006
1007            WindowEvent::FocusOut => {
1008                cx.emit(TextEvent::EndEdit);
1009            }
1010
1011            WindowEvent::MouseDoubleClick(MouseButton::Left) => {
1012                cx.emit(TextEvent::SelectWord);
1013            }
1014
1015            WindowEvent::MouseTripleClick(MouseButton::Left) => {
1016                cx.emit(TextEvent::SelectParagraph);
1017            }
1018
1019            WindowEvent::MouseUp(MouseButton::Left) => {
1020                self.reset_caret_timer(cx);
1021                cx.unlock_cursor_icon();
1022                cx.release();
1023            }
1024
1025            WindowEvent::MouseMove(x, y) => {
1026                if cx.mouse.left.state == MouseButtonState::Pressed
1027                    && cx.mouse.left.pressed == cx.current
1028                {
1029                    if self.edit {
1030                        self.reset_caret_timer(cx);
1031                    }
1032                    if cx.mouse.left.pos_down.0 != *x || cx.mouse.left.pos_down.1 != *y {
1033                        cx.emit(TextEvent::Drag(cx.mouse.cursor_x, cx.mouse.cursor_y));
1034                    }
1035                }
1036            }
1037
1038            WindowEvent::MouseScroll(x, y) => {
1039                cx.emit(TextEvent::Scroll(*x, *y));
1040            }
1041
1042            WindowEvent::CharInput(c) => {
1043                if *c != '\u{1b}' && // Escape
1044                    *c != '\u{8}' && // Backspace
1045                    *c != '\u{9}' && // Tab
1046                    *c != '\u{7f}' && // Delete
1047                    *c != '\u{0d}' && // Carriage return
1048                    !cx.modifiers.ctrl() &&
1049                    !cx.modifiers.logo() &&
1050                    self.edit &&
1051                    !cx.is_read_only()
1052                {
1053                    self.reset_caret_timer(cx);
1054                    cx.emit(TextEvent::InsertText(String::from(*c)));
1055                }
1056            }
1057
1058            WindowEvent::ImeCommit(text) => {
1059                if !cx.modifiers.ctrl() && !cx.modifiers.logo() && self.edit && !cx.is_read_only() {
1060                    self.reset_caret_timer(cx);
1061                    cx.emit(TextEvent::ClearPreedit);
1062                    cx.emit(TextEvent::InsertText(text.to_string()));
1063
1064                    self.reset_ime_position(cx);
1065                }
1066            }
1067
1068            WindowEvent::ImePreedit(text, cursor) => {
1069                if !cx.modifiers.ctrl() && !cx.modifiers.logo() && self.edit && !cx.is_read_only() {
1070                    self.reset_caret_timer(cx);
1071                    cx.emit(TextEvent::UpdatePreedit(text.to_string(), *cursor));
1072                }
1073            }
1074
1075            WindowEvent::KeyDown(code, _) => match code {
1076                Code::Enter => {
1077                    if matches!(self.kind, TextboxKind::SingleLine) {
1078                        cx.emit(TextEvent::Submit(true));
1079                    } else if !cx.is_read_only() {
1080                        self.reset_caret_timer(cx);
1081                        cx.emit(TextEvent::InsertText("\n".to_owned()));
1082                    }
1083                }
1084
1085                Code::Space => {
1086                    cx.emit(TextEvent::InsertText(String::from(" ")));
1087                }
1088
1089                Code::ArrowLeft => {
1090                    self.reset_caret_timer(cx);
1091                    let movement = if cx.modifiers.ctrl() {
1092                        Movement::Word(Direction::Left)
1093                    } else {
1094                        Movement::Grapheme(Direction::Left)
1095                    };
1096
1097                    cx.emit(TextEvent::MoveCursor(movement, cx.modifiers.shift()));
1098                }
1099
1100                Code::ArrowRight => {
1101                    self.reset_caret_timer(cx);
1102
1103                    let movement = if cx.modifiers.ctrl() {
1104                        Movement::Word(Direction::Right)
1105                    } else {
1106                        Movement::Grapheme(Direction::Right)
1107                    };
1108
1109                    cx.emit(TextEvent::MoveCursor(movement, cx.modifiers.shift()));
1110                }
1111
1112                Code::ArrowUp => {
1113                    self.reset_caret_timer(cx);
1114                    if self.kind != TextboxKind::SingleLine {
1115                        cx.emit(TextEvent::MoveCursor(
1116                            Movement::Vertical(VerticalMovement::LineUp),
1117                            cx.modifiers.shift(),
1118                        ));
1119                    }
1120                }
1121
1122                Code::ArrowDown => {
1123                    self.reset_caret_timer(cx);
1124                    if self.kind != TextboxKind::SingleLine {
1125                        cx.emit(TextEvent::MoveCursor(
1126                            Movement::Vertical(VerticalMovement::LineDown),
1127                            cx.modifiers.shift(),
1128                        ));
1129                    }
1130                }
1131
1132                Code::Backspace => {
1133                    self.reset_caret_timer(cx);
1134                    if !cx.is_read_only() {
1135                        if cx.modifiers.ctrl() {
1136                            cx.emit(TextEvent::DeleteText(Movement::Word(Direction::Upstream)));
1137                        } else {
1138                            cx.emit(TextEvent::DeleteText(Movement::Grapheme(Direction::Upstream)));
1139                        }
1140                    }
1141                }
1142
1143                Code::Delete => {
1144                    self.reset_caret_timer(cx);
1145                    if !cx.is_read_only() {
1146                        if cx.modifiers.ctrl() {
1147                            cx.emit(TextEvent::DeleteText(Movement::Word(Direction::Downstream)));
1148                        } else {
1149                            cx.emit(TextEvent::DeleteText(Movement::Grapheme(
1150                                Direction::Downstream,
1151                            )));
1152                        }
1153                    }
1154                }
1155
1156                Code::Escape => {
1157                    if let Some(callback) = &self.on_cancel {
1158                        (callback)(cx);
1159                    } else {
1160                        cx.emit(TextEvent::EndEdit);
1161                    }
1162                }
1163
1164                Code::Home => {
1165                    self.reset_caret_timer(cx);
1166                    cx.emit(TextEvent::MoveCursor(Movement::LineStart, cx.modifiers.shift()));
1167                }
1168
1169                Code::End => {
1170                    self.reset_caret_timer(cx);
1171                    cx.emit(TextEvent::MoveCursor(Movement::LineEnd, cx.modifiers.shift()));
1172                }
1173
1174                Code::PageUp | Code::PageDown => {
1175                    self.reset_caret_timer(cx);
1176                    let direction = if *code == Code::PageUp {
1177                        Direction::Upstream
1178                    } else {
1179                        Direction::Downstream
1180                    };
1181                    cx.emit(TextEvent::MoveCursor(
1182                        if cx.modifiers.ctrl() {
1183                            Movement::Body(direction)
1184                        } else {
1185                            Movement::Page(direction)
1186                        },
1187                        cx.modifiers.shift(),
1188                    ));
1189                }
1190
1191                Code::KeyA => {
1192                    #[cfg(target_os = "macos")]
1193                    let modifier = Modifiers::SUPER;
1194                    #[cfg(not(target_os = "macos"))]
1195                    let modifier = Modifiers::CTRL;
1196
1197                    if cx.modifiers == &modifier {
1198                        cx.emit(TextEvent::SelectAll);
1199                    }
1200                }
1201
1202                Code::KeyC => {
1203                    #[cfg(target_os = "macos")]
1204                    let modifier = Modifiers::SUPER;
1205                    #[cfg(not(target_os = "macos"))]
1206                    let modifier = Modifiers::CTRL;
1207
1208                    if cx.modifiers == &modifier {
1209                        cx.emit(TextEvent::Copy);
1210                    }
1211                }
1212
1213                Code::KeyV => {
1214                    #[cfg(target_os = "macos")]
1215                    let modifier = Modifiers::SUPER;
1216                    #[cfg(not(target_os = "macos"))]
1217                    let modifier = Modifiers::CTRL;
1218
1219                    if cx.modifiers == &modifier {
1220                        cx.emit(TextEvent::Paste);
1221                    }
1222                }
1223
1224                Code::KeyX => {
1225                    #[cfg(target_os = "macos")]
1226                    let modifier = Modifiers::SUPER;
1227                    #[cfg(not(target_os = "macos"))]
1228                    let modifier = Modifiers::CTRL;
1229
1230                    if cx.modifiers == &modifier && !cx.is_read_only() {
1231                        cx.emit(TextEvent::Cut);
1232                    }
1233                }
1234
1235                _ => {}
1236            },
1237
1238            WindowEvent::ActionRequest(ActionRequest {
1239                action: accesskit::Action::SetTextSelection,
1240                target: _,
1241                data: Some(ActionData::SetTextSelection(_selection)),
1242            }) => {
1243                // TODO: Implement SetTextSelection action for screen reader support.
1244            }
1245
1246            _ => {}
1247        });
1248
1249        // Textbox Events
1250        event.map(|text_event, _| match text_event {
1251            TextEvent::InsertText(text) => {
1252                if self.preedit_backup.is_some() {
1253                    return;
1254                }
1255
1256                if self.show_placeholder {
1257                    self.reset_text(cx);
1258                }
1259
1260                self.insert_text(cx, text);
1261
1262                let text = self.clone_text(cx);
1263
1264                if let Ok(value) = &text.parse::<L::Target>() {
1265                    if let Some(validate) = &self.validate {
1266                        cx.set_valid(validate(value));
1267                    } else {
1268                        cx.set_valid(true);
1269                    }
1270                } else {
1271                    cx.set_valid(false);
1272                }
1273
1274                if self.edit {
1275                    if let Some(callback) = &self.on_edit {
1276                        (callback)(cx, text);
1277                    }
1278                }
1279            }
1280
1281            TextEvent::UpdatePreedit(preedit, cursor) => {
1282                self.update_preedit(cx, preedit, *cursor);
1283            }
1284
1285            TextEvent::ClearPreedit => {
1286                self.clear_preedit(cx);
1287            }
1288
1289            TextEvent::Clear => {
1290                self.reset_text(cx);
1291                // self.scroll(cx, 0.0, 0.0); // ensure_visible
1292                cx.needs_relayout();
1293                cx.needs_redraw();
1294            }
1295
1296            TextEvent::DeleteText(movement) => {
1297                if self.edit {
1298                    self.delete_text(cx, *movement);
1299
1300                    let text = self.clone_text(cx);
1301
1302                    if let Ok(value) = &text.parse::<L::Target>() {
1303                        if let Some(validate) = &self.validate {
1304                            cx.set_valid(validate(value));
1305                        } else {
1306                            cx.set_valid(true);
1307                        }
1308                    } else {
1309                        cx.set_valid(false);
1310                    }
1311
1312                    if let Some(callback) = &self.on_edit {
1313                        (callback)(cx, text);
1314                    }
1315                }
1316            }
1317
1318            TextEvent::MoveCursor(movement, selection) => {
1319                if self.edit && !self.show_placeholder && self.preedit_backup.is_none() {
1320                    self.move_cursor(cx, *movement, *selection);
1321                }
1322            }
1323
1324            TextEvent::SetPlaceholder(text) => {
1325                self.placeholder.clone_from(text);
1326                cx.style.needs_access_update(cx.current);
1327            }
1328
1329            TextEvent::StartEdit => {
1330                if !cx.is_disabled() && !self.edit {
1331                    self.edit = true;
1332                    cx.focus_with_visibility(false);
1333                    cx.capture();
1334                    cx.set_checked(true);
1335                    self.reset_caret_timer(cx);
1336                    self.reset_ime_position(cx);
1337
1338                    self.text_overflow = cx.style.text_overflow.get_inline(cx.current).copied();
1339                    cx.style.text_overflow.remove(cx.current);
1340
1341                    let text = self.lens.get(cx);
1342                    let text = text.to_string_local(cx);
1343
1344                    if text.is_empty() {
1345                        self.show_placeholder = true;
1346                        self.selection = Selection::caret(0);
1347                        cx.style.needs_access_update(cx.current);
1348                    } else {
1349                        self.select_all(cx);
1350                    }
1351
1352                    if let Ok(value) = &text.parse::<L::Target>() {
1353                        if let Some(validate) = &self.validate {
1354                            cx.set_valid(validate(value));
1355                        } else {
1356                            cx.set_valid(true);
1357                        }
1358                    } else {
1359                        cx.set_valid(false);
1360                    }
1361                }
1362
1363                cx.style.needs_text_update(cx.current);
1364            }
1365
1366            TextEvent::EndEdit => {
1367                self.deselect();
1368                self.edit = false;
1369                cx.set_checked(false);
1370                cx.release();
1371                cx.stop_timer(self.caret_timer);
1372
1373                let text = self.lens.get(cx);
1374                let text = text.to_string_local(cx);
1375
1376                if let Some(text_overflow) = self.text_overflow {
1377                    cx.style.text_overflow.insert(cx.current, text_overflow);
1378                } else {
1379                    cx.style.text_overflow.remove(cx.current);
1380                }
1381
1382                self.select_all(cx);
1383
1384                if let Ok(value) = &text.parse::<L::Target>() {
1385                    if let Some(validate) = &self.validate {
1386                        cx.set_valid(validate(value));
1387                    } else {
1388                        cx.set_valid(true);
1389                    }
1390                } else {
1391                    cx.set_valid(false);
1392                }
1393
1394                // Reset transform to 0,0
1395                let mut transform = self.transform.borrow_mut();
1396                *transform = (0.0, 0.0);
1397
1398                // Reset cursor position
1399                self.selection = Selection::caret(0);
1400
1401                cx.style.needs_text_update(cx.current);
1402                cx.style.needs_access_update(cx.current);
1403            }
1404
1405            TextEvent::Blur => {
1406                cx.set_checked(false);
1407                if let Some(callback) = &self.on_blur {
1408                    (callback)(cx);
1409                } else {
1410                    cx.emit(TextEvent::Submit(false));
1411                    cx.emit(TextEvent::EndEdit);
1412                }
1413            }
1414
1415            TextEvent::Submit(reason) => {
1416                if let Some(callback) = &self.on_submit {
1417                    if cx.is_valid() {
1418                        let text = self.clone_text(cx);
1419                        if let Ok(value) = text.parse::<L::Target>() {
1420                            (callback)(cx, value, *reason);
1421                        }
1422                    }
1423                }
1424            }
1425
1426            TextEvent::SelectAll => {
1427                self.select_all(cx);
1428            }
1429
1430            TextEvent::SelectWord => {
1431                self.select_word(cx);
1432            }
1433
1434            TextEvent::SelectParagraph => {
1435                self.select_paragraph(cx);
1436            }
1437
1438            TextEvent::Hit(posx, posy, selection) => {
1439                if !self.show_placeholder {
1440                    self.hit(cx, *posx, *posy, *selection);
1441                }
1442            }
1443
1444            TextEvent::Drag(posx, posy) => {
1445                if !self.show_placeholder {
1446                    self.drag(cx, *posx, *posy);
1447                }
1448            }
1449
1450            TextEvent::Scroll(_x, _y) => {
1451                //self.scroll(cx, *x, *y);
1452            }
1453
1454            TextEvent::Copy =>
1455            {
1456                #[cfg(feature = "clipboard")]
1457                if self.edit {
1458                    if let Some(selected_text) = self.clone_selected(cx) {
1459                        if !selected_text.is_empty() {
1460                            cx.set_clipboard(selected_text)
1461                                .expect("Failed to add text to clipboard");
1462                        }
1463                    }
1464                }
1465            }
1466
1467            TextEvent::Paste =>
1468            {
1469                #[cfg(feature = "clipboard")]
1470                if self.edit {
1471                    if let Ok(text) = cx.get_clipboard() {
1472                        cx.emit(TextEvent::InsertText(text));
1473                    }
1474                }
1475            }
1476
1477            TextEvent::Cut =>
1478            {
1479                #[cfg(feature = "clipboard")]
1480                if self.edit {
1481                    if let Some(selected_text) = self.clone_selected(cx) {
1482                        if !selected_text.is_empty() {
1483                            cx.set_clipboard(selected_text)
1484                                .expect("Failed to add text to clipboard");
1485                            self.delete_text(cx, Movement::Grapheme(Direction::Upstream));
1486
1487                            let text = self.clone_text(cx);
1488
1489                            if let Ok(value) = &text.parse::<L::Target>() {
1490                                if let Some(validate) = &self.validate {
1491                                    cx.set_valid(validate(value));
1492                                } else {
1493                                    cx.set_valid(true);
1494                                }
1495                            } else {
1496                                cx.set_valid(false);
1497                            }
1498
1499                            if let Some(callback) = &self.on_edit {
1500                                (callback)(cx, text);
1501                            }
1502                        }
1503                    }
1504                }
1505            }
1506
1507            TextEvent::ToggleCaret => {
1508                self.show_caret ^= true;
1509            }
1510        });
1511    }
1512
1513    // Use custom drawing for the textbox so a transform can be applied to just the text.
1514    fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
1515        cx.draw_shadows(canvas);
1516        cx.draw_background(canvas);
1517        cx.draw_border(canvas);
1518        cx.draw_outline(canvas);
1519        canvas.save();
1520        let transform = *self.transform.borrow();
1521        canvas.translate((transform.0, transform.1));
1522        cx.draw_text(canvas);
1523
1524        if self.edit {
1525            self.draw_selection(cx, canvas);
1526            self.draw_text_caret(cx, canvas);
1527        }
1528
1529        canvas.restore();
1530    }
1531}