1use super::{
11    EventResult, FontMetrics, InputType, Item, ItemConsts, ItemRc, ItemRef, KeyEventArg,
12    KeyEventResult, KeyEventType, PointArg, PointerEventButton, RenderingResult,
13    TextHorizontalAlignment, TextOverflow, TextStrokeStyle, TextVerticalAlignment, TextWrap,
14    VoidArg, WindowItem,
15};
16use crate::graphics::{Brush, Color, FontRequest};
17use crate::input::{
18    key_codes, FocusEvent, FocusEventResult, FocusReason, InputEventFilterResult, InputEventResult,
19    KeyEvent, KeyboardModifiers, MouseEvent, StandardShortcut, TextShortcut,
20};
21use crate::item_rendering::{CachedRenderingData, ItemRenderer, RenderText};
22use crate::layout::{LayoutInfo, Orientation};
23use crate::lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor};
24use crate::platform::Clipboard;
25#[cfg(feature = "rtti")]
26use crate::rtti::*;
27use crate::window::{InputMethodProperties, InputMethodRequest, WindowAdapter, WindowInner};
28use crate::{Callback, Coord, Property, SharedString, SharedVector};
29use alloc::rc::Rc;
30use alloc::string::String;
31use const_field_offset::FieldOffsets;
32use core::cell::Cell;
33use core::pin::Pin;
34#[allow(unused)]
35use euclid::num::Ceil;
36use i_slint_core_macros::*;
37use unicode_segmentation::UnicodeSegmentation;
38
39#[repr(C)]
41#[derive(FieldOffsets, Default, SlintElement)]
42#[pin]
43pub struct ComplexText {
44    pub width: Property<LogicalLength>,
45    pub height: Property<LogicalLength>,
46    pub text: Property<SharedString>,
47    pub font_size: Property<LogicalLength>,
48    pub font_weight: Property<i32>,
49    pub color: Property<Brush>,
50    pub horizontal_alignment: Property<TextHorizontalAlignment>,
51    pub vertical_alignment: Property<TextVerticalAlignment>,
52
53    pub font_family: Property<SharedString>,
54    pub font_italic: Property<bool>,
55    pub wrap: Property<TextWrap>,
56    pub overflow: Property<TextOverflow>,
57    pub letter_spacing: Property<LogicalLength>,
58    pub stroke: Property<Brush>,
59    pub stroke_width: Property<LogicalLength>,
60    pub stroke_style: Property<TextStrokeStyle>,
61    pub cached_rendering_data: CachedRenderingData,
62}
63
64impl Item for ComplexText {
65    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
66
67    fn layout_info(
68        self: Pin<&Self>,
69        orientation: Orientation,
70        window_adapter: &Rc<dyn WindowAdapter>,
71        self_rc: &ItemRc,
72    ) -> LayoutInfo {
73        text_layout_info(
74            self,
75            &self_rc,
76            window_adapter,
77            orientation,
78            Self::FIELD_OFFSETS.width.apply_pin(self),
79        )
80    }
81
82    fn input_event_filter_before_children(
83        self: Pin<&Self>,
84        _: &MouseEvent,
85        _window_adapter: &Rc<dyn WindowAdapter>,
86        _self_rc: &ItemRc,
87    ) -> InputEventFilterResult {
88        InputEventFilterResult::ForwardAndIgnore
89    }
90
91    fn input_event(
92        self: Pin<&Self>,
93        _: &MouseEvent,
94        _window_adapter: &Rc<dyn WindowAdapter>,
95        _self_rc: &ItemRc,
96    ) -> InputEventResult {
97        InputEventResult::EventIgnored
98    }
99
100    fn capture_key_event(
101        self: Pin<&Self>,
102        _: &KeyEvent,
103        _window_adapter: &Rc<dyn WindowAdapter>,
104        _self_rc: &ItemRc,
105    ) -> KeyEventResult {
106        KeyEventResult::EventIgnored
107    }
108
109    fn key_event(
110        self: Pin<&Self>,
111        _: &KeyEvent,
112        _window_adapter: &Rc<dyn WindowAdapter>,
113        _self_rc: &ItemRc,
114    ) -> KeyEventResult {
115        KeyEventResult::EventIgnored
116    }
117
118    fn focus_event(
119        self: Pin<&Self>,
120        _: &FocusEvent,
121        _window_adapter: &Rc<dyn WindowAdapter>,
122        _self_rc: &ItemRc,
123    ) -> FocusEventResult {
124        FocusEventResult::FocusIgnored
125    }
126
127    fn render(
128        self: Pin<&Self>,
129        backend: &mut &mut dyn ItemRenderer,
130        self_rc: &ItemRc,
131        size: LogicalSize,
132    ) -> RenderingResult {
133        (*backend).draw_text(self, self_rc, size, &self.cached_rendering_data);
134        RenderingResult::ContinueRenderingChildren
135    }
136
137    fn bounding_rect(
138        self: core::pin::Pin<&Self>,
139        _window_adapter: &Rc<dyn WindowAdapter>,
140        _self_rc: &ItemRc,
141        geometry: LogicalRect,
142    ) -> LogicalRect {
143        geometry
144    }
145
146    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
147        false
148    }
149}
150
151impl ItemConsts for ComplexText {
152    const cached_rendering_data_offset: const_field_offset::FieldOffset<
153        ComplexText,
154        CachedRenderingData,
155    > = ComplexText::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
156}
157
158impl RenderText for ComplexText {
159    fn target_size(self: Pin<&Self>) -> LogicalSize {
160        LogicalSize::from_lengths(self.width(), self.height())
161    }
162
163    fn text(self: Pin<&Self>) -> SharedString {
164        self.text()
165    }
166
167    fn font_request(self: Pin<&Self>, self_rc: &ItemRc) -> FontRequest {
168        WindowItem::resolved_font_request(
169            self_rc,
170            self.font_family(),
171            self.font_weight(),
172            self.font_size(),
173            self.letter_spacing(),
174            self.font_italic(),
175        )
176    }
177
178    fn color(self: Pin<&Self>) -> Brush {
179        self.color()
180    }
181
182    fn alignment(
183        self: Pin<&Self>,
184    ) -> (super::TextHorizontalAlignment, super::TextVerticalAlignment) {
185        (self.horizontal_alignment(), self.vertical_alignment())
186    }
187
188    fn wrap(self: Pin<&Self>) -> TextWrap {
189        self.wrap()
190    }
191
192    fn overflow(self: Pin<&Self>) -> TextOverflow {
193        self.overflow()
194    }
195
196    fn letter_spacing(self: Pin<&Self>) -> LogicalLength {
197        self.letter_spacing()
198    }
199
200    fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
201        (self.stroke(), self.stroke_width(), self.stroke_style())
202    }
203
204    fn is_markdown(self: Pin<&Self>) -> bool {
205        false
206    }
207}
208
209impl ComplexText {
210    pub fn font_metrics(
211        self: Pin<&Self>,
212        window_adapter: &Rc<dyn WindowAdapter>,
213        self_rc: &ItemRc,
214    ) -> FontMetrics {
215        let window_inner = WindowInner::from_pub(window_adapter.window());
216        let scale_factor = ScaleFactor::new(window_inner.scale_factor());
217        let font_request = self.font_request(self_rc);
218        window_adapter.renderer().font_metrics(font_request, scale_factor)
219    }
220}
221
222#[repr(C)]
224#[derive(FieldOffsets, Default, SlintElement)]
225#[pin]
226pub struct MarkdownText {
227    pub width: Property<LogicalLength>,
228    pub height: Property<LogicalLength>,
229    pub text: Property<SharedString>,
230    pub font_size: Property<LogicalLength>,
231    pub font_weight: Property<i32>,
232    pub color: Property<Brush>,
233    pub horizontal_alignment: Property<TextHorizontalAlignment>,
234    pub vertical_alignment: Property<TextVerticalAlignment>,
235
236    pub font_family: Property<SharedString>,
237    pub font_italic: Property<bool>,
238    pub wrap: Property<TextWrap>,
239    pub overflow: Property<TextOverflow>,
240    pub letter_spacing: Property<LogicalLength>,
241    pub stroke: Property<Brush>,
242    pub stroke_width: Property<LogicalLength>,
243    pub stroke_style: Property<TextStrokeStyle>,
244    pub cached_rendering_data: CachedRenderingData,
245}
246
247impl Item for MarkdownText {
248    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
249
250    fn layout_info(
251        self: Pin<&Self>,
252        orientation: Orientation,
253        window_adapter: &Rc<dyn WindowAdapter>,
254        self_rc: &ItemRc,
255    ) -> LayoutInfo {
256        text_layout_info(
257            self,
258            &self_rc,
259            window_adapter,
260            orientation,
261            Self::FIELD_OFFSETS.width.apply_pin(self),
262        )
263    }
264
265    fn input_event_filter_before_children(
266        self: Pin<&Self>,
267        _: &MouseEvent,
268        _window_adapter: &Rc<dyn WindowAdapter>,
269        _self_rc: &ItemRc,
270    ) -> InputEventFilterResult {
271        InputEventFilterResult::ForwardAndIgnore
272    }
273
274    fn input_event(
275        self: Pin<&Self>,
276        _: &MouseEvent,
277        _window_adapter: &Rc<dyn WindowAdapter>,
278        _self_rc: &ItemRc,
279    ) -> InputEventResult {
280        InputEventResult::EventIgnored
281    }
282
283    fn capture_key_event(
284        self: Pin<&Self>,
285        _: &KeyEvent,
286        _window_adapter: &Rc<dyn WindowAdapter>,
287        _self_rc: &ItemRc,
288    ) -> KeyEventResult {
289        KeyEventResult::EventIgnored
290    }
291
292    fn key_event(
293        self: Pin<&Self>,
294        _: &KeyEvent,
295        _window_adapter: &Rc<dyn WindowAdapter>,
296        _self_rc: &ItemRc,
297    ) -> KeyEventResult {
298        KeyEventResult::EventIgnored
299    }
300
301    fn focus_event(
302        self: Pin<&Self>,
303        _: &FocusEvent,
304        _window_adapter: &Rc<dyn WindowAdapter>,
305        _self_rc: &ItemRc,
306    ) -> FocusEventResult {
307        FocusEventResult::FocusIgnored
308    }
309
310    fn render(
311        self: Pin<&Self>,
312        backend: &mut &mut dyn ItemRenderer,
313        self_rc: &ItemRc,
314        size: LogicalSize,
315    ) -> RenderingResult {
316        (*backend).draw_text(self, self_rc, size, &self.cached_rendering_data);
317        RenderingResult::ContinueRenderingChildren
318    }
319
320    fn bounding_rect(
321        self: core::pin::Pin<&Self>,
322        _window_adapter: &Rc<dyn WindowAdapter>,
323        _self_rc: &ItemRc,
324        geometry: LogicalRect,
325    ) -> LogicalRect {
326        geometry
327    }
328
329    fn clips_children(self: Pin<&Self>) -> bool {
330        false
331    }
332}
333
334impl ItemConsts for MarkdownText {
335    const cached_rendering_data_offset: const_field_offset::FieldOffset<
336        MarkdownText,
337        CachedRenderingData,
338    > = MarkdownText::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
339}
340
341impl RenderText for MarkdownText {
342    fn target_size(self: Pin<&Self>) -> LogicalSize {
343        LogicalSize::from_lengths(self.width(), self.height())
344    }
345
346    fn text(self: Pin<&Self>) -> SharedString {
347        self.text()
348    }
349
350    fn font_request(self: Pin<&Self>, self_rc: &ItemRc) -> FontRequest {
351        WindowItem::resolved_font_request(
352            self_rc,
353            self.font_family(),
354            self.font_weight(),
355            self.font_size(),
356            self.letter_spacing(),
357            self.font_italic(),
358        )
359    }
360
361    fn color(self: Pin<&Self>) -> Brush {
362        self.color()
363    }
364
365    fn alignment(
366        self: Pin<&Self>,
367    ) -> (super::TextHorizontalAlignment, super::TextVerticalAlignment) {
368        (self.horizontal_alignment(), self.vertical_alignment())
369    }
370
371    fn wrap(self: Pin<&Self>) -> TextWrap {
372        self.wrap()
373    }
374
375    fn overflow(self: Pin<&Self>) -> TextOverflow {
376        self.overflow()
377    }
378
379    fn letter_spacing(self: Pin<&Self>) -> LogicalLength {
380        self.letter_spacing()
381    }
382
383    fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
384        (self.stroke(), self.stroke_width(), self.stroke_style())
385    }
386
387    fn is_markdown(self: Pin<&Self>) -> bool {
388        true
389    }
390}
391
392impl MarkdownText {
393    pub fn font_metrics(
394        self: Pin<&Self>,
395        window_adapter: &Rc<dyn WindowAdapter>,
396        self_rc: &ItemRc,
397    ) -> FontMetrics {
398        let window_inner = WindowInner::from_pub(window_adapter.window());
399        let scale_factor = ScaleFactor::new(window_inner.scale_factor());
400        let font_request = self.font_request(self_rc);
401        window_adapter.renderer().font_metrics(font_request, scale_factor)
402    }
403}
404
405#[repr(C)]
407#[derive(FieldOffsets, Default, SlintElement)]
408#[pin]
409pub struct SimpleText {
410    pub width: Property<LogicalLength>,
411    pub height: Property<LogicalLength>,
412    pub text: Property<SharedString>,
413    pub font_size: Property<LogicalLength>,
414    pub font_weight: Property<i32>,
415    pub color: Property<Brush>,
416    pub horizontal_alignment: Property<TextHorizontalAlignment>,
417    pub vertical_alignment: Property<TextVerticalAlignment>,
418
419    pub cached_rendering_data: CachedRenderingData,
420}
421
422impl Item for SimpleText {
423    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
424
425    fn layout_info(
426        self: Pin<&Self>,
427        orientation: Orientation,
428        window_adapter: &Rc<dyn WindowAdapter>,
429        self_rc: &ItemRc,
430    ) -> LayoutInfo {
431        text_layout_info(
432            self,
433            self_rc,
434            window_adapter,
435            orientation,
436            Self::FIELD_OFFSETS.width.apply_pin(self),
437        )
438    }
439
440    fn input_event_filter_before_children(
441        self: Pin<&Self>,
442        _: &MouseEvent,
443        _window_adapter: &Rc<dyn WindowAdapter>,
444        _self_rc: &ItemRc,
445    ) -> InputEventFilterResult {
446        InputEventFilterResult::ForwardAndIgnore
447    }
448
449    fn input_event(
450        self: Pin<&Self>,
451        _: &MouseEvent,
452        _window_adapter: &Rc<dyn WindowAdapter>,
453        _self_rc: &ItemRc,
454    ) -> InputEventResult {
455        InputEventResult::EventIgnored
456    }
457
458    fn capture_key_event(
459        self: Pin<&Self>,
460        _: &KeyEvent,
461        _window_adapter: &Rc<dyn WindowAdapter>,
462        _self_rc: &ItemRc,
463    ) -> KeyEventResult {
464        KeyEventResult::EventIgnored
465    }
466
467    fn key_event(
468        self: Pin<&Self>,
469        _: &KeyEvent,
470        _window_adapter: &Rc<dyn WindowAdapter>,
471        _self_rc: &ItemRc,
472    ) -> KeyEventResult {
473        KeyEventResult::EventIgnored
474    }
475
476    fn focus_event(
477        self: Pin<&Self>,
478        _: &FocusEvent,
479        _window_adapter: &Rc<dyn WindowAdapter>,
480        _self_rc: &ItemRc,
481    ) -> FocusEventResult {
482        FocusEventResult::FocusIgnored
483    }
484
485    fn render(
486        self: Pin<&Self>,
487        backend: &mut &mut dyn ItemRenderer,
488        self_rc: &ItemRc,
489        size: LogicalSize,
490    ) -> RenderingResult {
491        (*backend).draw_text(self, self_rc, size, &self.cached_rendering_data);
492        RenderingResult::ContinueRenderingChildren
493    }
494
495    fn bounding_rect(
496        self: core::pin::Pin<&Self>,
497        _window_adapter: &Rc<dyn WindowAdapter>,
498        _self_rc: &ItemRc,
499        geometry: LogicalRect,
500    ) -> LogicalRect {
501        geometry
502    }
503
504    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
505        false
506    }
507}
508
509impl ItemConsts for SimpleText {
510    const cached_rendering_data_offset: const_field_offset::FieldOffset<
511        SimpleText,
512        CachedRenderingData,
513    > = SimpleText::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
514}
515
516impl RenderText for SimpleText {
517    fn target_size(self: Pin<&Self>) -> LogicalSize {
518        LogicalSize::from_lengths(self.width(), self.height())
519    }
520
521    fn text(self: Pin<&Self>) -> SharedString {
522        self.text()
523    }
524
525    fn font_request(self: Pin<&Self>, self_rc: &ItemRc) -> FontRequest {
526        WindowItem::resolved_font_request(
527            self_rc,
528            SharedString::default(),
529            self.font_weight(),
530            self.font_size(),
531            self.letter_spacing(),
532            false,
533        )
534    }
535
536    fn color(self: Pin<&Self>) -> Brush {
537        self.color()
538    }
539
540    fn alignment(
541        self: Pin<&Self>,
542    ) -> (super::TextHorizontalAlignment, super::TextVerticalAlignment) {
543        (self.horizontal_alignment(), self.vertical_alignment())
544    }
545
546    fn wrap(self: Pin<&Self>) -> TextWrap {
547        TextWrap::default()
548    }
549
550    fn overflow(self: Pin<&Self>) -> TextOverflow {
551        TextOverflow::default()
552    }
553
554    fn letter_spacing(self: Pin<&Self>) -> LogicalLength {
555        LogicalLength::default()
556    }
557
558    fn stroke(self: Pin<&Self>) -> (Brush, LogicalLength, TextStrokeStyle) {
559        Default::default()
560    }
561
562    fn is_markdown(self: Pin<&Self>) -> bool {
563        false
564    }
565}
566
567impl SimpleText {
568    pub fn font_metrics(
569        self: Pin<&Self>,
570        window_adapter: &Rc<dyn WindowAdapter>,
571        self_rc: &ItemRc,
572    ) -> FontMetrics {
573        let window_inner = WindowInner::from_pub(window_adapter.window());
574        let scale_factor = ScaleFactor::new(window_inner.scale_factor());
575        let font_request = self.font_request(self_rc);
576        window_adapter.renderer().font_metrics(font_request, scale_factor)
577    }
578}
579
580fn text_layout_info(
581    text: Pin<&dyn RenderText>,
582    self_rc: &ItemRc,
583    window_adapter: &Rc<dyn WindowAdapter>,
584    orientation: Orientation,
585    width: Pin<&Property<LogicalLength>>,
586) -> LayoutInfo {
587    let window_inner = WindowInner::from_pub(window_adapter.window());
588    let text_string = text.text();
589    let font_request = text.font_request(self_rc);
590    let scale_factor = ScaleFactor::new(window_inner.scale_factor());
591    let implicit_size = |max_width, text_wrap| {
592        window_adapter.renderer().text_size(
593            font_request.clone(),
594            text_string.as_str(),
595            max_width,
596            scale_factor,
597            text_wrap,
598        )
599    };
600
601    match orientation {
605        Orientation::Horizontal => {
606            let implicit_size = implicit_size(None, TextWrap::NoWrap);
607            let min = match text.overflow() {
608                TextOverflow::Elide => implicit_size.width.min(
609                    window_adapter
610                        .renderer()
611                        .text_size(font_request, "…", None, scale_factor, TextWrap::NoWrap)
612                        .width,
613                ),
614                TextOverflow::Clip => match text.wrap() {
615                    TextWrap::NoWrap => implicit_size.width,
616                    TextWrap::WordWrap | TextWrap::CharWrap => 0 as Coord,
617                },
618            };
619            LayoutInfo {
620                min: min.ceil(),
621                preferred: implicit_size.width.ceil(),
622                ..LayoutInfo::default()
623            }
624        }
625        Orientation::Vertical => {
626            let h = match text.wrap() {
627                TextWrap::NoWrap => implicit_size(None, TextWrap::NoWrap).height,
628                TextWrap::WordWrap => implicit_size(Some(width.get()), TextWrap::WordWrap).height,
629                TextWrap::CharWrap => implicit_size(Some(width.get()), TextWrap::CharWrap).height,
630            }
631            .ceil();
632            LayoutInfo { min: h, preferred: h, ..LayoutInfo::default() }
633        }
634    }
635}
636
637#[repr(C)]
638#[derive(Default, Clone, Copy, PartialEq)]
639struct PreEditSelection {
643    valid: bool,
644    start: i32,
645    end: i32,
646}
647
648impl From<Option<core::ops::Range<i32>>> for PreEditSelection {
649    fn from(value: Option<core::ops::Range<i32>>) -> Self {
650        value.map_or_else(Default::default, |r| Self { valid: true, start: r.start, end: r.end })
651    }
652}
653
654impl PreEditSelection {
655    fn as_option(self) -> Option<core::ops::Range<i32>> {
656        self.valid.then_some(self.start..self.end)
657    }
658}
659
660#[repr(C)]
661#[derive(Clone)]
662enum UndoItemKind {
663    TextInsert,
664    TextRemove,
665}
666
667#[repr(C)]
668#[derive(Clone)]
669struct UndoItem {
670    pos: usize,
671    text: SharedString,
672    cursor: usize,
673    anchor: usize,
674    kind: UndoItemKind,
675}
676
677#[repr(C)]
679#[derive(FieldOffsets, Default, SlintElement)]
680#[pin]
681pub struct TextInput {
682    pub text: Property<SharedString>,
683    pub font_family: Property<SharedString>,
684    pub font_size: Property<LogicalLength>,
685    pub font_weight: Property<i32>,
686    pub font_italic: Property<bool>,
687    pub color: Property<Brush>,
688    pub selection_foreground_color: Property<Color>,
689    pub selection_background_color: Property<Color>,
690    pub horizontal_alignment: Property<TextHorizontalAlignment>,
691    pub vertical_alignment: Property<TextVerticalAlignment>,
692    pub wrap: Property<TextWrap>,
693    pub input_type: Property<InputType>,
694    pub letter_spacing: Property<LogicalLength>,
695    pub width: Property<LogicalLength>,
696    pub height: Property<LogicalLength>,
697    pub cursor_position_byte_offset: Property<i32>,
698    pub anchor_position_byte_offset: Property<i32>,
699    pub text_cursor_width: Property<LogicalLength>,
700    pub page_height: Property<LogicalLength>,
701    pub cursor_visible: Property<bool>,
702    pub has_focus: Property<bool>,
703    pub enabled: Property<bool>,
704    pub accepted: Callback<VoidArg>,
705    pub cursor_position_changed: Callback<PointArg>,
706    pub edited: Callback<VoidArg>,
707    pub key_pressed: Callback<KeyEventArg, EventResult>,
708    pub key_released: Callback<KeyEventArg, EventResult>,
709    pub single_line: Property<bool>,
710    pub read_only: Property<bool>,
711    pub preedit_text: Property<SharedString>,
712    preedit_selection: Property<PreEditSelection>,
714    pub cached_rendering_data: CachedRenderingData,
715    preferred_x_pos: Cell<Coord>,
718    pressed: Cell<u8>,
720    undo_items: Cell<SharedVector<UndoItem>>,
721    redo_items: Cell<SharedVector<UndoItem>>,
722}
723
724impl Item for TextInput {
725    fn init(self: Pin<&Self>, _self_rc: &ItemRc) {}
726
727    fn layout_info(
728        self: Pin<&Self>,
729        orientation: Orientation,
730        window_adapter: &Rc<dyn WindowAdapter>,
731        self_rc: &ItemRc,
732    ) -> LayoutInfo {
733        let text = self.text();
734        let implicit_size = |max_width, text_wrap| {
735            window_adapter.renderer().text_size(
736                self.font_request(&self_rc),
737                {
738                    if text.is_empty() {
739                        "*"
740                    } else {
741                        text.as_str()
742                    }
743                },
744                max_width,
745                ScaleFactor::new(window_adapter.window().scale_factor()),
746                text_wrap,
747            )
748        };
749
750        match orientation {
754            Orientation::Horizontal => {
755                let implicit_size = implicit_size(None, TextWrap::NoWrap);
756                let min = match self.wrap() {
757                    TextWrap::NoWrap => implicit_size.width,
758                    TextWrap::WordWrap | TextWrap::CharWrap => 0 as Coord,
759                };
760                LayoutInfo {
761                    min: min.ceil(),
762                    preferred: implicit_size.width.ceil(),
763                    ..LayoutInfo::default()
764                }
765            }
766            Orientation::Vertical => {
767                let h = match self.wrap() {
768                    TextWrap::NoWrap => implicit_size(None, TextWrap::NoWrap).height,
769                    TextWrap::WordWrap => {
770                        implicit_size(Some(self.width()), TextWrap::WordWrap).height
771                    }
772                    TextWrap::CharWrap => {
773                        implicit_size(Some(self.width()), TextWrap::CharWrap).height
774                    }
775                }
776                .ceil();
777                LayoutInfo { min: h, preferred: h, ..LayoutInfo::default() }
778            }
779        }
780    }
781
782    fn input_event_filter_before_children(
783        self: Pin<&Self>,
784        _: &MouseEvent,
785        _window_adapter: &Rc<dyn WindowAdapter>,
786        _self_rc: &ItemRc,
787    ) -> InputEventFilterResult {
788        InputEventFilterResult::ForwardEvent
789    }
790
791    fn input_event(
792        self: Pin<&Self>,
793        event: &MouseEvent,
794        window_adapter: &Rc<dyn WindowAdapter>,
795        self_rc: &ItemRc,
796    ) -> InputEventResult {
797        if !self.enabled() {
798            return InputEventResult::EventIgnored;
799        }
800        match event {
801            MouseEvent::Pressed { position, button: PointerEventButton::Left, click_count } => {
802                let clicked_offset =
803                    self.byte_offset_for_position(*position, window_adapter, self_rc) as i32;
804                self.as_ref().pressed.set((click_count % 3) + 1);
805
806                if !window_adapter.window().0.modifiers.get().shift() {
807                    self.as_ref().anchor_position_byte_offset.set(clicked_offset);
808                }
809
810                #[cfg(not(target_os = "android"))]
811                self.ensure_focus_and_ime(window_adapter, self_rc);
812
813                match click_count % 3 {
814                    0 => self.set_cursor_position(
815                        clicked_offset,
816                        true,
817                        TextChangeNotify::TriggerCallbacks,
818                        window_adapter,
819                        self_rc,
820                    ),
821                    1 => self.select_word(window_adapter, self_rc),
822                    2 => self.select_paragraph(window_adapter, self_rc),
823                    _ => unreachable!(),
824                };
825
826                return InputEventResult::GrabMouse;
827            }
828            MouseEvent::Pressed { button: PointerEventButton::Middle, .. } => {
829                #[cfg(not(target_os = "android"))]
830                self.ensure_focus_and_ime(window_adapter, self_rc);
831            }
832            MouseEvent::Released { button: PointerEventButton::Left, .. } => {
833                self.as_ref().pressed.set(0);
834                self.copy_clipboard(window_adapter, Clipboard::SelectionClipboard);
835                #[cfg(target_os = "android")]
836                self.ensure_focus_and_ime(window_adapter, self_rc);
837            }
838            MouseEvent::Released { position, button: PointerEventButton::Middle, .. } => {
839                let clicked_offset =
840                    self.byte_offset_for_position(*position, window_adapter, self_rc) as i32;
841                self.as_ref().anchor_position_byte_offset.set(clicked_offset);
842                self.set_cursor_position(
843                    clicked_offset,
844                    true,
845                    TextChangeNotify::TriggerCallbacks,
847                    window_adapter,
848                    self_rc,
849                );
850                self.paste_clipboard(window_adapter, self_rc, Clipboard::SelectionClipboard);
851            }
852            MouseEvent::Exit => {
853                if let Some(x) = window_adapter.internal(crate::InternalToken) {
854                    x.set_mouse_cursor(super::MouseCursor::Default);
855                }
856                self.as_ref().pressed.set(0)
857            }
858            MouseEvent::Moved { position } => {
859                if let Some(x) = window_adapter.internal(crate::InternalToken) {
860                    x.set_mouse_cursor(super::MouseCursor::Text);
861                }
862                let pressed = self.as_ref().pressed.get();
863                if pressed > 0 {
864                    let clicked_offset =
865                        self.byte_offset_for_position(*position, window_adapter, self_rc) as i32;
866                    self.set_cursor_position(
867                        clicked_offset,
868                        true,
869                        if (pressed - 1) % 3 == 0 {
870                            TextChangeNotify::TriggerCallbacks
871                        } else {
872                            TextChangeNotify::SkipCallbacks
873                        },
874                        window_adapter,
875                        self_rc,
876                    );
877                    match (pressed - 1) % 3 {
878                        0 => (),
879                        1 => self.select_word(window_adapter, self_rc),
880                        2 => self.select_paragraph(window_adapter, self_rc),
881                        _ => unreachable!(),
882                    }
883                    return InputEventResult::GrabMouse;
884                }
885            }
886            _ => return InputEventResult::EventIgnored,
887        }
888        InputEventResult::EventAccepted
889    }
890
891    fn capture_key_event(
892        self: Pin<&Self>,
893        _: &KeyEvent,
894        _window_adapter: &Rc<dyn WindowAdapter>,
895        _self_rc: &ItemRc,
896    ) -> KeyEventResult {
897        KeyEventResult::EventIgnored
898    }
899
900    fn key_event(
901        self: Pin<&Self>,
902        event: &KeyEvent,
903        window_adapter: &Rc<dyn WindowAdapter>,
904        self_rc: &ItemRc,
905    ) -> KeyEventResult {
906        if !self.enabled() {
907            return KeyEventResult::EventIgnored;
908        }
909        match event.event_type {
910            KeyEventType::KeyPressed => {
911                if Self::FIELD_OFFSETS.key_pressed.apply_pin(self).call(&(event.clone(),))
913                    == EventResult::Accept
914                {
915                    return KeyEventResult::EventAccepted;
916                }
917
918                match event.text_shortcut() {
919                    Some(text_shortcut) if !self.read_only() => match text_shortcut {
920                        TextShortcut::Move(direction) => {
921                            TextInput::move_cursor(
922                                self,
923                                direction,
924                                event.modifiers.into(),
925                                TextChangeNotify::TriggerCallbacks,
926                                window_adapter,
927                                self_rc,
928                            );
929                            return KeyEventResult::EventAccepted;
930                        }
931                        TextShortcut::DeleteForward => {
932                            TextInput::select_and_delete(
933                                self,
934                                TextCursorDirection::Forward,
935                                window_adapter,
936                                self_rc,
937                            );
938                            return KeyEventResult::EventAccepted;
939                        }
940                        TextShortcut::DeleteBackward => {
941                            TextInput::select_and_delete(
943                                self,
944                                TextCursorDirection::PreviousCharacter,
945                                window_adapter,
946                                self_rc,
947                            );
948                            return KeyEventResult::EventAccepted;
949                        }
950                        TextShortcut::DeleteWordForward => {
951                            TextInput::select_and_delete(
952                                self,
953                                TextCursorDirection::ForwardByWord,
954                                window_adapter,
955                                self_rc,
956                            );
957                            return KeyEventResult::EventAccepted;
958                        }
959                        TextShortcut::DeleteWordBackward => {
960                            TextInput::select_and_delete(
961                                self,
962                                TextCursorDirection::BackwardByWord,
963                                window_adapter,
964                                self_rc,
965                            );
966                            return KeyEventResult::EventAccepted;
967                        }
968                        TextShortcut::DeleteToStartOfLine => {
969                            TextInput::select_and_delete(
970                                self,
971                                TextCursorDirection::StartOfLine,
972                                window_adapter,
973                                self_rc,
974                            );
975                            return KeyEventResult::EventAccepted;
976                        }
977                    },
978                    Some(_) => {
979                        return KeyEventResult::EventIgnored;
980                    }
981                    None => (),
982                };
983
984                if let Some(keycode) = event.text.chars().next() {
985                    if keycode == key_codes::Return && !self.read_only() && self.single_line() {
986                        Self::FIELD_OFFSETS.accepted.apply_pin(self).call(&());
987                        return KeyEventResult::EventAccepted;
988                    }
989                }
990
991                if event.text.is_empty()
993                    || event.text.as_str().chars().any(|ch| {
994                        ('\u{f700}'..='\u{f7ff}').contains(&ch) || (ch.is_control() && ch != '\n')
996                    })
997                {
998                    return KeyEventResult::EventIgnored;
999                }
1000
1001                if let Some(shortcut) = event.shortcut() {
1002                    match shortcut {
1003                        StandardShortcut::SelectAll => {
1004                            self.select_all(window_adapter, self_rc);
1005                            return KeyEventResult::EventAccepted;
1006                        }
1007                        StandardShortcut::Copy => {
1008                            self.copy(window_adapter, self_rc);
1009                            return KeyEventResult::EventAccepted;
1010                        }
1011                        StandardShortcut::Paste if !self.read_only() => {
1012                            self.paste(window_adapter, self_rc);
1013                            return KeyEventResult::EventAccepted;
1014                        }
1015                        StandardShortcut::Cut if !self.read_only() => {
1016                            self.cut(window_adapter, self_rc);
1017                            return KeyEventResult::EventAccepted;
1018                        }
1019                        StandardShortcut::Paste | StandardShortcut::Cut => {
1020                            return KeyEventResult::EventIgnored;
1021                        }
1022                        StandardShortcut::Undo if !self.read_only() => {
1023                            self.undo(window_adapter, self_rc);
1024                            return KeyEventResult::EventAccepted;
1025                        }
1026                        StandardShortcut::Redo if !self.read_only() => {
1027                            self.redo(window_adapter, self_rc);
1028                            return KeyEventResult::EventAccepted;
1029                        }
1030                        _ => (),
1031                    }
1032                }
1033
1034                if self.read_only() || event.modifiers.control {
1035                    return KeyEventResult::EventIgnored;
1036                }
1037
1038                let (real_cursor, real_anchor) = {
1040                    let text = self.text();
1041                    (self.cursor_position(&text), self.anchor_position(&text))
1042                };
1043
1044                if !self.accept_text_input(event.text.as_str()) {
1045                    return KeyEventResult::EventIgnored;
1046                }
1047
1048                self.delete_selection(window_adapter, self_rc, TextChangeNotify::SkipCallbacks);
1049
1050                let mut text: String = self.text().into();
1051
1052                let insert_pos = self.selection_anchor_and_cursor().1;
1054                text.insert_str(insert_pos, &event.text);
1055
1056                self.add_undo_item(UndoItem {
1057                    pos: insert_pos,
1058                    text: event.text.clone(),
1059                    cursor: real_cursor,
1060                    anchor: real_anchor,
1061                    kind: UndoItemKind::TextInsert,
1062                });
1063
1064                self.as_ref().text.set(text.into());
1065                let new_cursor_pos = (insert_pos + event.text.len()) as i32;
1066                self.as_ref().anchor_position_byte_offset.set(new_cursor_pos);
1067                self.set_cursor_position(
1068                    new_cursor_pos,
1069                    true,
1070                    TextChangeNotify::TriggerCallbacks,
1071                    window_adapter,
1072                    self_rc,
1073                );
1074
1075                self.as_ref().show_cursor(window_adapter);
1078
1079                Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
1080
1081                KeyEventResult::EventAccepted
1082            }
1083            KeyEventType::KeyReleased => {
1084                match Self::FIELD_OFFSETS.key_released.apply_pin(self).call(&(event.clone(),)) {
1085                    EventResult::Accept => KeyEventResult::EventAccepted,
1086                    EventResult::Reject => KeyEventResult::EventIgnored,
1087                }
1088            }
1089            KeyEventType::UpdateComposition | KeyEventType::CommitComposition => {
1090                if !self.accept_text_input(&event.text) {
1091                    return KeyEventResult::EventIgnored;
1092                }
1093
1094                let cursor = self.cursor_position(&self.text()) as i32;
1095                self.preedit_text.set(event.preedit_text.clone());
1096                self.preedit_selection.set(event.preedit_selection.clone().into());
1097
1098                if let Some(r) = &event.replacement_range {
1099                    self.anchor_position_byte_offset.set(cursor.saturating_add(r.start));
1101                    self.cursor_position_byte_offset.set(cursor.saturating_add(r.end));
1102                    if event.text.is_empty() {
1103                        self.delete_selection(
1104                            window_adapter,
1105                            self_rc,
1106                            if event.cursor_position.is_none() {
1107                                TextChangeNotify::TriggerCallbacks
1108                            } else {
1109                                TextChangeNotify::SkipCallbacks
1111                            },
1112                        );
1113                    }
1114                }
1115                self.insert(&event.text, window_adapter, self_rc);
1116                if let Some(cursor) = event.cursor_position {
1117                    self.anchor_position_byte_offset.set(event.anchor_position.unwrap_or(cursor));
1118                    self.set_cursor_position(
1119                        cursor,
1120                        true,
1121                        TextChangeNotify::TriggerCallbacks,
1122                        window_adapter,
1123                        self_rc,
1124                    );
1125                }
1126                KeyEventResult::EventAccepted
1127            }
1128        }
1129    }
1130
1131    fn focus_event(
1132        self: Pin<&Self>,
1133        event: &FocusEvent,
1134        window_adapter: &Rc<dyn WindowAdapter>,
1135        self_rc: &ItemRc,
1136    ) -> FocusEventResult {
1137        match event {
1138            FocusEvent::FocusIn(_reason) => {
1139                if !self.enabled() {
1140                    return FocusEventResult::FocusIgnored;
1141                }
1142                self.has_focus.set(true);
1143                self.show_cursor(window_adapter);
1144                WindowInner::from_pub(window_adapter.window()).set_text_input_focused(true);
1145                if !self.read_only() {
1147                    if let Some(w) = window_adapter.internal(crate::InternalToken) {
1148                        w.input_method_request(InputMethodRequest::Enable(
1149                            self.ime_properties(window_adapter, self_rc),
1150                        ));
1151                    }
1152
1153                    #[cfg(not(target_vendor = "apple"))]
1154                    if *_reason == FocusReason::TabNavigation {
1155                        self.select_all(window_adapter, self_rc);
1156                    }
1157                }
1158            }
1159            FocusEvent::FocusOut(reason) => {
1160                self.has_focus.set(false);
1161                self.hide_cursor();
1162                if !matches!(reason, FocusReason::WindowActivation | FocusReason::PopupActivation) {
1163                    self.as_ref()
1164                        .anchor_position_byte_offset
1165                        .set(self.as_ref().cursor_position_byte_offset());
1166                }
1167                WindowInner::from_pub(window_adapter.window()).set_text_input_focused(false);
1168                if !self.read_only() {
1169                    if let Some(window_adapter) = window_adapter.internal(crate::InternalToken) {
1170                        window_adapter.input_method_request(InputMethodRequest::Disable);
1171                    }
1172                    #[cfg(target_os = "android")]
1174                    {
1175                        let preedit_text = self.preedit_text();
1176                        if !preedit_text.is_empty() {
1177                            let mut text = String::from(self.text());
1178                            let cursor_position = self.cursor_position(&text);
1179                            text.insert_str(cursor_position, &preedit_text);
1180                            self.text.set(text.into());
1181                            let new_pos = (cursor_position + preedit_text.len()) as i32;
1182                            self.anchor_position_byte_offset.set(new_pos);
1183                            self.set_cursor_position(
1184                                new_pos,
1185                                false,
1186                                TextChangeNotify::TriggerCallbacks,
1187                                window_adapter,
1188                                self_rc,
1189                            );
1190                            Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
1191                        }
1192                    }
1193                    self.preedit_text.set(Default::default());
1194                }
1195            }
1196        }
1197        FocusEventResult::FocusAccepted
1198    }
1199
1200    fn render(
1201        self: Pin<&Self>,
1202        backend: &mut &mut dyn ItemRenderer,
1203        self_rc: &ItemRc,
1204        size: LogicalSize,
1205    ) -> RenderingResult {
1206        crate::properties::evaluate_no_tracking(|| {
1207            if self.has_focus() && self.text() != *backend.window().last_ime_text.borrow() {
1208                let window_adapter = &backend.window().window_adapter();
1209                if let Some(w) = window_adapter.internal(crate::InternalToken) {
1210                    w.input_method_request(InputMethodRequest::Update(
1211                        self.ime_properties(window_adapter, self_rc),
1212                    ));
1213                }
1214            }
1215        });
1216        (*backend).draw_text_input(self, self_rc, size);
1217        RenderingResult::ContinueRenderingChildren
1218    }
1219
1220    fn bounding_rect(
1221        self: core::pin::Pin<&Self>,
1222        _window_adapter: &Rc<dyn WindowAdapter>,
1223        _self_rc: &ItemRc,
1224        geometry: LogicalRect,
1225    ) -> LogicalRect {
1226        geometry
1227    }
1228
1229    fn clips_children(self: core::pin::Pin<&Self>) -> bool {
1230        false
1231    }
1232}
1233
1234impl ItemConsts for TextInput {
1235    const cached_rendering_data_offset: const_field_offset::FieldOffset<
1236        TextInput,
1237        CachedRenderingData,
1238    > = TextInput::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
1239}
1240
1241pub enum TextCursorDirection {
1242    Forward,
1243    Backward,
1244    ForwardByWord,
1245    BackwardByWord,
1246    NextLine,
1247    PreviousLine,
1248    PreviousCharacter,
1250    StartOfLine,
1251    EndOfLine,
1252    StartOfParagraph,
1254    EndOfParagraph,
1255    StartOfText,
1256    EndOfText,
1257    PageUp,
1258    PageDown,
1259}
1260
1261impl core::convert::TryFrom<char> for TextCursorDirection {
1262    type Error = ();
1263
1264    fn try_from(value: char) -> Result<Self, Self::Error> {
1265        Ok(match value {
1266            key_codes::LeftArrow => Self::Backward,
1267            key_codes::RightArrow => Self::Forward,
1268            key_codes::UpArrow => Self::PreviousLine,
1269            key_codes::DownArrow => Self::NextLine,
1270            key_codes::PageUp => Self::PageUp,
1271            key_codes::PageDown => Self::PageDown,
1272            #[cfg(not(target_os = "macos"))]
1274            key_codes::Home => Self::StartOfLine,
1275            #[cfg(not(target_os = "macos"))]
1276            key_codes::End => Self::EndOfLine,
1277            _ => return Err(()),
1278        })
1279    }
1280}
1281
1282#[derive(PartialEq)]
1283enum AnchorMode {
1284    KeepAnchor,
1285    MoveAnchor,
1286}
1287
1288impl From<KeyboardModifiers> for AnchorMode {
1289    fn from(modifiers: KeyboardModifiers) -> Self {
1290        if modifiers.shift {
1291            Self::KeepAnchor
1292        } else {
1293            Self::MoveAnchor
1294        }
1295    }
1296}
1297
1298#[derive(Copy, Clone, PartialEq, Eq)]
1301pub enum TextChangeNotify {
1302    TriggerCallbacks,
1304    SkipCallbacks,
1306}
1307
1308fn safe_byte_offset(unsafe_byte_offset: i32, text: &str) -> usize {
1309    if unsafe_byte_offset <= 0 {
1310        return 0;
1311    }
1312    let byte_offset_candidate = unsafe_byte_offset as usize;
1313
1314    if byte_offset_candidate >= text.len() {
1315        return text.len();
1316    }
1317
1318    if text.is_char_boundary(byte_offset_candidate) {
1319        return byte_offset_candidate;
1320    }
1321
1322    text.char_indices()
1324        .find_map(|(offset, _)| if offset >= byte_offset_candidate { Some(offset) } else { None })
1325        .unwrap_or(text.len())
1326}
1327
1328#[derive(Debug)]
1332pub struct TextInputVisualRepresentation {
1333    pub text: String,
1335    pub preedit_range: core::ops::Range<usize>,
1339    pub selection_range: core::ops::Range<usize>,
1341    pub cursor_position: Option<usize>,
1343    pub text_color: Brush,
1345    pub cursor_color: Color,
1347    text_without_password: Option<String>,
1348    password_character: char,
1349}
1350
1351impl TextInputVisualRepresentation {
1352    fn apply_password_character_substitution(
1356        &mut self,
1357        text_input: Pin<&TextInput>,
1358        password_character_fn: Option<fn() -> char>,
1359    ) {
1360        if !matches!(text_input.input_type(), InputType::Password) {
1361            return;
1362        }
1363
1364        let password_character = password_character_fn.map_or('●', |f| f());
1365
1366        let text = &mut self.text;
1367        let fixup_range = |r: &mut core::ops::Range<usize>| {
1368            if !core::ops::Range::is_empty(r) {
1369                r.start = text[..r.start].chars().count() * password_character.len_utf8();
1370                r.end = text[..r.end].chars().count() * password_character.len_utf8();
1371            }
1372        };
1373        fixup_range(&mut self.preedit_range);
1374        fixup_range(&mut self.selection_range);
1375        if let Some(cursor_pos) = self.cursor_position.as_mut() {
1376            *cursor_pos = text[..*cursor_pos].chars().count() * password_character.len_utf8();
1377        }
1378        self.text_without_password = Some(core::mem::replace(
1379            text,
1380            core::iter::repeat(password_character).take(text.chars().count()).collect(),
1381        ));
1382        self.password_character = password_character;
1383    }
1384
1385    pub fn map_byte_offset_from_byte_offset_in_visual_text(&self, byte_offset: usize) -> usize {
1388        if let Some(text_without_password) = self.text_without_password.as_ref() {
1389            text_without_password
1390                .char_indices()
1391                .nth(byte_offset / self.password_character.len_utf8())
1392                .map_or(text_without_password.len(), |(r, _)| r)
1393        } else {
1394            byte_offset
1395        }
1396    }
1397}
1398
1399impl TextInput {
1400    fn show_cursor(&self, window_adapter: &Rc<dyn WindowAdapter>) {
1401        WindowInner::from_pub(window_adapter.window())
1402            .set_cursor_blink_binding(&self.cursor_visible);
1403    }
1404
1405    fn hide_cursor(&self) {
1406        self.cursor_visible.set(false);
1407    }
1408
1409    fn move_cursor(
1411        self: Pin<&Self>,
1412        direction: TextCursorDirection,
1413        anchor_mode: AnchorMode,
1414        trigger_callbacks: TextChangeNotify,
1415        window_adapter: &Rc<dyn WindowAdapter>,
1416        self_rc: &ItemRc,
1417    ) -> bool {
1418        let text = self.text();
1419        if text.is_empty() {
1420            return false;
1421        }
1422
1423        let (anchor, cursor) = self.selection_anchor_and_cursor();
1424        let last_cursor_pos = self.cursor_position(&text);
1425
1426        let mut grapheme_cursor =
1427            unicode_segmentation::GraphemeCursor::new(last_cursor_pos, text.len(), true);
1428
1429        let font_height = window_adapter
1430            .renderer()
1431            .text_size(
1432                self.font_request(self_rc),
1433                " ",
1434                None,
1435                ScaleFactor::new(window_adapter.window().scale_factor()),
1436                TextWrap::NoWrap,
1437            )
1438            .height;
1439
1440        let mut reset_preferred_x_pos = true;
1441
1442        let new_cursor_pos = match direction {
1443            TextCursorDirection::Forward => {
1444                if anchor == cursor || anchor_mode == AnchorMode::KeepAnchor {
1445                    grapheme_cursor
1446                        .next_boundary(&text, 0)
1447                        .ok()
1448                        .flatten()
1449                        .unwrap_or_else(|| text.len())
1450                } else {
1451                    cursor
1452                }
1453            }
1454            TextCursorDirection::Backward => {
1455                if anchor == cursor || anchor_mode == AnchorMode::KeepAnchor {
1456                    grapheme_cursor.prev_boundary(&text, 0).ok().flatten().unwrap_or(0)
1457                } else {
1458                    anchor
1459                }
1460            }
1461            TextCursorDirection::NextLine => {
1462                reset_preferred_x_pos = false;
1463
1464                let cursor_rect =
1465                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1466                let mut cursor_xy_pos = cursor_rect.center();
1467
1468                cursor_xy_pos.y += font_height;
1469                cursor_xy_pos.x = self.preferred_x_pos.get();
1470                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1471            }
1472            TextCursorDirection::PreviousLine => {
1473                reset_preferred_x_pos = false;
1474
1475                let cursor_rect =
1476                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1477                let mut cursor_xy_pos = cursor_rect.center();
1478
1479                cursor_xy_pos.y -= font_height;
1480                cursor_xy_pos.x = self.preferred_x_pos.get();
1481                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1482            }
1483            TextCursorDirection::PreviousCharacter => {
1484                let mut i = last_cursor_pos;
1485                loop {
1486                    i = i.checked_sub(1).unwrap_or_default();
1487                    if text.is_char_boundary(i) {
1488                        break i;
1489                    }
1490                }
1491            }
1492            TextCursorDirection::ForwardByWord => next_word_boundary(&text, last_cursor_pos + 1),
1494            TextCursorDirection::BackwardByWord => {
1495                prev_word_boundary(&text, last_cursor_pos.saturating_sub(1))
1496            }
1497            TextCursorDirection::StartOfLine => {
1498                let cursor_rect =
1499                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1500                let mut cursor_xy_pos = cursor_rect.center();
1501
1502                cursor_xy_pos.x = 0 as Coord;
1503                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1504            }
1505            TextCursorDirection::EndOfLine => {
1506                let cursor_rect =
1507                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1508                let mut cursor_xy_pos = cursor_rect.center();
1509
1510                cursor_xy_pos.x = Coord::MAX;
1511                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1512            }
1513            TextCursorDirection::StartOfParagraph => {
1514                prev_paragraph_boundary(&text, last_cursor_pos.saturating_sub(1))
1515            }
1516            TextCursorDirection::EndOfParagraph => {
1517                next_paragraph_boundary(&text, last_cursor_pos + 1)
1518            }
1519            TextCursorDirection::StartOfText => 0,
1520            TextCursorDirection::EndOfText => text.len(),
1521            TextCursorDirection::PageUp => {
1522                let offset = self.page_height().get() - font_height;
1523                if offset <= 0 as Coord {
1524                    return false;
1525                }
1526                reset_preferred_x_pos = false;
1527                let cursor_rect =
1528                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1529                let mut cursor_xy_pos = cursor_rect.center();
1530                cursor_xy_pos.y -= offset;
1531                cursor_xy_pos.x = self.preferred_x_pos.get();
1532                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1533            }
1534            TextCursorDirection::PageDown => {
1535                let offset = self.page_height().get() - font_height;
1536                if offset <= 0 as Coord {
1537                    return false;
1538                }
1539                reset_preferred_x_pos = false;
1540                let cursor_rect =
1541                    self.cursor_rect_for_byte_offset(last_cursor_pos, window_adapter, self_rc);
1542                let mut cursor_xy_pos = cursor_rect.center();
1543                cursor_xy_pos.y += offset;
1544                cursor_xy_pos.x = self.preferred_x_pos.get();
1545                self.byte_offset_for_position(cursor_xy_pos, window_adapter, self_rc)
1546            }
1547        };
1548
1549        match anchor_mode {
1550            AnchorMode::KeepAnchor => {}
1551            AnchorMode::MoveAnchor => {
1552                self.as_ref().anchor_position_byte_offset.set(new_cursor_pos as i32);
1553            }
1554        }
1555        self.set_cursor_position(
1556            new_cursor_pos as i32,
1557            reset_preferred_x_pos,
1558            trigger_callbacks,
1559            window_adapter,
1560            self_rc,
1561        );
1562
1563        self.as_ref().show_cursor(window_adapter);
1566
1567        new_cursor_pos != last_cursor_pos
1568    }
1569
1570    pub fn set_cursor_position(
1571        self: Pin<&Self>,
1572        new_position: i32,
1573        reset_preferred_x_pos: bool,
1574        trigger_callbacks: TextChangeNotify,
1575        window_adapter: &Rc<dyn WindowAdapter>,
1576        self_rc: &ItemRc,
1577    ) {
1578        self.cursor_position_byte_offset.set(new_position);
1579        if new_position >= 0 {
1580            let pos = self
1581                .cursor_rect_for_byte_offset(new_position as usize, window_adapter, self_rc)
1582                .origin;
1583            if reset_preferred_x_pos {
1584                self.preferred_x_pos.set(pos.x);
1585            }
1586            if trigger_callbacks == TextChangeNotify::TriggerCallbacks {
1587                Self::FIELD_OFFSETS
1588                    .cursor_position_changed
1589                    .apply_pin(self)
1590                    .call(&(crate::api::LogicalPosition::from_euclid(pos),));
1591                self.update_ime(window_adapter, self_rc);
1592            }
1593        }
1594    }
1595
1596    fn update_ime(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
1597        if self.read_only() || !self.has_focus() {
1598            return;
1599        }
1600        if let Some(w) = window_adapter.internal(crate::InternalToken) {
1601            w.input_method_request(InputMethodRequest::Update(
1602                self.ime_properties(window_adapter, self_rc),
1603            ));
1604        }
1605    }
1606
1607    fn select_and_delete(
1608        self: Pin<&Self>,
1609        step: TextCursorDirection,
1610        window_adapter: &Rc<dyn WindowAdapter>,
1611        self_rc: &ItemRc,
1612    ) {
1613        if !self.has_selection() {
1614            self.move_cursor(
1615                step,
1616                AnchorMode::KeepAnchor,
1617                TextChangeNotify::SkipCallbacks,
1618                window_adapter,
1619                self_rc,
1620            );
1621        }
1622        self.delete_selection(window_adapter, self_rc, TextChangeNotify::TriggerCallbacks);
1623    }
1624
1625    pub fn delete_selection(
1626        self: Pin<&Self>,
1627        window_adapter: &Rc<dyn WindowAdapter>,
1628        self_rc: &ItemRc,
1629        trigger_callbacks: TextChangeNotify,
1630    ) {
1631        let text: String = self.text().into();
1632        if text.is_empty() {
1633            return;
1634        }
1635
1636        let (anchor, cursor) = self.selection_anchor_and_cursor();
1637        if anchor == cursor {
1638            return;
1639        }
1640
1641        let removed_text: SharedString = text[anchor..cursor].into();
1642        let (real_cursor, real_anchor) = {
1644            let text = self.text();
1645            (self.cursor_position(&text), self.anchor_position(&text))
1646        };
1647
1648        let text = [text.split_at(anchor).0, text.split_at(cursor).1].concat();
1649        self.text.set(text.into());
1650        self.anchor_position_byte_offset.set(anchor as i32);
1651
1652        self.add_undo_item(UndoItem {
1653            pos: anchor,
1654            text: removed_text,
1655            cursor: real_cursor,
1656            anchor: real_anchor,
1657            kind: UndoItemKind::TextRemove,
1658        });
1659
1660        if trigger_callbacks == TextChangeNotify::TriggerCallbacks {
1661            self.set_cursor_position(
1662                anchor as i32,
1663                true,
1664                trigger_callbacks,
1665                window_adapter,
1666                self_rc,
1667            );
1668            Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
1669        } else {
1670            self.cursor_position_byte_offset.set(anchor as i32);
1671        }
1672    }
1673
1674    pub fn anchor_position(self: Pin<&Self>, text: &str) -> usize {
1675        safe_byte_offset(self.anchor_position_byte_offset(), text)
1676    }
1677
1678    pub fn cursor_position(self: Pin<&Self>, text: &str) -> usize {
1679        safe_byte_offset(self.cursor_position_byte_offset(), text)
1680    }
1681
1682    fn ime_properties(
1683        self: Pin<&Self>,
1684        window_adapter: &Rc<dyn WindowAdapter>,
1685        self_rc: &ItemRc,
1686    ) -> InputMethodProperties {
1687        let text = self.text();
1688        WindowInner::from_pub(window_adapter.window()).last_ime_text.replace(text.clone());
1689        let cursor_position = self.cursor_position(&text);
1690        let anchor_position = self.anchor_position(&text);
1691        let cursor_relative =
1692            self.cursor_rect_for_byte_offset(cursor_position, window_adapter, self_rc);
1693        let geometry = self_rc.geometry();
1694        let origin = self_rc.map_to_window(geometry.origin);
1695        let origin_vector = origin.to_vector();
1696        let cursor_rect_origin =
1697            crate::api::LogicalPosition::from_euclid(cursor_relative.origin + origin_vector);
1698        let cursor_rect_size = crate::api::LogicalSize::from_euclid(cursor_relative.size);
1699        let anchor_point = crate::api::LogicalPosition::from_euclid(
1700            self.cursor_rect_for_byte_offset(anchor_position, window_adapter, self_rc).origin
1701                + origin_vector
1702                + cursor_relative.size,
1703        );
1704        let maybe_parent =
1705            self_rc.parent_item(crate::item_tree::ParentItemTraversalMode::StopAtPopups);
1706        let clip_rect = maybe_parent.map(|parent| {
1707            let geom = parent.geometry();
1708            LogicalRect::new(parent.map_to_window(geom.origin), geom.size)
1709        });
1710
1711        InputMethodProperties {
1712            text,
1713            cursor_position,
1714            anchor_position: (cursor_position != anchor_position).then_some(anchor_position),
1715            preedit_text: self.preedit_text(),
1716            preedit_offset: cursor_position,
1717            cursor_rect_origin,
1718            cursor_rect_size,
1719            anchor_point,
1720            input_type: self.input_type(),
1721            clip_rect,
1722        }
1723    }
1724
1725    pub fn selection_anchor_and_cursor(self: Pin<&Self>) -> (usize, usize) {
1728        let text = self.text();
1729        let cursor_pos = self.cursor_position(&text);
1730        let anchor_pos = self.anchor_position(&text);
1731
1732        if anchor_pos > cursor_pos {
1733            (cursor_pos as _, anchor_pos as _)
1734        } else {
1735            (anchor_pos as _, cursor_pos as _)
1736        }
1737    }
1738
1739    pub fn has_selection(self: Pin<&Self>) -> bool {
1740        let (anchor_pos, cursor_pos) = self.selection_anchor_and_cursor();
1741        anchor_pos != cursor_pos
1742    }
1743
1744    fn insert(
1745        self: Pin<&Self>,
1746        text_to_insert: &str,
1747        window_adapter: &Rc<dyn WindowAdapter>,
1748        self_rc: &ItemRc,
1749    ) {
1750        if text_to_insert.is_empty() {
1751            return;
1752        }
1753
1754        let (real_cursor, real_anchor) = {
1755            let text = self.text();
1756            (self.cursor_position(&text), self.anchor_position(&text))
1757        };
1758
1759        self.delete_selection(window_adapter, self_rc, TextChangeNotify::SkipCallbacks);
1760        let mut text: String = self.text().into();
1761        let cursor_pos = self.selection_anchor_and_cursor().1;
1762        let mut inserted_text: SharedString = text_to_insert.into();
1763        if text_to_insert.contains('\n') && self.single_line() {
1764            inserted_text = text_to_insert.replace('\n', " ").into();
1765            text.insert_str(cursor_pos, &inserted_text);
1766        } else {
1767            text.insert_str(cursor_pos, text_to_insert);
1768        }
1769
1770        self.add_undo_item(UndoItem {
1771            pos: cursor_pos,
1772            text: inserted_text,
1773            cursor: real_cursor,
1774            anchor: real_anchor,
1775            kind: UndoItemKind::TextInsert,
1776        });
1777
1778        let cursor_pos = cursor_pos + text_to_insert.len();
1779        self.text.set(text.into());
1780        self.anchor_position_byte_offset.set(cursor_pos as i32);
1781        self.set_cursor_position(
1782            cursor_pos as i32,
1783            true,
1784            TextChangeNotify::TriggerCallbacks,
1785            window_adapter,
1786            self_rc,
1787        );
1788        Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
1789    }
1790
1791    pub fn cut(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
1792        self.copy(window_adapter, self_rc);
1793        self.delete_selection(window_adapter, self_rc, TextChangeNotify::TriggerCallbacks);
1794    }
1795
1796    pub fn set_selection_offsets(
1797        self: Pin<&Self>,
1798        window_adapter: &Rc<dyn WindowAdapter>,
1799        self_rc: &ItemRc,
1800        start: i32,
1801        end: i32,
1802    ) {
1803        let text = self.text();
1804        let safe_start = safe_byte_offset(start, &text);
1805        let safe_end = safe_byte_offset(end, &text);
1806
1807        self.as_ref().anchor_position_byte_offset.set(safe_start as i32);
1808        self.set_cursor_position(
1809            safe_end as i32,
1810            true,
1811            TextChangeNotify::TriggerCallbacks,
1812            window_adapter,
1813            self_rc,
1814        );
1815    }
1816
1817    pub fn select_all(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
1818        self.move_cursor(
1819            TextCursorDirection::StartOfText,
1820            AnchorMode::MoveAnchor,
1821            TextChangeNotify::SkipCallbacks,
1822            window_adapter,
1823            self_rc,
1824        );
1825        self.move_cursor(
1826            TextCursorDirection::EndOfText,
1827            AnchorMode::KeepAnchor,
1828            TextChangeNotify::TriggerCallbacks,
1829            window_adapter,
1830            self_rc,
1831        );
1832    }
1833
1834    pub fn clear_selection(self: Pin<&Self>, _: &Rc<dyn WindowAdapter>, _: &ItemRc) {
1835        self.as_ref().anchor_position_byte_offset.set(self.as_ref().cursor_position_byte_offset());
1836    }
1837
1838    pub fn select_word(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
1839        let text = self.text();
1840        let anchor = self.anchor_position(&text);
1841        let cursor = self.cursor_position(&text);
1842        let (new_a, new_c) = if anchor <= cursor {
1843            (prev_word_boundary(&text, anchor), next_word_boundary(&text, cursor))
1844        } else {
1845            (next_word_boundary(&text, anchor), prev_word_boundary(&text, cursor))
1846        };
1847        self.as_ref().anchor_position_byte_offset.set(new_a as i32);
1848        self.set_cursor_position(
1849            new_c as i32,
1850            true,
1851            TextChangeNotify::TriggerCallbacks,
1852            window_adapter,
1853            self_rc,
1854        );
1855    }
1856
1857    fn select_paragraph(
1858        self: Pin<&Self>,
1859        window_adapter: &Rc<dyn WindowAdapter>,
1860        self_rc: &ItemRc,
1861    ) {
1862        let text = self.text();
1863        let anchor = self.anchor_position(&text);
1864        let cursor = self.cursor_position(&text);
1865        let (new_a, new_c) = if anchor <= cursor {
1866            (prev_paragraph_boundary(&text, anchor), next_paragraph_boundary(&text, cursor))
1867        } else {
1868            (next_paragraph_boundary(&text, anchor), prev_paragraph_boundary(&text, cursor))
1869        };
1870        self.as_ref().anchor_position_byte_offset.set(new_a as i32);
1871        self.set_cursor_position(
1872            new_c as i32,
1873            true,
1874            TextChangeNotify::TriggerCallbacks,
1875            window_adapter,
1876            self_rc,
1877        );
1878    }
1879
1880    pub fn copy(self: Pin<&Self>, w: &Rc<dyn WindowAdapter>, _: &ItemRc) {
1881        self.copy_clipboard(w, Clipboard::DefaultClipboard);
1882    }
1883
1884    fn copy_clipboard(
1885        self: Pin<&Self>,
1886        window_adapter: &Rc<dyn WindowAdapter>,
1887        clipboard: Clipboard,
1888    ) {
1889        let (anchor, cursor) = self.selection_anchor_and_cursor();
1890        if anchor == cursor {
1891            return;
1892        }
1893        let text = self.text();
1894
1895        WindowInner::from_pub(window_adapter.window())
1896            .ctx
1897            .platform()
1898            .set_clipboard_text(&text[anchor..cursor], clipboard);
1899    }
1900
1901    pub fn paste(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
1902        self.paste_clipboard(window_adapter, self_rc, Clipboard::DefaultClipboard);
1903    }
1904
1905    fn paste_clipboard(
1906        self: Pin<&Self>,
1907        window_adapter: &Rc<dyn WindowAdapter>,
1908        self_rc: &ItemRc,
1909        clipboard: Clipboard,
1910    ) {
1911        if let Some(text) =
1912            WindowInner::from_pub(window_adapter.window()).ctx.platform().clipboard_text(clipboard)
1913        {
1914            self.preedit_text.set(Default::default());
1915            self.insert(&text, window_adapter, self_rc);
1916        }
1917    }
1918
1919    pub fn font_request(self: Pin<&Self>, self_rc: &ItemRc) -> FontRequest {
1920        WindowItem::resolved_font_request(
1921            self_rc,
1922            self.font_family(),
1923            self.font_weight(),
1924            self.font_size(),
1925            self.letter_spacing(),
1926            self.font_italic(),
1927        )
1928    }
1929
1930    pub fn visual_representation(
1934        self: Pin<&Self>,
1935        password_character_fn: Option<fn() -> char>,
1936    ) -> TextInputVisualRepresentation {
1937        let mut text: String = self.text().into();
1938
1939        let preedit_text = self.preedit_text();
1940        let (preedit_range, selection_range, cursor_position) = if !preedit_text.is_empty() {
1941            let cursor_position = self.cursor_position(&text);
1942
1943            text.insert_str(cursor_position, &preedit_text);
1944            let preedit_range = cursor_position..cursor_position + preedit_text.len();
1945
1946            if let Some(preedit_sel) = self.preedit_selection().as_option() {
1947                let preedit_selection = cursor_position + preedit_sel.start as usize
1948                    ..cursor_position + preedit_sel.end as usize;
1949                (preedit_range, preedit_selection, Some(cursor_position + preedit_sel.end as usize))
1950            } else {
1951                let cur = preedit_range.end;
1952                (preedit_range, cur..cur, None)
1953            }
1954        } else {
1955            let preedit_range = Default::default();
1956            let (selection_anchor_pos, selection_cursor_pos) = self.selection_anchor_and_cursor();
1957            let selection_range = selection_anchor_pos..selection_cursor_pos;
1958            let cursor_position = self.cursor_position(&text);
1959            let cursor_visible = self.cursor_visible() && self.enabled() && !self.read_only();
1960            let cursor_position = if cursor_visible && selection_range.is_empty() {
1961                Some(cursor_position)
1962            } else {
1963                None
1964            };
1965            (preedit_range, selection_range, cursor_position)
1966        };
1967
1968        let text_color = self.color();
1969
1970        let cursor_color = if cfg!(any(target_os = "android", target_vendor = "apple")) {
1971            if cursor_position.is_some() {
1972                self.selection_background_color().with_alpha(1.)
1973            } else {
1974                Default::default()
1975            }
1976        } else {
1977            text_color.color()
1978        };
1979
1980        let mut repr = TextInputVisualRepresentation {
1981            text,
1982            preedit_range,
1983            selection_range,
1984            cursor_position,
1985            text_without_password: None,
1986            password_character: Default::default(),
1987            text_color,
1988            cursor_color,
1989        };
1990        repr.apply_password_character_substitution(self, password_character_fn);
1991        repr
1992    }
1993
1994    fn cursor_rect_for_byte_offset(
1995        self: Pin<&Self>,
1996        byte_offset: usize,
1997        window_adapter: &Rc<dyn WindowAdapter>,
1998        self_rc: &ItemRc,
1999    ) -> LogicalRect {
2000        window_adapter.renderer().text_input_cursor_rect_for_byte_offset(
2001            self,
2002            byte_offset,
2003            self.font_request(self_rc),
2004            ScaleFactor::new(window_adapter.window().scale_factor()),
2005        )
2006    }
2007
2008    pub fn byte_offset_for_position(
2009        self: Pin<&Self>,
2010        pos: LogicalPoint,
2011        window_adapter: &Rc<dyn WindowAdapter>,
2012        self_rc: &ItemRc,
2013    ) -> usize {
2014        window_adapter.renderer().text_input_byte_offset_for_position(
2015            self,
2016            pos,
2017            self.font_request(self_rc),
2018            ScaleFactor::new(window_adapter.window().scale_factor()),
2019        )
2020    }
2021
2022    fn ensure_focus_and_ime(
2025        self: Pin<&Self>,
2026        window_adapter: &Rc<dyn WindowAdapter>,
2027        self_rc: &ItemRc,
2028    ) {
2029        if !self.has_focus() {
2030            WindowInner::from_pub(window_adapter.window()).set_focus_item(
2031                self_rc,
2032                true,
2033                FocusReason::PointerClick,
2034            );
2035        } else if !self.read_only() {
2036            if let Some(w) = window_adapter.internal(crate::InternalToken) {
2037                w.input_method_request(InputMethodRequest::Enable(
2038                    self.ime_properties(window_adapter, self_rc),
2039                ));
2040            }
2041        }
2042    }
2043
2044    fn add_undo_item(self: Pin<&Self>, item: UndoItem) {
2045        let mut items = self.undo_items.take();
2046        if let Some(last) = items.make_mut_slice().last_mut() {
2048            match (&item.kind, &last.kind) {
2049                (UndoItemKind::TextInsert, UndoItemKind::TextInsert) => {
2050                    let is_new_line = item.text == "\n";
2051                    let last_is_new_line = last.text == "\n";
2052                    if item.pos == last.pos + last.text.len() && !is_new_line && !last_is_new_line {
2055                        last.text += &item.text;
2056                    } else {
2057                        items.push(item);
2058                    }
2059                }
2060                (UndoItemKind::TextRemove, UndoItemKind::TextRemove) => {
2061                    if item.pos + item.text.len() == last.pos {
2062                        last.pos = item.pos;
2063                        let old_text = last.text.clone();
2064                        last.text = item.text;
2065                        last.text += &old_text;
2066                        } else {
2068                        items.push(item);
2069                    }
2070                }
2071                _ => {
2072                    items.push(item);
2073                }
2074            }
2075        } else {
2076            items.push(item);
2077        }
2078
2079        self.undo_items.set(items);
2080    }
2081
2082    fn undo(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
2083        let mut items = self.undo_items.take();
2084        let Some(last) = items.pop() else {
2085            return;
2086        };
2087
2088        match last.kind {
2089            UndoItemKind::TextInsert => {
2090                let text: String = self.text().into();
2091                let text = [text.split_at(last.pos).0, text.split_at(last.pos + last.text.len()).1]
2092                    .concat();
2093                self.text.set(text.into());
2094
2095                self.anchor_position_byte_offset.set(last.anchor as i32);
2096                self.set_cursor_position(
2097                    last.cursor as i32,
2098                    true,
2099                    TextChangeNotify::TriggerCallbacks,
2100                    window_adapter,
2101                    self_rc,
2102                );
2103            }
2104            UndoItemKind::TextRemove => {
2105                let mut text: String = self.text().into();
2106                text.insert_str(last.pos, &last.text);
2107                self.text.set(text.into());
2108
2109                self.anchor_position_byte_offset.set(last.anchor as i32);
2110                self.set_cursor_position(
2111                    last.cursor as i32,
2112                    true,
2113                    TextChangeNotify::TriggerCallbacks,
2114                    window_adapter,
2115                    self_rc,
2116                );
2117            }
2118        }
2119        self.undo_items.set(items);
2120
2121        let mut redo = self.redo_items.take();
2122        redo.push(last);
2123        self.redo_items.set(redo);
2124        Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
2125    }
2126
2127    fn redo(self: Pin<&Self>, window_adapter: &Rc<dyn WindowAdapter>, self_rc: &ItemRc) {
2128        let mut items = self.redo_items.take();
2129        let Some(last) = items.pop() else {
2130            return;
2131        };
2132
2133        match last.kind {
2134            UndoItemKind::TextInsert => {
2135                let mut text: String = self.text().into();
2136                text.insert_str(last.pos, &last.text);
2137                self.text.set(text.into());
2138
2139                self.anchor_position_byte_offset.set(last.anchor as i32);
2140                self.set_cursor_position(
2141                    last.cursor as i32,
2142                    true,
2143                    TextChangeNotify::TriggerCallbacks,
2144                    window_adapter,
2145                    self_rc,
2146                );
2147            }
2148            UndoItemKind::TextRemove => {
2149                let text: String = self.text().into();
2150                let text = [text.split_at(last.pos).0, text.split_at(last.pos + last.text.len()).1]
2151                    .concat();
2152                self.text.set(text.into());
2153
2154                self.anchor_position_byte_offset.set(last.anchor as i32);
2155                self.set_cursor_position(
2156                    last.cursor as i32,
2157                    true,
2158                    TextChangeNotify::TriggerCallbacks,
2159                    window_adapter,
2160                    self_rc,
2161                );
2162            }
2163        }
2164
2165        self.redo_items.set(items);
2166
2167        let mut undo_items = self.undo_items.take();
2168        undo_items.push(last);
2169        self.undo_items.set(undo_items);
2170        Self::FIELD_OFFSETS.edited.apply_pin(self).call(&());
2171    }
2172
2173    pub fn font_metrics(
2174        self: Pin<&Self>,
2175        window_adapter: &Rc<dyn WindowAdapter>,
2176        self_rc: &ItemRc,
2177    ) -> FontMetrics {
2178        let window_inner = WindowInner::from_pub(window_adapter.window());
2179        let scale_factor = ScaleFactor::new(window_inner.scale_factor());
2180        let font_request = self.font_request(self_rc);
2181        window_adapter.renderer().font_metrics(font_request, scale_factor)
2182    }
2183
2184    fn accept_text_input(self: Pin<&Self>, text_to_insert: &str) -> bool {
2185        let input_type = self.input_type();
2186        if input_type == InputType::Number && !text_to_insert.chars().all(|ch| ch.is_ascii_digit())
2187        {
2188            return false;
2189        } else if input_type == InputType::Decimal {
2190            let (a, c) = self.selection_anchor_and_cursor();
2191            let text = self.text();
2192            let text = [&text[..a], text_to_insert, &text[c..]].concat();
2193            if text.as_str() != "." && text.as_str() != "-" && text.parse::<f64>().is_err() {
2194                return false;
2195            }
2196        }
2197        true
2198    }
2199}
2200
2201fn next_paragraph_boundary(text: &str, last_cursor_pos: usize) -> usize {
2202    text.as_bytes()
2203        .iter()
2204        .enumerate()
2205        .skip(last_cursor_pos)
2206        .find(|(_, &c)| c == b'\n')
2207        .map(|(new_pos, _)| new_pos)
2208        .unwrap_or(text.len())
2209}
2210
2211fn prev_paragraph_boundary(text: &str, last_cursor_pos: usize) -> usize {
2212    text.as_bytes()
2213        .iter()
2214        .enumerate()
2215        .rev()
2216        .skip(text.len() - last_cursor_pos)
2217        .find(|(_, &c)| c == b'\n')
2218        .map(|(new_pos, _)| new_pos + 1)
2219        .unwrap_or(0)
2220}
2221
2222fn prev_word_boundary(text: &str, last_cursor_pos: usize) -> usize {
2223    let mut word_offset = 0;
2224
2225    for (current_word_offset, _) in text.unicode_word_indices() {
2226        if current_word_offset <= last_cursor_pos {
2227            word_offset = current_word_offset;
2228        } else {
2229            break;
2230        }
2231    }
2232
2233    word_offset
2234}
2235
2236fn next_word_boundary(text: &str, last_cursor_pos: usize) -> usize {
2237    text.unicode_word_indices()
2238        .find(|(offset, slice)| *offset + slice.len() >= last_cursor_pos)
2239        .map_or(text.len(), |(offset, slice)| offset + slice.len())
2240}
2241
2242#[cfg(feature = "ffi")]
2243#[unsafe(no_mangle)]
2244pub unsafe extern "C" fn slint_textinput_set_selection_offsets(
2245    text_input: Pin<&TextInput>,
2246    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2247    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2248    self_index: u32,
2249    start: i32,
2250    end: i32,
2251) {
2252    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2253    let self_rc = ItemRc::new(self_component.clone(), self_index);
2254    text_input.set_selection_offsets(window_adapter, &self_rc, start, end);
2255}
2256
2257#[cfg(feature = "ffi")]
2258#[unsafe(no_mangle)]
2259pub unsafe extern "C" fn slint_textinput_select_all(
2260    text_input: Pin<&TextInput>,
2261    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2262    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2263    self_index: u32,
2264) {
2265    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2266    let self_rc = ItemRc::new(self_component.clone(), self_index);
2267    text_input.select_all(window_adapter, &self_rc);
2268}
2269
2270#[cfg(feature = "ffi")]
2271#[unsafe(no_mangle)]
2272pub unsafe extern "C" fn slint_textinput_clear_selection(
2273    text_input: Pin<&TextInput>,
2274    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2275    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2276    self_index: u32,
2277) {
2278    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2279    let self_rc = ItemRc::new(self_component.clone(), self_index);
2280    text_input.clear_selection(window_adapter, &self_rc);
2281}
2282
2283#[cfg(feature = "ffi")]
2284#[unsafe(no_mangle)]
2285pub unsafe extern "C" fn slint_textinput_cut(
2286    text_input: Pin<&TextInput>,
2287    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2288    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2289    self_index: u32,
2290) {
2291    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2292    let self_rc = ItemRc::new(self_component.clone(), self_index);
2293    text_input.cut(window_adapter, &self_rc);
2294}
2295
2296#[cfg(feature = "ffi")]
2297#[unsafe(no_mangle)]
2298pub unsafe extern "C" fn slint_textinput_copy(
2299    text_input: Pin<&TextInput>,
2300    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2301    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2302    self_index: u32,
2303) {
2304    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2305    let self_rc = ItemRc::new(self_component.clone(), self_index);
2306    text_input.copy(window_adapter, &self_rc);
2307}
2308
2309#[cfg(feature = "ffi")]
2310#[unsafe(no_mangle)]
2311pub unsafe extern "C" fn slint_textinput_paste(
2312    text_input: Pin<&TextInput>,
2313    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2314    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2315    self_index: u32,
2316) {
2317    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2318    let self_rc = ItemRc::new(self_component.clone(), self_index);
2319    text_input.paste(window_adapter, &self_rc);
2320}
2321
2322pub fn slint_text_item_fontmetrics(
2323    window_adapter: &Rc<dyn WindowAdapter>,
2324    item_ref: Pin<ItemRef<'_>>,
2325    self_rc: &ItemRc,
2326) -> FontMetrics {
2327    if let Some(simple_text) = ItemRef::downcast_pin::<SimpleText>(item_ref) {
2328        simple_text.font_metrics(window_adapter, self_rc)
2329    } else if let Some(complex_text) = ItemRef::downcast_pin::<ComplexText>(item_ref) {
2330        complex_text.font_metrics(window_adapter, self_rc)
2331    } else if let Some(text_input) = ItemRef::downcast_pin::<TextInput>(item_ref) {
2332        text_input.font_metrics(window_adapter, self_rc)
2333    } else {
2334        Default::default()
2335    }
2336}
2337
2338#[cfg(feature = "ffi")]
2339#[unsafe(no_mangle)]
2340pub unsafe extern "C" fn slint_cpp_text_item_fontmetrics(
2341    window_adapter: *const crate::window::ffi::WindowAdapterRcOpaque,
2342    self_component: &vtable::VRc<crate::item_tree::ItemTreeVTable>,
2343    self_index: u32,
2344) -> FontMetrics {
2345    let window_adapter = &*(window_adapter as *const Rc<dyn WindowAdapter>);
2346    let self_rc = ItemRc::new(self_component.clone(), self_index);
2347    let self_ref = self_rc.borrow();
2348    slint_text_item_fontmetrics(window_adapter, self_ref, &self_rc)
2349}