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