vizia_core/views/
textbox.rs

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