1use std::cell::RefCell;
2use std::rc::Rc;
3
4use 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};
11use accesskit::{ActionData, ActionRequest};
13use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle};
14use skia_safe::{Paint, PaintStyle, Rect};
15use unicode_segmentation::UnicodeSegmentation;
16
17pub enum TextEvent {
19 InsertText(String),
21 UpdatePreedit(String, Option<(usize, usize)>),
23 ClearPreedit,
25 Clear,
27 DeleteText(Movement),
29 MoveCursor(Movement, bool),
31 SelectAll,
33 SelectWord,
35 SelectParagraph,
37 StartEdit,
39 EndEdit,
41 Submit(bool),
43 Hit(f32, f32, bool),
45 Drag(f32, f32),
47 Scroll(f32, f32),
49 Copy,
51 Paste,
53 Cut,
55 SetPlaceholder(String),
57 Blur,
59 ToggleCaret,
61}
62
63#[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#[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 pub fn new(cx: &mut Context, lens: L) -> Handle<Self> {
122 Self::new_core(cx, lens, TextboxKind::SingleLine)
123 }
124
125 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 let new_selection = Selection::caret(original_selection.min() + cursor.unwrap().0);
279 self.selection = new_selection;
280 } else {
281 let start = original_selection.min();
283 let end = start + prev_preedit_text.chars().map(|c| c.len_utf8()).sum::<usize>();
284
285 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 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 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 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 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 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 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 #[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 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 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 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 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 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 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 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 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 _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 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 let mut character_lengths = Vec::new();
844 let mut character_positions = Vec::new();
845 let mut character_widths = Vec::new();
846
847 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 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 }
876 }
877
878 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 node.add_child(line_node);
935
936 }
939 }
940 }
941
942 }
953
954 fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
955 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 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}' && *c != '\u{8}' && *c != '\u{9}' && *c != '\u{7f}' && *c != '\u{0d}' && !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 }
1286
1287 _ => {}
1288 });
1289
1290 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 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 let mut transform = self.transform.borrow_mut();
1440 *transform = (0.0, 0.0);
1441
1442 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 }
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 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(canvas);
1567 if self.edit {
1568 self.draw_selection(cx, canvas);
1569 self.draw_text_caret(cx, canvas);
1570 }
1571 canvas.restore();
1572 }
1573}