1use crate::presenters::{TextPresenter, TextStyleOverrides};
2use ferroui_base::media::text_formatting::TextParagraphStyles;
3use crate::primitives::{
4 ScrollBarVisibility, TemplateAppliedEventArgs, TemplatedControl, TemplatedControlImpl,
5};
6use crate::text_box_text_input_method_client::{PreeditSelectionError, TextBoxTextInputMethodClient};
7use crate::utils::{ClipboardHelper, IUndoRedoHost, PrimarySelectionHelper, StringUtils, UndoRedoHelper};
8use crate::{
9 Application, Border, ContentControl, ControlImpl, Decorator, PastingFromClipboardEventArgs, ScrollViewer,
10 SelectableTextBlock, TextBlock, TextBoxTextChange, TextChangedEventArgs, TextChangingEventArgs, TopLevel,
11};
12use ferroui_base::input::platform::{ClipboardError, ClipboardExtensions, ClipboardType, IClipboard};
13use ferroui_base::logging::{LogArea, LogEventLevel, Logger};
14use std::any::Any;
15use ferroui_base::animation::TimeSpan;
16use ferroui_base::data::{BindingMode, BindingPriority};
17use ferroui_base::input::{
18 ContextRequestedEventArgs, FocusChangedEventArgs, HoldingRoutedEventArgs, HoldingState, InputElement,
19 InputElementImpl, InputElementImplExt, Key, KeyEventArgs, KeyGesture, KeyModifiers, MouseButton, NavigationMethod,
20 PointerEventArgs, PointerPressedEventArgs, PointerReleasedEventArgs, PointerType, TappedEventArgs,
21 TextInputEventArgs,
22};
23use ferroui_base::interactivity::{
24 IRoutedEventArgs, Interactive, InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
25};
26use ferroui_base::layout::{HorizontalAlignment, Layoutable, LayoutableImpl, LayoutableImplExt, VerticalAlignment};
27use ferroui_base::media::text_formatting::unicode::GraphemeEnumerator;
28use ferroui_base::media::text_formatting::{
29 GenericTextParagraphProperties, GenericTextRunProperties, ITextSource, LogicalDirection, TextEndOfLine,
30 TextLayout, TextParagraphProperties, TextRun, TextRunProperties,
31};
32use ferroui_base::media::{
33 BaselineAlignment, CharacterHit, FlowDirection, IBrush, TextAlignment, TextWrapping, Typeface,
34};
35use ferroui_base::platform::IPlatformSettings;
36use ferroui_base::reactive::{CompositeDisposable, IDisposable, IObserver, Observable, ObservableExt};
37use ferroui_base::threading::{Dispatcher, DispatcherPriority, DispatcherTask};
38use ferroui_base::utilities::MathUtilities;
39use ferroui_base::{
40 ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, AttachedProperty, BoxedValue,
41 DirectProperty, FerroLocator, FerroObject, LocatorExtensions, VisualImplExt, FerroObjectExtensions, FerroObjectImpl, FerroObjectImplExt,
42 FerroProperty, FerroPropertyChangedEventArgs, Point, Rect, Ref, Size, StyledElementImpl, StyledProperty,
43 StyledPropertyMetadata, StyledPropertyOptions, Thickness, Visual, VisualImpl, VisualTreeAttachmentEventArgs,
44 WeakRef,
45};
46use std::cell::{Cell, OnceCell, RefCell};
47use std::rc::Rc;
48
49#[path = "text_box_history.rs"]
50mod text_box_history;
51use text_box_history::UndoRedoState;
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54enum TextMutationKind {
55 ExternalReplacement,
56 Edit,
57 InternalSynchronization,
58}
59
60const INVALID_CHARACTERS: [u16; 1] = [0x007f];
62
63const MAX_CHARS_BEFORE_UNDO_SNAPSHOT: i32 = 7;
64
65const CARRIAGE_RETURN: u16 = b'\r' as u16;
66const LINE_FEED: u16 = b'\n' as u16;
67const SPACE: u16 = b' ' as u16;
68
69#[repr(C)]
79pub struct TextBox {
80 base: TemplatedControl,
81 touch_radius: OnceCell<i32>,
84 presenter: RefCell<Option<Ref<TextPresenter>>>,
85 scroll_viewer: RefCell<Option<Ref<ScrollViewer>>>,
86 im_client: Rc<TextBoxTextInputMethodClient>,
87 undo_redo_helper: UndoRedoHelper<UndoRedoState>,
88 undo_redo_host: RefCell<Option<Rc<dyn IUndoRedoHost<UndoRedoState>>>>,
90 is_undoing_redoing: Cell<bool>,
91 text_mutation_kind: Cell<TextMutationKind>,
92 native_text_change: RefCell<Option<TextBoxTextChange>>,
93 text_change_generation: Cell<u64>,
94 text_undo_state: RefCell<Option<Rc<dyn Any>>>,
95 needs_undo_redo_snapshot_after_text_change: Cell<bool>,
97 can_cut: Cell<bool>,
98 can_copy: Cell<bool>,
99 can_paste: Cell<bool>,
100 can_undo: Cell<bool>,
101 can_redo: Cell<bool>,
102
103 word_selection_start: Cell<i32>,
104 selection_at_pointer_press: Cell<(i32, i32)>,
105 selected_text_changes_made_since_last_undo_snapshot: Cell<i32>,
106 has_done_snapshot_once: Cell<bool>,
107 current_click_count: Cell<i32>,
108 is_double_tapped: Cell<bool>,
109 is_in_touch_mode: Cell<bool>,
110 last_point: Cell<Point>,
111 is_in_touch_selection_mode: Cell<bool>,
112 is_in_touch_caret_mode: Cell<bool>,
113 has_touch_selection: Cell<bool>,
114 presenter_property_changed: RefCell<Option<Rc<dyn IDisposable>>>,
116}
117
118ferro_class! {
119 TextBox: TemplatedControl, virtuals TextBoxImpl: TemplatedControlImpl {
120 fn coerce_text(this, value: Option<String>) -> Option<String>;
126 }
127}
128ferroui_base::ferro_class_info!(TextBox { new: TextBox::new });
129
130ferro_impl_classes!(TextBox: StyledElementImpl, InteractiveImpl);
131
132impl ControlImpl for TextBox {
133 fn on_create_automation_peer(this: &Self) -> Ref<crate::automation::peers::AutomationPeer> {
134 crate::automation::peers::TextBoxAutomationPeer::new(this).upcast()
135 }
136}
137
138impl FerroObjectImpl for TextBox {
139 fn constructed(this: &Self) {
140 Self::parent_constructed(this);
142
143 let object: &FerroObject = this;
144 let accepts_return = FerroObjectExtensions::get_observable(object, Self::accepts_return_property());
145 let text_wrapping = FerroObjectExtensions::get_observable(object, Self::text_wrapping_property());
146
147 let horizontal_scroll_bar_visibility =
150 Observable::create(move |observer: Rc<dyn IObserver<ScrollBarVisibility>>| {
151 let latest: Rc<(Cell<Option<bool>>, Cell<Option<TextWrapping>>)> =
152 Rc::new((Cell::new(None), Cell::new(None)));
153
154 let publish = {
155 let latest = latest.clone();
156 move || {
157 if let (Some(accepts_return), Some(wrapping)) = (latest.0.get(), latest.1.get()) {
158 observer.on_next(if wrapping != TextWrapping::NoWrap {
159 ScrollBarVisibility::Disabled
160 } else if accepts_return {
161 ScrollBarVisibility::Auto
162 } else {
163 ScrollBarVisibility::Hidden
164 });
165 }
166 }
167 };
168
169 let accepts_return = {
170 let (latest, publish) = (latest.clone(), publish.clone());
171 accepts_return.subscribe_fn(move |value| {
172 latest.0.set(Some(value));
173 publish();
174 })
175 };
176
177 let text_wrapping = text_wrapping.subscribe_fn(move |value| {
178 latest.1.set(Some(value));
179 publish();
180 });
181
182 let subscription: Rc<dyn IDisposable> =
183 Rc::new(CompositeDisposable::from_disposables([accepts_return, text_wrapping]));
184 subscription
185 });
186
187 FerroObjectExtensions::bind_typed(
188 object,
189 ScrollViewer::horizontal_scroll_bar_visibility_property(),
190 horizontal_scroll_bar_visibility,
191 BindingPriority::Style,
192 );
193
194 let host: Rc<dyn IUndoRedoHost<UndoRedoState>> = Rc::new(TextBoxUndoRedoHost(this.to_ref().downgrade()));
195 this.undo_redo_helper.set_host(Rc::downgrade(&host));
196 *this.undo_redo_host.borrow_mut() = Some(host);
197
198 this.selected_text_changes_made_since_last_undo_snapshot.set(0);
199 this.has_done_snapshot_once.set(false);
200 this.update_command_states();
201 this.update_pseudoclasses();
202 }
203
204 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
205 Self::parent_on_property_changed(this, change);
206
207 let property = change.property();
208
209 if property == Self::text_property().as_property() {
210 this.text_change_generation.set(this.text_change_generation.get().wrapping_add(1));
211 let _change = this.im_client.begin_change();
213
214 if this.needs_undo_redo_snapshot_after_text_change.get() {
215 this.needs_undo_redo_snapshot_after_text_change.set(false);
216 this.snapshot_undo_redo(true);
217 }
218
219 if !this.im_client.synchronize_preparing_presenter_text(this) {
224 return;
225 }
226 this.coerce_value(Self::caret_index_property().as_property());
227 this.coerce_value(Self::selection_start_property().as_property());
228 this.coerce_value(Self::selection_end_property().as_property());
229
230 let (old_text, new_text) = change.get_old_and_new_value::<Option<String>>();
231 this.raise_text_change_events(old_text.as_deref().unwrap_or_default(), new_text.as_deref().unwrap_or_default());
232
233 this.update_pseudoclasses();
234 this.update_command_states();
235 } else if property == Self::is_read_only_property().as_property()
236 || property == Self::password_char_property().as_property()
237 || property == Self::reveal_password_property().as_property()
238 {
239 this.update_command_states();
240 } else if property == Self::caret_index_property().as_property() {
241 this.on_caret_index_changed(change);
242 } else if property == Self::selection_start_property().as_property() {
243 this.on_selection_start_changed(change);
244 } else if property == Self::selection_end_property().as_property() {
245 this.on_selection_end_changed(change);
246 } else if property == Self::max_lines_property().as_property() {
247 this.invalidate_measure();
248 } else if property == Self::min_lines_property().as_property() {
249 this.invalidate_measure();
250 } else if property == Self::undo_limit_property().as_property() {
251 this.on_undo_limit_changed(change.get_new_value::<i32>());
252 } else if property == Self::is_undo_enabled_property().as_property() && !change.get_new_value::<bool>() {
253 this.clear_undo_redo();
257 }
258 }
259}
260
261impl VisualImpl for TextBox {
262 fn on_attached_to_visual_tree(this: &Self, e: &VisualTreeAttachmentEventArgs) {
263 Self::parent_on_attached_to_visual_tree(this, e);
264
265 if let Some(presenter) = this.presenter() {
266 if this.is_focused() {
267 presenter.show_caret();
268 } else if this.is_inactive_selection_highlight_enabled() {
269 presenter.set_show_selection_highlight(true);
270 }
271 }
272 this.subscribe_presenter_property_changes(this.presenter().as_ref());
273 }
274
275 fn on_detached_from_visual_tree(this: &Self, e: &VisualTreeAttachmentEventArgs) {
276 Self::parent_on_detached_from_visual_tree(this, e);
277
278 if let Some(presenter) = this.presenter() {
279 presenter.hide_caret();
280 }
281 this.subscribe_presenter_property_changes(None);
282
283 this.im_client.set_presenter(None, None);
284 }
285}
286
287impl LayoutableImpl for TextBox {
288 fn apply_template(this: &Self) {
289 let removing = this.template().is_none() && this.is_attached_to_logical_tree();
294 if removing {
295 this.subscribe_presenter_property_changes(None);
296 }
297 Self::parent_apply_template(this);
298 if removing && this.template().is_none() && this.visual_children_count() == 0 {
299 let previous = this.presenter.borrow_mut().take();
300 this.scroll_viewer.borrow_mut().take();
301 if previous.is_some() {
302 this.im_client.set_presenter(None, None);
307 if this.is_attached_to_visual_tree() {
308 if let Some(current) = this.presenter() {
309 this.im_client.set_presenter(Some(¤t), Some(&this.to_ref()));
312 }
313 }
314 }
315 }
316 }
317
318 fn measure_override(this: &Self, available_size: Size) -> Size {
319 if let Some(scroll_viewer) = this.scroll_viewer() {
320 let mut max_height = f64::INFINITY;
321
322 let max_lines = this.max_lines();
323
324 if max_lines > 0 && this.height().is_nan() {
325 let text_layout = this.create_lines_text_layout(max_lines);
326 let vertical_space = this.get_vertical_space_between_scroll_viewer_and_presenter();
327
328 max_height = (text_layout.height() + vertical_space).ceil();
329 }
330
331 scroll_viewer.set_current_value(Layoutable::max_height_property(), max_height);
332
333 let mut min_height = 0.0;
334
335 let min_lines = this.min_lines();
336
337 if min_lines > 0 && this.height().is_nan() {
338 let text_layout = this.create_lines_text_layout(min_lines);
339 let vertical_space = this.get_vertical_space_between_scroll_viewer_and_presenter();
340
341 min_height = (text_layout.height() + vertical_space).ceil();
342 }
343
344 scroll_viewer.set_current_value(Layoutable::min_height_property(), min_height);
345 }
346
347 Self::parent_measure_override(this, available_size)
348 }
349}
350
351impl InputElementImpl for TextBox {
352 fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
353 Self::parent_on_got_focus(this, e);
354
355 let presenter = this.presenter();
356
357 if let Some(presenter) = &presenter {
358 presenter.set_show_selection_highlight(true);
359 }
360
361 if e.navigation_method == NavigationMethod::Tab
365 && !this.accepts_return()
366 && this.text().is_some_and(|text| !text.is_empty())
367 {
368 this.select_all();
369 }
370
371 this.update_command_states();
372
373 let presenter = this.presenter();
374
375 this.im_client.set_presenter(presenter.as_ref(), Some(&this.to_ref()));
376
377 if let Some(presenter) = &presenter {
378 presenter.show_caret();
379 }
380
381 if this.selection_start() != this.selection_end() {
382 if let Some(canvas) = presenter.as_ref().and_then(|presenter| presenter.text_selection_handle_canvas()) {
383 canvas.show(false);
384 }
385 }
386 }
387
388 fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
389 Self::parent_on_lost_focus(this, e);
390
391 this.im_client.set_presenter(None, None);
394
395 if !this.context_flyout().is_some_and(|flyout| flyout.is_open())
396 && !this.context_menu().is_some_and(|menu| menu.is_open())
397 {
398 if this.clear_selection_on_lost_focus() {
399 this.clear_selection();
400 }
401
402 this.set_current_value(Self::reveal_password_property(), false);
403 if let Some(presenter) = this.presenter() {
404 presenter.remove_text_selection_canvas();
405 }
406 }
407
408 this.update_command_states();
409
410 let presenter = this.presenter();
411
412 if let Some(presenter) = &presenter {
413 presenter.hide_caret();
414 }
415
416 if let Some(presenter) = &presenter {
417 if !this.is_inactive_selection_highlight_enabled() {
418 presenter.set_show_selection_highlight(false);
419 }
420 }
421 }
422
423 fn on_text_input(this: &Self, e: &TextInputEventArgs) {
424 if !e.handled() {
425 this.handle_text_input(e.text.as_deref());
426 e.set_handled(true);
427 }
428 }
429
430 fn on_key_down(this: &Self, e: &KeyEventArgs) {
431 let Some(presenter) = this.presenter() else {
432 return;
433 };
434
435 if presenter.preedit_text().is_some_and(|preedit_text| !preedit_text.is_empty()) {
436 return;
437 }
438
439 let mut text = this.text_utf16();
440 let caret_index = this.caret_index();
441 let mut movement = false;
442 let mut selection = false;
443 let mut handled = false;
444 let modifiers = e.key_modifiers;
445
446 let keymap = Application::current()
447 .expect("the application")
448 .platform_settings()
449 .expect("the platform settings of the application")
450 .hotkey_configuration();
451
452 let _change = this.im_client.begin_change();
453
454 let matches = |gestures: &[KeyGesture]| gestures.iter().any(|g| g.matches(Some(e)));
455 let detect_selection = || e.key_modifiers.contains(keymap.selection_modifiers);
456
457 if matches(&keymap.select_all) {
458 this.select_all();
459 handled = true;
460 } else if matches(&keymap.copy) {
461 if this.can_copy() {
462 this.copy();
463 }
464
465 handled = true;
466 } else if matches(&keymap.cut) {
467 if this.can_cut() {
468 this.cut();
469 }
470
471 handled = true;
472 } else if matches(&keymap.paste) {
473 if this.can_paste() {
474 this.paste();
475 }
476
477 handled = true;
478 } else if matches(&keymap.undo) && this.is_undo_enabled() {
479 if !this.is_read_only() {
480 this.undo();
481 }
482
483 handled = true;
484 } else if matches(&keymap.redo) && this.is_undo_enabled() {
485 if !this.is_read_only() {
486 this.redo();
487 }
488
489 handled = true;
490 } else if matches(&keymap.move_cursor_to_the_start_of_document) {
491 this.move_home(true);
492 movement = true;
493 selection = false;
494 handled = true;
495 this.set_current_value(Self::caret_index_property(), presenter.caret_index());
496 } else if matches(&keymap.move_cursor_to_the_end_of_document) {
497 this.move_end(true);
498 movement = true;
499 selection = false;
500 handled = true;
501 this.set_current_value(Self::caret_index_property(), presenter.caret_index());
502 } else if matches(&keymap.move_cursor_to_the_start_of_line) {
503 this.move_home(false);
504 movement = true;
505 selection = false;
506 handled = true;
507 this.set_current_value(Self::caret_index_property(), presenter.caret_index());
508 } else if matches(&keymap.move_cursor_to_the_end_of_line) {
509 this.move_end(false);
510 movement = true;
511 selection = false;
512 handled = true;
513 this.set_current_value(Self::caret_index_property(), presenter.caret_index());
514 } else if matches(&keymap.move_cursor_to_the_start_of_document_with_selection) {
515 this.set_current_value(Self::selection_start_property(), caret_index);
516 this.move_home(true);
517 this.set_current_value(Self::selection_end_property(), presenter.caret_index());
518 movement = true;
519 selection = true;
520 handled = true;
521 } else if matches(&keymap.move_cursor_to_the_end_of_document_with_selection) {
522 this.set_current_value(Self::selection_start_property(), caret_index);
523 this.move_end(true);
524 this.set_current_value(Self::selection_end_property(), presenter.caret_index());
525 movement = true;
526 selection = true;
527 handled = true;
528 } else if matches(&keymap.move_cursor_to_the_start_of_line_with_selection) {
529 this.set_current_value(Self::selection_start_property(), caret_index);
530 this.move_home(false);
531 this.set_current_value(Self::selection_end_property(), presenter.caret_index());
532 movement = true;
533 selection = true;
534 handled = true;
535 } else if matches(&keymap.move_cursor_to_the_end_of_line_with_selection) {
536 this.set_current_value(Self::selection_start_property(), caret_index);
537 this.move_end(false);
538 this.set_current_value(Self::selection_end_property(), presenter.caret_index());
539 movement = true;
540 selection = true;
541 handled = true;
542 } else if matches(&keymap.page_left) {
543 this.move_page_left();
544 movement = true;
545 selection = false;
546 handled = true;
547 } else if matches(&keymap.page_right) {
548 this.move_page_right();
549 movement = true;
550 selection = false;
551 handled = true;
552 } else if matches(&keymap.page_up) {
553 this.move_page_up();
554 movement = true;
555 selection = false;
556 handled = true;
557 } else if matches(&keymap.page_down) {
558 this.move_page_down();
559 movement = true;
560 selection = false;
561 handled = true;
562 } else {
563 let has_whole_word_modifiers =
565 modifiers.contains(keymap.whole_word_text_action_modifiers) && !this.is_password_box();
566
567 match e.key {
568 Key::Left => {
569 selection = detect_selection();
570 this.move_horizontal(-1, has_whole_word_modifiers, selection, true);
571 if caret_index != presenter.caret_index() {
572 movement = true;
573 }
574 }
575
576 Key::Right => {
577 selection = detect_selection();
578 this.move_horizontal(1, has_whole_word_modifiers, selection, true);
579 if caret_index != presenter.caret_index() {
580 movement = true;
581 }
582 }
583
584 Key::Up => {
585 selection = detect_selection();
586 this.move_vertical(LogicalDirection::Backward, selection);
587 if caret_index != presenter.caret_index() {
588 movement = true;
589 }
590 }
591
592 Key::Down => {
593 selection = detect_selection();
594 this.move_vertical(LogicalDirection::Forward, selection);
595 if caret_index != presenter.caret_index() {
596 movement = true;
597 }
598 }
599
600 Key::Back => {
601 if !this.is_read_only() {
602 this.snapshot_undo_redo(true);
603
604 if has_whole_word_modifiers && this.selection_start() == this.selection_end() {
605 this.set_selection_for_control_backspace();
606 }
607
608 if !this.delete_selection() {
609 let character_hit = presenter.get_next_character_hit(LogicalDirection::Backward);
610
611 let mut backspace_position =
612 character_hit.first_character_index() + character_hit.trailing_length();
613
614 let text_layout = presenter.text_layout();
615
616 let line_index = text_layout.get_line_index_from_character_index(caret_index, true);
617
618 let backspace_character_hit = text_layout.text_lines()[line_index as usize]
619 .get_backspace_caret_character_hit(CharacterHit::new(caret_index));
620
621 if backspace_character_hit.first_character_index() > backspace_position
622 && backspace_character_hit.first_character_index() < caret_index
623 {
624 backspace_position = backspace_character_hit.first_character_index();
625 }
626
627 if caret_index != backspace_position {
628 let start = backspace_position.min(caret_index);
629 let end = backspace_position.max(caret_index);
630
631 remove_range(&mut text, start, end - start);
632
633 if !this.set_text_from_range_edit(Some(String::from_utf16_lossy(&text)), start..end, 0) {
634 e.set_handled(true);
635 return;
636 }
637
638 this.set_current_value(Self::caret_index_property(), start);
639
640 presenter.move_caret_to_text_position(start, false);
641 }
642 }
643
644 this.snapshot_undo_redo(true);
645 }
646
647 handled = true;
648 }
649
650 Key::Delete => {
651 if !this.is_read_only() {
652 this.snapshot_undo_redo(true);
653
654 if has_whole_word_modifiers && this.selection_start() == this.selection_end() {
655 this.set_selection_for_control_delete();
656 }
657
658 if !this.delete_selection() {
659 let character_hit = presenter.get_next_character_hit(LogicalDirection::Forward);
660
661 let next_position =
662 character_hit.first_character_index() + character_hit.trailing_length();
663
664 if next_position != caret_index {
665 let start = next_position.min(caret_index);
666 let end = next_position.max(caret_index);
667
668 remove_range(&mut text, start, end - start);
669
670 if !this.set_text_from_range_edit(Some(String::from_utf16_lossy(&text)), start..end, 0) {
671 e.set_handled(true);
672 return;
673 }
674 }
675 }
676
677 this.snapshot_undo_redo(true);
678 }
679
680 handled = true;
681 }
682
683 Key::Enter => {
684 if this.accepts_return() {
685 if !this.is_read_only() {
686 this.snapshot_undo_redo(true);
687 this.handle_text_input(Some(&this.new_line()));
688 }
689
690 handled = true;
691 }
692 }
693
694 Key::Tab => {
695 if this.accepts_tab() {
696 if !this.is_read_only() {
697 this.snapshot_undo_redo(true);
698 this.handle_text_input(Some("\t"));
699 }
700
701 handled = true;
702 } else {
703 Self::parent_on_key_down(this, e);
704 }
705 }
706
707 Key::Space => {
708 if !this.is_read_only() {
709 this.snapshot_undo_redo(true); }
711 }
712
713 _ => {
714 handled = false;
715 }
716 }
717 }
718
719 if movement && !selection {
720 this.clear_selection();
721 }
722
723 if handled || movement {
724 e.set_handled(true);
725 }
726 }
727
728 fn on_holding(this: &Self, e: &HoldingRoutedEventArgs) {
729 Self::parent_on_holding(this, e);
730
731 let presenter = match this.presenter() {
732 Some(presenter) if e.holding_state() == HoldingState::Started => presenter,
733 _ => {
734 this.is_in_touch_selection_mode.set(e.holding_state() == HoldingState::Canceled);
735 this.has_touch_selection.set(false);
736 return;
737 }
738 };
739
740 let text = this.text();
741
742 let _change = this.im_client.begin_change();
743
744 if let Some(text) = text {
745 let text: Vec<u16> = text.encode_utf16().collect();
746 let position = e.pointer_event_args().get_position(Some(&presenter));
747 let selection_start = this.selection_start();
748 let selection_end = this.selection_end();
749 presenter.move_caret_to_point(position);
750 let caret_index = presenter.caret_index();
751 let is_in_selection =
752 selection_start != selection_end && caret_index >= selection_start && caret_index <= selection_end;
753
754 if is_in_selection {
755 presenter.raise_event(&ContextRequestedEventArgs::from_pointer_event_args(e.pointer_event_args()));
756 } else {
757 if this.is_password_box() {
759 this.word_selection_start.set(-1);
760
761 this.select_all();
762 } else {
763 this.select_word(&text, caret_index, caret_index, caret_index);
764 }
765
766 presenter.ensure_text_selection_layer();
767
768 if this.selection_start() != this.selection_end() {
769 if let Some(canvas) = presenter.text_selection_handle_canvas() {
770 canvas.show(true);
771 }
772 } else {
773 presenter
774 .raise_event(&ContextRequestedEventArgs::from_pointer_event_args(e.pointer_event_args()));
775 }
776 }
777
778 this.has_touch_selection.set(true);
779
780 e.set_handled(true);
781 let element: &InputElement = this;
782 crate::platform::PlatformFeedbackExtensions::perform_feedback(element, crate::platform::FeedbackAction::hold());
783 }
784 }
785
786 fn on_tapped(this: &Self, e: &TappedEventArgs) {
787 Self::parent_on_tapped(this, e);
788
789 if e.pointer().type_() != PointerType::Mouse {
790 if let Some(presenter) = this.presenter() {
791 presenter.ensure_text_selection_layer();
792 if let Some(canvas) = presenter.text_selection_handle_canvas() {
793 canvas.show(false);
794 }
795 }
796 }
797 }
798
799 fn on_pointer_pressed(this: &Self, e: &PointerPressedEventArgs) {
800 let Some(presenter) = this.presenter() else {
801 return;
802 };
803
804 let text = this.text();
805 let click_info = e.get_current_point(Some(this));
806
807 let _change = this.im_client.begin_change();
808
809 this.is_in_touch_mode.set(false);
810 this.is_in_touch_selection_mode.set(false);
811 this.is_double_tapped.set(e.click_count() == 2);
812 this.selection_at_pointer_press.set(this.get_selection_range());
813
814 if let Some(text) = text.filter(|_| !click_info.pointer.captured().is_some_and(|c| c.is::<Border>())) {
815 let text: Vec<u16> = text.encode_utf16().collect();
816
817 if e.pointer().type_() == PointerType::Mouse && click_info.properties.is_left_button_pressed {
818 if let Some(canvas) = presenter.text_selection_handle_canvas() {
819 canvas.hide();
820 }
821 this.current_click_count.set(e.click_count());
822 let point = e.get_position(Some(&presenter));
823
824 presenter.move_caret_to_point(point);
825
826 let caret_index = presenter.caret_index();
827 let click_to_select = e.key_modifiers().contains(KeyModifiers::SHIFT);
828 let mut selection_start = this.selection_start();
829 let mut selection_end = this.selection_end();
830
831 match e.click_count() {
832 1 => {
833 if click_to_select {
834 if this.word_selection_start.get() >= 0 {
835 this.update_word_selection_range(
836 caret_index,
837 &mut selection_start,
838 &mut selection_end,
839 );
840
841 this.set_current_value(Self::selection_start_property(), selection_start);
842 this.set_current_value(Self::selection_end_property(), selection_end);
843 } else {
844 this.set_current_value(Self::selection_end_property(), caret_index);
845 }
846 } else {
847 this.set_current_value(Self::selection_start_property(), caret_index);
848 this.set_current_value(Self::selection_end_property(), caret_index);
849 this.word_selection_start.set(-1);
850 }
851 }
852 2 => {
853 this.select_word(&text, caret_index, selection_start, selection_end);
854 }
855 3 => {
856 this.word_selection_start.set(-1);
857
858 this.select_all();
859 }
860 _ => {}
861 }
862 } else if e.pointer().type_() != PointerType::Mouse {
863 this.is_in_touch_mode.set(true);
864 this.last_point.set(e.get_current_point(Some(&presenter)).position);
865
866 if this.is_double_tapped.get() {
867 presenter.move_caret_to_point(this.last_point.get());
868 let caret_index = presenter.caret_index();
869
870 let selection_start = this.selection_start();
871 let selection_end = this.selection_end();
872
873 this.select_word(&text, caret_index, selection_start, selection_end);
874 presenter.ensure_text_selection_layer();
875 if let Some(canvas) = presenter.text_selection_handle_canvas() {
876 canvas.show(false);
877 }
878 }
879 }
880 }
881
882 this.update_pseudoclasses();
883
884 let element: Ref<InputElement> = presenter.upcast();
885 e.pointer().capture(Some(&element));
886 e.set_handled(true);
887 }
888
889 fn on_pointer_moved(this: &Self, e: &PointerEventArgs) {
890 let Some(presenter) = this.presenter().filter(|presenter| is_captured_by(e, presenter)) else {
891 return;
892 };
893
894 let _change = this.im_client.begin_change();
895 let mut point = e.get_position(Some(&presenter));
896
897 if e.pointer().type_() == PointerType::Mouse {
898 if is_captured_by(e, &presenter) && e.get_current_point(Some(this)).properties.is_left_button_pressed {
900 point = clamp_to_bounds(point, &presenter);
901
902 let previous_index = presenter.caret_index();
903
904 presenter.move_caret_to_point(point);
905
906 let caret_index = presenter.caret_index();
907
908 if (caret_index - previous_index).abs() == 1 {
909 e.prevent_gesture_recognition();
910 }
911
912 if e.pointer().type_() == PointerType::Mouse || this.is_double_tapped.get() {
913 let mut selection_start = this.selection_start();
914 let mut selection_end = this.selection_end();
915
916 if this.word_selection_start.get() >= 0 {
917 this.update_word_selection_range(caret_index, &mut selection_start, &mut selection_end);
918
919 this.set_current_value(Self::selection_start_property(), selection_start);
920 this.set_current_value(Self::selection_end_property(), selection_end);
921 } else {
922 this.set_current_value(Self::selection_end_property(), caret_index);
923 }
924 } else {
925 this.set_current_value(Self::selection_start_property(), caret_index);
926 this.set_current_value(Self::selection_end_property(), caret_index);
927 }
928 }
929 } else if this.is_in_touch_mode.get() {
930 if this.is_in_touch_selection_mode.get() {
931 point = clamp_to_bounds(point, &presenter);
932
933 let previous_index = presenter.caret_index();
934
935 presenter.move_caret_to_point(point);
936
937 let caret_index = presenter.caret_index();
938
939 if (caret_index - previous_index).abs() == 1 {
940 e.prevent_gesture_recognition();
941 }
942
943 let mut selection_start = this.selection_start();
944 let mut selection_end = this.selection_end();
945
946 if this.word_selection_start.get() >= 0 {
947 this.update_word_selection_range(caret_index, &mut selection_start, &mut selection_end);
948
949 this.set_current_value(Self::selection_start_property(), selection_start);
950 this.set_current_value(Self::selection_end_property(), selection_end);
951 } else {
952 this.set_current_value(Self::selection_end_property(), caret_index);
953 }
954 } else {
955 if !this.is_in_touch_caret_mode.get() {
956 let last_point = this.last_point.get();
957 let touch_rect =
958 Rect::new(last_point.x, last_point.y, 0.0, 0.0).inflate(f64::from(this.touch_radius()));
959 let is_in_rect = touch_rect.x < point.x
960 && touch_rect.y < point.y
961 && touch_rect.right() > point.x
962 && touch_rect.bottom() > point.y;
963
964 if !is_in_rect {
965 this.is_in_touch_caret_mode.set(true);
966 }
967 }
968
969 if this.is_in_touch_caret_mode.get() {
970 e.prevent_gesture_recognition();
971 presenter.move_caret_to_point(point);
972 let caret_index = presenter.caret_index();
973 this.set_current_value(Self::selection_start_property(), caret_index);
974 this.set_current_value(Self::selection_end_property(), caret_index);
975 }
976 }
977 }
978 }
979
980 fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
981 let Some(presenter) = this.presenter() else {
982 return;
983 };
984
985 if !is_captured_by(e, &presenter) {
986 return;
987 }
988
989 let _change = this.im_client.begin_change();
990
991 if e.pointer().type_() != PointerType::Mouse
992 && !this.is_in_touch_selection_mode.get()
993 && !this.is_double_tapped.get()
994 && !this.has_touch_selection.get()
995 {
996 let point = e.get_position(Some(&presenter));
997
998 presenter.move_caret_to_point(point);
999
1000 let caret_index = presenter.caret_index();
1001 this.set_current_value(Self::caret_index_property(), caret_index);
1002 this.set_current_value(Self::selection_end_property(), caret_index);
1003 this.set_current_value(Self::selection_start_property(), caret_index);
1004 }
1005
1006 if e.initial_press_mouse_button() == MouseButton::Right {
1007 let point = e.get_position(Some(&presenter));
1008
1009 presenter.move_caret_to_point(point);
1010
1011 let caret_index = presenter.caret_index();
1012
1013 let first_selection = this.selection_start().min(this.selection_end());
1016 let last_selection = this.selection_start().max(this.selection_end());
1017 let did_click_in_selection = this.selection_start() != this.selection_end()
1018 && caret_index >= first_selection
1019 && caret_index <= last_selection;
1020 if !did_click_in_selection {
1021 this.set_current_value(Self::caret_index_property(), caret_index);
1022 this.set_current_value(Self::selection_end_property(), caret_index);
1023 this.set_current_value(Self::selection_start_property(), caret_index);
1024 }
1025 }
1026
1027 if e.initial_press_mouse_button() == MouseButton::Middle {
1028 if !this.is_read_only() {
1030 if let Some(primary_selection) = TopLevel::get_top_level(Some(this))
1031 .and_then(|top_level| top_level.try_get_clipboard(ClipboardType::PrimarySelection))
1032 {
1033 presenter.move_caret_to_point(e.get_position(Some(&presenter)));
1034
1035 let caret_index = presenter.caret_index();
1036 this.set_current_value(Self::caret_index_property(), caret_index);
1037 this.set_current_value(Self::selection_start_property(), caret_index);
1038 this.set_current_value(Self::selection_end_property(), caret_index);
1039
1040 drop(this.paste_core_async(Some(primary_selection)));
1043 e.set_handled(true);
1044 }
1045 }
1046 } else if e.initial_press_mouse_button() == MouseButton::Left {
1047 let selection = this.get_selection_range();
1048 if !this.is_password_box()
1049 && selection.0 != selection.1
1050 && selection != this.selection_at_pointer_press.get()
1051 {
1052 let target = this.to_ref();
1054 drop(PrimarySelectionHelper::publish_text_async(this, move || Some(target.get_selection())));
1055 }
1056 }
1057
1058 this.is_in_touch_selection_mode.set(false);
1059 this.is_in_touch_caret_mode.set(false);
1060 this.has_touch_selection.set(false);
1061 }
1062}
1063
1064impl TemplatedControlImpl for TextBox {
1065 fn on_apply_template(this: &Self, e: &TemplateAppliedEventArgs) {
1066 let presenter = e.name_scope().get_as::<TextPresenter>("PART_TextPresenter");
1067 *this.presenter.borrow_mut() = Some(presenter.clone());
1068 this.subscribe_presenter_property_changes(
1072 this.is_attached_to_visual_tree().then_some(&presenter),
1073 );
1074
1075 let scroll_viewer = e.name_scope().find_as::<ScrollViewer>("PART_ScrollViewer");
1076 *this.scroll_viewer.borrow_mut() = scroll_viewer;
1077
1078 this.im_client.set_presenter(Some(&presenter), Some(&this.to_ref()));
1079 this.update_pseudoclasses();
1080
1081 if this.is_focused() {
1082 presenter.show_caret();
1083 }
1084 }
1085}
1086
1087impl TextBoxImpl for TextBox {
1088 fn coerce_text(this: &Self, value: Option<String>) -> Option<String> {
1089 if !this.is_undoing_redoing.get() {
1090 match this.text_mutation_kind.get() {
1091 TextMutationKind::Edit => {
1092 this.snapshot_undo_redo(true);
1093
1094 if !this.undo_redo_helper.can_undo() && this.text() != value {
1095 this.needs_undo_redo_snapshot_after_text_change.set(true);
1096 }
1097 }
1098
1099 TextMutationKind::InternalSynchronization => {}
1100
1101 TextMutationKind::ExternalReplacement => {
1102 if this.text() != value {
1108 this.clear_undo_redo();
1109 this.needs_undo_redo_snapshot_after_text_change.set(true);
1110 }
1111 }
1112 }
1113 }
1114
1115 value
1116 }
1117}
1118
1119struct TextBoxUndoRedoHost(WeakRef<TextBox>);
1121
1122impl IUndoRedoHost<UndoRedoState> for TextBoxUndoRedoHost {
1123 fn undo_redo_state(&self) -> UndoRedoState {
1124 match self.0.upgrade() {
1125 Some(text_box) => text_box.capture_text_history_state(),
1126 None => UndoRedoState::new(None, 0),
1127 }
1128 }
1129
1130 fn set_undo_redo_state(&self, value: UndoRedoState) {
1131 if let Some(text_box) = self.0.upgrade() {
1132 text_box.restore_text_history_state(value);
1133 }
1134 }
1135
1136 fn on_undo_stack_changed(&self) {
1138 if let Some(text_box) = self.0.upgrade() {
1139 text_box.set_can_undo(text_box.undo_redo_helper.can_undo());
1140 }
1141 }
1142
1143 fn on_redo_stack_changed(&self) {
1145 if let Some(text_box) = self.0.upgrade() {
1146 text_box.set_can_redo(text_box.undo_redo_helper.can_redo());
1147 }
1148 }
1149}
1150
1151struct LineTextSource {
1154 lines: i32,
1155}
1156
1157impl ITextSource for LineTextSource {
1158 fn get_text_run(&self, text_source_index: i32) -> Option<Rc<dyn TextRun>> {
1159 if text_source_index >= self.lines {
1160 return None;
1161 }
1162
1163 Some(Rc::new(TextEndOfLine::with_length(1)))
1164 }
1165}
1166
1167impl TextBox {
1168 pub fn cut_gesture() -> Option<KeyGesture> {
1170 Application::current()?.platform_settings()?.hotkey_configuration().cut.first().cloned()
1171 }
1172
1173 pub fn copy_gesture() -> Option<KeyGesture> {
1175 Application::current()?.platform_settings()?.hotkey_configuration().copy.first().cloned()
1176 }
1177
1178 pub fn paste_gesture() -> Option<KeyGesture> {
1180 Application::current()?.platform_settings()?.hotkey_configuration().paste.first().cloned()
1181 }
1182}
1183
1184ferroui_base::ferro_properties! { impl TextBox, also [
1185 TextBox::use_floating_placeholder_property,
1186 TextBox::placeholder_foreground_property,
1187 TextBox::new_line_property,
1188 TextBox::inner_left_content_property,
1189 TextBox::inner_right_content_property,
1190 TextBox::reveal_password_property,
1191 TextBox::can_cut_property,
1192 TextBox::can_copy_property,
1193 TextBox::can_paste_property,
1194 TextBox::is_undo_enabled_property,
1195 TextBox::undo_limit_property,
1196 TextBox::can_undo_property,
1197 TextBox::can_redo_property,
1198] {
1199 ferro_property!(
1200 pub fn is_inactive_selection_highlight_enabled_property() -> StyledProperty<bool> {
1202 FerroProperty::register::<TextBox, _>("IsInactiveSelectionHighlightEnabled", true)
1203 }
1204 );
1205
1206 ferro_property!(
1207 pub fn clear_selection_on_lost_focus_property() -> StyledProperty<bool> {
1209 FerroProperty::register::<TextBox, _>("ClearSelectionOnLostFocus", true)
1210 }
1211 );
1212
1213 ferro_property!(
1214 pub fn accepts_return_property() -> StyledProperty<bool> {
1216 FerroProperty::register::<TextBox, _>("AcceptsReturn", false)
1217 }
1218 );
1219
1220 ferro_property!(
1221 pub fn accepts_tab_property() -> StyledProperty<bool> {
1223 FerroProperty::register::<TextBox, _>("AcceptsTab", false)
1224 }
1225 );
1226
1227 ferro_property!(
1228 pub fn caret_index_property() -> StyledProperty<i32> {
1230 FerroProperty::register_with::<TextBox, _>(
1231 "CaretIndex",
1232 StyledPropertyOptions::new(0).coerce(Self::coerce_caret_index),
1233 )
1234 }
1235 );
1236
1237 ferro_property!(
1238 pub fn is_read_only_property() -> StyledProperty<bool> {
1240 FerroProperty::register::<TextBox, _>("IsReadOnly", false)
1241 }
1242 );
1243
1244 ferro_property!(
1245 pub fn password_char_property() -> StyledProperty<char> {
1247 FerroProperty::register::<TextBox, _>("PasswordChar", '\0')
1248 }
1249 );
1250
1251 ferro_property!(
1252 pub fn selection_brush_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
1254 FerroProperty::register::<TextBox, _>("SelectionBrush", None)
1255 }
1256 );
1257
1258 ferro_property!(
1259 pub fn selection_foreground_brush_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
1261 FerroProperty::register::<TextBox, _>("SelectionForegroundBrush", None)
1262 }
1263 );
1264
1265 ferro_property!(
1266 pub fn caret_brush_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
1268 FerroProperty::register::<TextBox, _>("CaretBrush", None)
1269 }
1270 );
1271
1272 ferro_property!(
1273 pub fn caret_blink_interval_property() -> StyledProperty<TimeSpan> {
1275 FerroProperty::register::<TextBox, _>("CaretBlinkInterval", TimeSpan::from_milliseconds(500.0))
1276 }
1277 );
1278
1279 ferro_property!(
1280 pub fn selection_start_property() -> StyledProperty<i32> {
1282 FerroProperty::register_with::<TextBox, _>(
1283 "SelectionStart",
1284 StyledPropertyOptions::new(0).coerce(Self::coerce_caret_index),
1285 )
1286 }
1287 );
1288
1289 ferro_property!(
1290 pub fn selection_end_property() -> StyledProperty<i32> {
1292 FerroProperty::register_with::<TextBox, _>(
1293 "SelectionEnd",
1294 StyledPropertyOptions::new(0).coerce(Self::coerce_caret_index),
1295 )
1296 }
1297 );
1298
1299 ferro_property!(
1300 pub fn max_length_property() -> StyledProperty<i32> {
1302 FerroProperty::register::<TextBox, _>("MaxLength", 0)
1303 }
1304 );
1305
1306 ferro_property!(
1307 pub fn max_lines_property() -> StyledProperty<i32> {
1309 FerroProperty::register::<TextBox, _>("MaxLines", 0)
1310 }
1311 );
1312
1313 ferro_property!(
1314 pub fn min_lines_property() -> StyledProperty<i32> {
1316 FerroProperty::register::<TextBox, _>("MinLines", 0)
1317 }
1318 );
1319
1320 ferro_property!(
1321 pub fn text_property() -> StyledProperty<Option<String>> {
1323 TextBlock::text_property().add_owner_with::<TextBox>(
1324 StyledPropertyMetadata::new(None)
1325 .with_coerce(Self::coerce_text_callback)
1326 .with_default_binding_mode(BindingMode::TwoWay)
1327 .with_enable_data_validation(true),
1328 )
1329 }
1330 );
1331
1332 ferro_property!(
1333 pub fn text_style_overrides_property() -> StyledProperty<Option<TextStyleOverrides>> {
1336 FerroProperty::register::<TextBox, _>("TextStyleOverrides", None)
1337 }
1338 );
1339
1340 ferro_property!(
1341 pub fn text_paragraph_styles_property() -> StyledProperty<Option<TextParagraphStyles>> {
1344 FerroProperty::register::<TextBox, _>("TextParagraphStyles", None)
1345 }
1346 );
1347
1348 ferro_property!(
1349 pub fn text_alignment_property() -> AttachedProperty<TextAlignment> {
1351 TextBlock::text_alignment_property().add_owner::<TextBox>()
1352 }
1353 );
1354
1355 ferro_property!(
1356 pub fn horizontal_content_alignment_property() -> StyledProperty<HorizontalAlignment> {
1358 ContentControl::horizontal_content_alignment_property().add_owner::<TextBox>()
1359 }
1360 );
1361
1362 ferro_property!(
1363 pub fn vertical_content_alignment_property() -> StyledProperty<VerticalAlignment> {
1365 ContentControl::vertical_content_alignment_property().add_owner::<TextBox>()
1366 }
1367 );
1368
1369 ferro_property!(
1370 pub fn text_wrapping_property() -> AttachedProperty<TextWrapping> {
1372 TextBlock::text_wrapping_property().add_owner::<TextBox>()
1373 }
1374 );
1375
1376 ferro_property!(
1377 pub fn line_height_property() -> AttachedProperty<f64> {
1379 TextBlock::line_height_property().add_owner_with::<TextBox>(StyledPropertyMetadata::new(Some(f64::NAN)))
1380 }
1381 );
1382
1383 ferro_property!(
1384 pub fn placeholder_text_property() -> StyledProperty<Option<String>> {
1386 FerroProperty::register::<TextBox, _>("PlaceholderText", None)
1387 }
1388 );
1389} }
1390
1391impl TextBox {
1392 #[deprecated(note = "Use placeholder_text_property instead.")]
1394 pub fn watermark_property() -> &'static StyledProperty<Option<String>> {
1395 Self::placeholder_text_property()
1396 }
1397
1398 ferro_property!(for TextBox;
1399 pub fn use_floating_placeholder_property() -> StyledProperty<bool> {
1401 FerroProperty::register::<TextBox, _>("UseFloatingPlaceholder", false)
1402 }
1403 );
1404
1405 #[deprecated(note = "Use use_floating_placeholder_property instead.")]
1407 pub fn use_floating_watermark_property() -> &'static StyledProperty<bool> {
1408 Self::use_floating_placeholder_property()
1409 }
1410
1411 ferro_property!(for TextBox;
1412 pub fn placeholder_foreground_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
1414 FerroProperty::register::<TextBox, _>("PlaceholderForeground", None)
1415 }
1416 );
1417
1418 #[deprecated(note = "Use placeholder_foreground_property instead.")]
1420 pub fn watermark_foreground_property() -> &'static StyledProperty<Option<Rc<dyn IBrush>>> {
1421 Self::placeholder_foreground_property()
1422 }
1423
1424 ferro_property!(for TextBox;
1425 pub fn new_line_property() -> StyledProperty<String> {
1427 FerroProperty::register::<TextBox, _>("NewLine", environment_new_line().to_owned())
1428 }
1429 );
1430
1431 ferro_property!(for TextBox;
1432 pub fn inner_left_content_property() -> StyledProperty<Option<BoxedValue>> {
1434 FerroProperty::register::<TextBox, _>("InnerLeftContent", None)
1435 }
1436 );
1437
1438 ferro_property!(for TextBox;
1439 pub fn inner_right_content_property() -> StyledProperty<Option<BoxedValue>> {
1441 FerroProperty::register::<TextBox, _>("InnerRightContent", None)
1442 }
1443 );
1444
1445 ferro_property!(for TextBox;
1446 pub fn reveal_password_property() -> StyledProperty<bool> {
1448 FerroProperty::register::<TextBox, _>("RevealPassword", false)
1449 }
1450 );
1451
1452 ferro_property!(for TextBox;
1453 pub fn can_cut_property() -> DirectProperty<TextBox, bool> {
1455 FerroProperty::register_direct::<TextBox, _>("CanCut", |o| o.can_cut(), None, false)
1456 }
1457 );
1458
1459 ferro_property!(for TextBox;
1460 pub fn can_copy_property() -> DirectProperty<TextBox, bool> {
1462 FerroProperty::register_direct::<TextBox, _>("CanCopy", |o| o.can_copy(), None, false)
1463 }
1464 );
1465
1466 ferro_property!(for TextBox;
1467 pub fn can_paste_property() -> DirectProperty<TextBox, bool> {
1469 FerroProperty::register_direct::<TextBox, _>("CanPaste", |o| o.can_paste(), None, false)
1470 }
1471 );
1472
1473 ferro_property!(for TextBox;
1474 pub fn is_undo_enabled_property() -> StyledProperty<bool> {
1476 FerroProperty::register::<TextBox, _>("IsUndoEnabled", true)
1477 }
1478 );
1479
1480 ferro_property!(for TextBox;
1481 pub fn undo_limit_property() -> StyledProperty<i32> {
1483 FerroProperty::register::<TextBox, _>("UndoLimit", UndoRedoHelper::<UndoRedoState>::DEFAULT_UNDO_LIMIT)
1484 }
1485 );
1486
1487 ferro_property!(for TextBox;
1488 pub fn can_undo_property() -> DirectProperty<TextBox, bool> {
1490 FerroProperty::register_direct::<TextBox, _>("CanUndo", |o| o.can_undo(), None, false)
1491 }
1492 );
1493
1494 ferro_property!(for TextBox;
1495 pub fn can_redo_property() -> DirectProperty<TextBox, bool> {
1497 FerroProperty::register_direct::<TextBox, _>("CanRedo", |o| o.can_redo(), None, false)
1498 }
1499 );
1500
1501 ferro_routed_event!(
1502 pub fn copying_to_clipboard_event() -> RoutedEvent<RoutedEventArgs> {
1504 RoutedEvent::register::<TextBox, _>("CopyingToClipboard", RoutingStrategies::BUBBLE)
1505 }
1506 );
1507
1508 ferro_routed_event!(
1509 pub fn cutting_to_clipboard_event() -> RoutedEvent<RoutedEventArgs> {
1511 RoutedEvent::register::<TextBox, _>("CuttingToClipboard", RoutingStrategies::BUBBLE)
1512 }
1513 );
1514
1515 ferro_routed_event!(
1516 pub fn pasting_from_clipboard_event() -> RoutedEvent<RoutedEventArgs> {
1520 RoutedEvent::register::<TextBox, _>("PastingFromClipboard", RoutingStrategies::BUBBLE)
1521 }
1522 );
1523
1524 ferro_routed_event!(
1525 pub fn text_changed_event() -> RoutedEvent<TextChangedEventArgs> {
1527 RoutedEvent::register::<TextBox, _>("TextChanged", RoutingStrategies::BUBBLE)
1528 }
1529 );
1530
1531 ferro_routed_event!(
1532 pub fn text_changing_event() -> RoutedEvent<TextChangingEventArgs> {
1534 RoutedEvent::register::<TextBox, _>("TextChanging", RoutingStrategies::BUBBLE)
1535 }
1536 );
1537
1538 fn static_constructor() {
1539 InputElement::focusable_property().override_default_value::<TextBox>(true);
1540 InputElement::text_input_method_client_requested_event().add_class_handler::<TextBox>(|tb, e| {
1542 if !tb.is_read_only() {
1543 e.set_client(Some(tb.im_client.clone()));
1544 }
1545 });
1546 }
1547
1548 pub fn construct() -> Self {
1550 Self {
1551 base: TemplatedControl::construct(),
1552 touch_radius: OnceCell::new(),
1553 presenter: RefCell::new(None),
1554 scroll_viewer: RefCell::new(None),
1555 im_client: TextBoxTextInputMethodClient::new(),
1556 undo_redo_helper: UndoRedoHelper::unattached(),
1557 undo_redo_host: RefCell::new(None),
1558 is_undoing_redoing: Cell::new(false),
1559 text_mutation_kind: Cell::new(TextMutationKind::ExternalReplacement),
1560 native_text_change: RefCell::new(None),
1561 text_change_generation: Cell::new(0),
1562 text_undo_state: RefCell::new(None),
1563 needs_undo_redo_snapshot_after_text_change: Cell::new(false),
1564 can_cut: Cell::new(false),
1565 can_copy: Cell::new(false),
1566 can_paste: Cell::new(false),
1567 can_undo: Cell::new(false),
1568 can_redo: Cell::new(false),
1569 word_selection_start: Cell::new(-1),
1570 selection_at_pointer_press: Cell::new((0, 0)),
1571 selected_text_changes_made_since_last_undo_snapshot: Cell::new(0),
1572 has_done_snapshot_once: Cell::new(false),
1573 current_click_count: Cell::new(0),
1574 is_double_tapped: Cell::new(false),
1575 is_in_touch_mode: Cell::new(false),
1576 last_point: Cell::new(Point::default()),
1577 is_in_touch_selection_mode: Cell::new(false),
1578 is_in_touch_caret_mode: Cell::new(false),
1579 has_touch_selection: Cell::new(false),
1580 presenter_property_changed: RefCell::new(None),
1581 }
1582 }
1583
1584 pub fn new() -> Ref<Self> {
1586 instantiate(Self::construct())
1587 }
1588
1589 pub fn is_inactive_selection_highlight_enabled(&self) -> bool {
1592 self.get_value(Self::is_inactive_selection_highlight_enabled_property())
1593 }
1594
1595 pub fn set_is_inactive_selection_highlight_enabled(&self, value: bool) {
1596 self.set_value(Self::is_inactive_selection_highlight_enabled_property(), value)
1597 }
1598
1599 pub fn clear_selection_on_lost_focus(&self) -> bool {
1601 self.get_value(Self::clear_selection_on_lost_focus_property())
1602 }
1603
1604 pub fn set_clear_selection_on_lost_focus(&self, value: bool) {
1605 self.set_value(Self::clear_selection_on_lost_focus_property(), value)
1606 }
1607
1608 pub fn accepts_return(&self) -> bool {
1611 self.get_value(Self::accepts_return_property())
1612 }
1613
1614 pub fn set_accepts_return(&self, value: bool) {
1615 self.set_value(Self::accepts_return_property(), value)
1616 }
1617
1618 pub fn accepts_tab(&self) -> bool {
1620 self.get_value(Self::accepts_tab_property())
1621 }
1622
1623 pub fn set_accepts_tab(&self, value: bool) {
1624 self.set_value(Self::accepts_tab_property(), value)
1625 }
1626
1627 pub fn caret_index(&self) -> i32 {
1629 self.get_value(Self::caret_index_property())
1630 }
1631
1632 pub fn set_caret_index(&self, value: i32) {
1633 self.set_value(Self::caret_index_property(), value)
1634 }
1635
1636 fn on_caret_index_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
1637 if self.is_undo_enabled() {
1638 if let Some(state) = self.undo_redo_helper.try_get_last_state() {
1639 if state.text == self.text() {
1640 self.undo_redo_helper.update_last_state();
1641 }
1642 }
1643 }
1644
1645 let _change = self.im_client.begin_change();
1646
1647 let new_value = e.get_new_value::<i32>();
1648 self.set_current_value(Self::selection_start_property(), new_value);
1649 self.set_current_value(Self::selection_end_property(), new_value);
1650
1651 if let Some(presenter) = self.presenter() {
1652 presenter.set_current_value(TextPresenter::caret_index_property(), new_value);
1653 }
1654 }
1655
1656 pub fn is_read_only(&self) -> bool {
1658 self.get_value(Self::is_read_only_property())
1659 }
1660
1661 pub fn set_is_read_only(&self, value: bool) {
1662 self.set_value(Self::is_read_only_property(), value)
1663 }
1664
1665 pub fn password_char(&self) -> char {
1668 self.get_value(Self::password_char_property())
1669 }
1670
1671 pub fn set_password_char(&self, value: char) {
1672 self.set_value(Self::password_char_property(), value)
1673 }
1674
1675 pub fn selection_brush(&self) -> Option<Rc<dyn IBrush>> {
1677 self.get_value(Self::selection_brush_property())
1678 }
1679
1680 pub fn set_selection_brush(&self, value: Option<Rc<dyn IBrush>>) {
1681 self.set_value(Self::selection_brush_property(), value)
1682 }
1683
1684 pub fn selection_foreground_brush(&self) -> Option<Rc<dyn IBrush>> {
1686 self.get_value(Self::selection_foreground_brush_property())
1687 }
1688
1689 pub fn set_selection_foreground_brush(&self, value: Option<Rc<dyn IBrush>>) {
1690 self.set_value(Self::selection_foreground_brush_property(), value)
1691 }
1692
1693 pub fn caret_brush(&self) -> Option<Rc<dyn IBrush>> {
1695 self.get_value(Self::caret_brush_property())
1696 }
1697
1698 pub fn set_caret_brush(&self, value: Option<Rc<dyn IBrush>>) {
1699 self.set_value(Self::caret_brush_property(), value)
1700 }
1701
1702 pub fn caret_blink_interval(&self) -> TimeSpan {
1704 self.get_value(Self::caret_blink_interval_property())
1705 }
1706
1707 pub fn set_caret_blink_interval(&self, value: TimeSpan) {
1708 self.set_value(Self::caret_blink_interval_property(), value)
1709 }
1710
1711 pub fn selection_start(&self) -> i32 {
1713 self.get_value(Self::selection_start_property())
1714 }
1715
1716 pub fn set_selection_start(&self, value: i32) {
1717 self.set_value(Self::selection_start_property(), value)
1718 }
1719
1720 fn on_selection_start_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
1721 self.update_command_states();
1722
1723 let value = e.get_new_value::<i32>();
1724 if self.selection_end() == value && self.caret_index() != value {
1725 self.set_current_value(Self::caret_index_property(), value);
1726 }
1727 }
1728
1729 pub fn selection_end(&self) -> i32 {
1734 self.get_value(Self::selection_end_property())
1735 }
1736
1737 pub fn set_selection_end(&self, value: i32) {
1738 self.set_value(Self::selection_end_property(), value)
1739 }
1740
1741 fn on_selection_end_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
1742 self.update_command_states();
1743
1744 let value = e.get_new_value::<i32>();
1745 if self.selection_start() == value && self.caret_index() != value {
1746 self.set_current_value(Self::caret_index_property(), value);
1747 }
1748 }
1749
1750 pub fn max_length(&self) -> i32 {
1753 self.get_value(Self::max_length_property())
1754 }
1755
1756 pub fn set_max_length(&self, value: i32) {
1757 self.set_value(Self::max_length_property(), value)
1758 }
1759
1760 pub fn max_lines(&self) -> i32 {
1762 self.get_value(Self::max_lines_property())
1763 }
1764
1765 pub fn set_max_lines(&self, value: i32) {
1766 self.set_value(Self::max_lines_property(), value)
1767 }
1768
1769 pub fn min_lines(&self) -> i32 {
1771 self.get_value(Self::min_lines_property())
1772 }
1773
1774 pub fn set_min_lines(&self, value: i32) {
1775 self.set_value(Self::min_lines_property(), value)
1776 }
1777
1778 pub fn line_height(&self) -> f64 {
1780 self.get_value(Self::line_height_property())
1781 }
1782
1783 pub fn set_line_height(&self, value: f64) {
1784 self.set_value(Self::line_height_property(), value)
1785 }
1786
1787 pub fn text(&self) -> Option<String> {
1789 self.get_value(Self::text_property())
1790 }
1791
1792 pub fn set_text(&self, value: Option<&str>) {
1793 self.set_value(Self::text_property(), value.map(str::to_owned))
1794 }
1795
1796 pub fn is_composing_text(&self) -> bool {
1803 self.im_client.has_pending_preedit()
1804 }
1805
1806 fn coerce_text_callback(sender: &FerroObject, value: Option<String>) -> Option<String> {
1807 match sender.downcast_ref::<TextBox>() {
1808 Some(text_box) => text_box.coerce_text(value),
1809 None => value,
1810 }
1811 }
1812
1813 pub fn selected_text(&self) -> String {
1815 self.get_selection()
1816 }
1817
1818 pub fn set_selected_text(&self, value: Option<&str>) {
1820 match value {
1821 Some(value) if !value.is_empty() => self.handle_text_input(Some(value)),
1822 _ => {
1823 self.selected_text_changes_made_since_last_undo_snapshot
1824 .set(self.selected_text_changes_made_since_last_undo_snapshot.get() + 1);
1825 self.snapshot_undo_redo(false);
1826 self.delete_selection();
1827 }
1828 }
1829 }
1830
1831 pub fn horizontal_content_alignment(&self) -> HorizontalAlignment {
1833 self.get_value(Self::horizontal_content_alignment_property())
1834 }
1835
1836 pub fn set_horizontal_content_alignment(&self, value: HorizontalAlignment) {
1837 self.set_value(Self::horizontal_content_alignment_property(), value)
1838 }
1839
1840 pub fn vertical_content_alignment(&self) -> VerticalAlignment {
1842 self.get_value(Self::vertical_content_alignment_property())
1843 }
1844
1845 pub fn set_vertical_content_alignment(&self, value: VerticalAlignment) {
1846 self.set_value(Self::vertical_content_alignment_property(), value)
1847 }
1848
1849 pub fn text_style_overrides(&self) -> Option<TextStyleOverrides> {
1854 self.get_value(Self::text_style_overrides_property())
1855 }
1856
1857 pub fn set_text_style_overrides(&self, value: Option<TextStyleOverrides>) {
1858 self.set_value(Self::text_style_overrides_property(), value)
1859 }
1860
1861 pub fn text_paragraph_styles(&self) -> Option<TextParagraphStyles> {
1865 self.get_value(Self::text_paragraph_styles_property())
1866 }
1867
1868 pub fn set_text_paragraph_styles(&self, value: Option<TextParagraphStyles>) {
1869 self.set_value(Self::text_paragraph_styles_property(), value)
1870 }
1871
1872 pub fn text_alignment(&self) -> TextAlignment {
1874 self.get_value(Self::text_alignment_property())
1875 }
1876
1877 pub fn set_text_alignment(&self, value: TextAlignment) {
1878 self.set_value(Self::text_alignment_property(), value)
1879 }
1880
1881 pub fn placeholder_text(&self) -> Option<String> {
1884 self.get_value(Self::placeholder_text_property())
1885 }
1886
1887 pub fn set_placeholder_text(&self, value: Option<&str>) {
1888 self.set_value(Self::placeholder_text_property(), value.map(str::to_owned))
1889 }
1890
1891 #[deprecated(note = "Use placeholder_text instead.")]
1894 pub fn watermark(&self) -> Option<String> {
1895 self.placeholder_text()
1896 }
1897
1898 #[deprecated(note = "Use set_placeholder_text instead.")]
1899 pub fn set_watermark(&self, value: Option<&str>) {
1900 self.set_placeholder_text(value)
1901 }
1902
1903 pub fn use_floating_placeholder(&self) -> bool {
1906 self.get_value(Self::use_floating_placeholder_property())
1907 }
1908
1909 pub fn set_use_floating_placeholder(&self, value: bool) {
1910 self.set_value(Self::use_floating_placeholder_property(), value)
1911 }
1912
1913 #[deprecated(note = "Use use_floating_placeholder instead.")]
1916 pub fn use_floating_watermark(&self) -> bool {
1917 self.use_floating_placeholder()
1918 }
1919
1920 #[deprecated(note = "Use set_use_floating_placeholder instead.")]
1921 pub fn set_use_floating_watermark(&self, value: bool) {
1922 self.set_use_floating_placeholder(value)
1923 }
1924
1925 pub fn placeholder_foreground(&self) -> Option<Rc<dyn IBrush>> {
1927 self.get_value(Self::placeholder_foreground_property())
1928 }
1929
1930 pub fn set_placeholder_foreground(&self, value: Option<Rc<dyn IBrush>>) {
1931 self.set_value(Self::placeholder_foreground_property(), value)
1932 }
1933
1934 #[deprecated(note = "Use placeholder_foreground instead.")]
1936 pub fn watermark_foreground(&self) -> Option<Rc<dyn IBrush>> {
1937 self.placeholder_foreground()
1938 }
1939
1940 #[deprecated(note = "Use set_placeholder_foreground instead.")]
1941 pub fn set_watermark_foreground(&self, value: Option<Rc<dyn IBrush>>) {
1942 self.set_placeholder_foreground(value)
1943 }
1944
1945 pub fn inner_left_content(&self) -> Option<BoxedValue> {
1948 self.get_value(Self::inner_left_content_property())
1949 }
1950
1951 pub fn set_inner_left_content(&self, value: Option<BoxedValue>) {
1952 self.set_value(Self::inner_left_content_property(), value)
1953 }
1954
1955 pub fn inner_right_content(&self) -> Option<BoxedValue> {
1958 self.get_value(Self::inner_right_content_property())
1959 }
1960
1961 pub fn set_inner_right_content(&self, value: Option<BoxedValue>) {
1962 self.set_value(Self::inner_right_content_property(), value)
1963 }
1964
1965 pub fn reveal_password(&self) -> bool {
1967 self.get_value(Self::reveal_password_property())
1968 }
1969
1970 pub fn set_reveal_password(&self, value: bool) {
1971 self.set_value(Self::reveal_password_property(), value)
1972 }
1973
1974 pub fn text_wrapping(&self) -> TextWrapping {
1976 self.get_value(Self::text_wrapping_property())
1977 }
1978
1979 pub fn set_text_wrapping(&self, value: TextWrapping) {
1980 self.set_value(Self::text_wrapping_property(), value)
1981 }
1982
1983 pub fn new_line(&self) -> String {
1986 self.get_value(Self::new_line_property())
1987 }
1988
1989 pub fn set_new_line(&self, value: &str) {
1990 self.set_value(Self::new_line_property(), value.to_owned())
1991 }
1992
1993 pub fn clear_selection(&self) {
1995 self.set_current_value(Self::caret_index_property(), self.selection_start());
1996 self.set_current_value(Self::selection_end_property(), self.selection_start());
1997 }
1998
1999 pub fn can_cut(&self) -> bool {
2001 self.can_cut.get()
2002 }
2003
2004 fn set_can_cut(&self, value: bool) {
2005 self.set_and_raise_cell(Self::can_cut_property(), &self.can_cut, value);
2006 }
2007
2008 pub fn can_copy(&self) -> bool {
2010 self.can_copy.get()
2011 }
2012
2013 fn set_can_copy(&self, value: bool) {
2014 self.set_and_raise_cell(Self::can_copy_property(), &self.can_copy, value);
2015 }
2016
2017 pub fn can_paste(&self) -> bool {
2019 self.can_paste.get()
2020 }
2021
2022 fn set_can_paste(&self, value: bool) {
2023 self.set_and_raise_cell(Self::can_paste_property(), &self.can_paste, value);
2024 }
2025
2026 pub fn is_undo_enabled(&self) -> bool {
2028 self.get_value(Self::is_undo_enabled_property())
2029 }
2030
2031 pub fn set_is_undo_enabled(&self, value: bool) {
2032 self.set_value(Self::is_undo_enabled_property(), value)
2033 }
2034
2035 pub fn undo_limit(&self) -> i32 {
2037 self.get_value(Self::undo_limit_property())
2038 }
2039
2040 pub fn set_undo_limit(&self, value: i32) {
2041 self.set_value(Self::undo_limit_property(), value)
2042 }
2043
2044 fn on_undo_limit_changed(&self, new_value: i32) {
2045 self.undo_redo_helper.set_limit(new_value);
2046
2047 self.clear_undo_redo();
2049 }
2050
2051 pub fn can_undo(&self) -> bool {
2053 self.can_undo.get()
2054 }
2055
2056 fn set_can_undo(&self, value: bool) {
2057 self.set_and_raise_cell(Self::can_undo_property(), &self.can_undo, value);
2058 }
2059
2060 pub fn can_redo(&self) -> bool {
2062 self.can_redo.get()
2063 }
2064
2065 fn set_can_redo(&self, value: bool) {
2066 self.set_and_raise_cell(Self::can_redo_property(), &self.can_redo, value);
2067 }
2068
2069 pub fn get_line_count(&self) -> i32 {
2076 match self.presenter() {
2077 Some(presenter) => presenter.text_layout().text_lines().len() as i32,
2078 None => -1,
2079 }
2080 }
2081
2082 pub fn copying_to_clipboard(
2084 &self,
2085 handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static,
2086 ) -> RoutedEventHandlerToken {
2087 self.add_handler(Self::copying_to_clipboard_event(), handler)
2088 }
2089
2090 pub fn cutting_to_clipboard(
2092 &self,
2093 handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static,
2094 ) -> RoutedEventHandlerToken {
2095 self.add_handler(Self::cutting_to_clipboard_event(), handler)
2096 }
2097
2098 pub fn pasting_from_clipboard(
2105 &self,
2106 handler: impl Fn(&Interactive, &dyn IRoutedEventArgs) + 'static,
2107 ) -> RoutedEventHandlerToken {
2108 self.add_route_handler(
2109 Self::pasting_from_clipboard_event(),
2110 Rc::new(handler),
2111 Interactive::DEFAULT_ROUTES,
2112 false,
2113 )
2114 }
2115
2116 pub fn text_changed(
2119 &self,
2120 handler: impl Fn(&Interactive, &TextChangedEventArgs) + 'static,
2121 ) -> RoutedEventHandlerToken {
2122 self.add_handler(Self::text_changed_event(), handler)
2123 }
2124
2125 pub fn text_changing(
2131 &self,
2132 handler: impl Fn(&Interactive, &TextChangingEventArgs) + 'static,
2133 ) -> RoutedEventHandlerToken {
2134 self.add_handler(Self::text_changing_event(), handler)
2135 }
2136
2137 fn presenter(&self) -> Option<Ref<TextPresenter>> {
2138 self.presenter.borrow().clone()
2139 }
2140
2141 fn scroll_viewer(&self) -> Option<Ref<ScrollViewer>> {
2142 self.scroll_viewer.borrow().clone()
2143 }
2144
2145 fn text_utf16(&self) -> Vec<u16> {
2147 self.text().map(|text| text.encode_utf16().collect()).unwrap_or_default()
2148 }
2149
2150 fn text_length(&self) -> i32 {
2153 self.text().map_or(0, |text| utf16_length(&text))
2154 }
2155
2156 fn touch_radius(&self) -> i32 {
2157 *self.touch_radius.get_or_init(|| {
2158 let height = FerroLocator::current()
2159 .get_service::<dyn IPlatformSettings>()
2160 .map_or(10.0, |settings| settings.get_tap_size(PointerType::Touch).height);
2161
2162 (height / 2.0) as i32 + 5
2163 })
2164 }
2165
2166 fn subscribe_presenter_property_changes(&self, presenter: Option<&Ref<TextPresenter>>) {
2167 let previous = self.presenter_property_changed.borrow_mut().take();
2168 if let Some(previous) = previous {
2169 previous.dispose();
2170 }
2171 if let Some(presenter) = presenter {
2172 let weak = self.to_ref().downgrade();
2173 let subscription = presenter.property_changed(move |e| {
2174 if let Some(this) = weak.upgrade() {
2175 this.presenter_property_changed(e);
2176 }
2177 });
2178 *self.presenter_property_changed.borrow_mut() = Some(subscription);
2179 }
2180 }
2181
2182 fn presenter_property_changed(&self, e: &FerroPropertyChangedEventArgs<'_>) {
2183 if e.property() == TextPresenter::preedit_text_property().as_property() {
2184 let (old_value, new_value) = e.get_old_and_new_value::<Option<String>>();
2185
2186 let has_preedit = new_value.is_some_and(|new_value| !new_value.is_empty());
2187 if old_value.is_none_or(|old_value| old_value.is_empty()) && has_preedit {
2188 self.delete_selection_with_kind(TextMutationKind::InternalSynchronization);
2192 }
2193 self.update_pseudoclasses();
2197 }
2198 }
2199
2200 fn update_command_states(&self) {
2201 let has_selection = self.has_selection();
2202 self.set_can_copy(!self.is_password_box() && has_selection);
2203 self.set_can_cut(!self.is_password_box() && has_selection && !self.is_read_only());
2204 self.set_can_paste(!self.is_read_only());
2205 }
2206
2207 pub(crate) fn handle_text_input(&self, input: Option<&str>) {
2209 if self.is_read_only() {
2210 return;
2211 }
2212
2213 let Some(mut input) = self.sanitize_input_text(input).filter(|input| !input.is_empty()) else {
2214 return;
2215 };
2216
2217 self.selected_text_changes_made_since_last_undo_snapshot
2218 .set(self.selected_text_changes_made_since_last_undo_snapshot.get() + 1);
2219 self.snapshot_undo_redo(false);
2220
2221 let mut text = self.text_utf16();
2222 let selection_length = (self.selection_start() - self.selection_end()).abs();
2223 let new_length = input.len() as i32 + text.len() as i32 - selection_length;
2224
2225 let max_length = self.max_length();
2226
2227 if max_length > 0 && new_length > max_length {
2228 let length = (input.len() as i32 - (new_length - max_length)).max(0);
2229 input.truncate(length as usize);
2230 }
2231
2232 if !input.is_empty() {
2233 let mut caret_index = self.caret_index();
2234
2235 if selection_length != 0 {
2236 let (start, _) = self.get_selection_range();
2237
2238 remove_range(&mut text, start, selection_length);
2239
2240 caret_index = start;
2241 }
2242
2243 let insert_at = (caret_index.max(0) as usize).min(text.len());
2244 let input_length = input.len() as i32;
2245
2246 text.splice(insert_at..insert_at, input);
2247
2248 let text = String::from_utf16_lossy(&text);
2249
2250 if !self.set_text_from_range_edit(Some(text.clone()), insert_at as i32..insert_at as i32 + selection_length, input_length) {
2251 return;
2252 }
2253
2254 self.clear_selection();
2255
2256 if self.is_undo_enabled() {
2257 self.undo_redo_helper.discard_redo();
2258 }
2259
2260 let presenter = self.presenter();
2261
2262 if let Some(presenter) = &presenter {
2264 presenter.set_current_value(TextPresenter::text_property(), Some(text));
2265 }
2266
2267 caret_index += input_length;
2268
2269 if let Some(presenter) = &presenter {
2271 presenter.move_caret_to_text_position(caret_index, false);
2272 }
2273
2274 self.set_current_value(Self::caret_index_property(), caret_index);
2275 }
2276 }
2277
2278 fn sanitize_input_text(&self, text: Option<&str>) -> Option<Vec<u16>> {
2282 let mut text: Vec<u16> = text?.encode_utf16().collect();
2283
2284 if !self.accepts_return() {
2285 let mut line_break_start = 0;
2286 let mut grapheme_enumerator = GraphemeEnumerator::new(&text);
2287
2288 while let Some(grapheme) = grapheme_enumerator.move_next() {
2289 if grapheme.first_codepoint().is_break_char() {
2290 break;
2291 }
2292
2293 line_break_start += grapheme.length();
2294 }
2295
2296 text.truncate(line_break_start);
2298 }
2299
2300 text.retain(|unit| !INVALID_CHARACTERS.contains(unit));
2301
2302 Some(text)
2303 }
2304
2305 pub fn cut(&self) {
2315 let text = self.get_selection();
2316
2317 if text.is_empty() {
2318 return;
2319 }
2320
2321 let event_args = RoutedEventArgs::with_event(Self::cutting_to_clipboard_event());
2322 self.raise_event(&event_args);
2323 if !event_args.handled() {
2324 self.snapshot_undo_redo(true);
2325
2326 let Some(clipboard) = TopLevel::get_top_level(Some(self)).and_then(|top_level| top_level.clipboard())
2327 else {
2328 return;
2329 };
2330
2331 let this = self.to_ref();
2332
2333 ClipboardHelper::start(async move {
2334 match clipboard.set_text_async(Some(&text)).await {
2335 Ok(()) => {}
2336 Err(ex) if ClipboardHelper::is_expected_clipboard_exception(&ex) => {
2337 if let Some(logger) = Logger::try_get(LogEventLevel::Warning, LogArea::CONTROL) {
2338 let source: &dyn Any = &this;
2339 logger.log_with_values(Some(source), "Failed to write text to clipboard: {Error}", &[&ex]);
2340 }
2341 return Ok(());
2342 }
2343 Err(ex) => return Err(ex),
2344 }
2345
2346 this.delete_selection();
2347 Ok(())
2348 });
2349 }
2350 }
2351
2352 pub fn copy(&self) {
2356 let text = self.get_selection();
2357
2358 if text.is_empty() {
2359 return;
2360 }
2361
2362 let event_args = RoutedEventArgs::with_event(Self::copying_to_clipboard_event());
2363 self.raise_event(&event_args);
2364 if !event_args.handled() {
2365 let Some(clipboard) = TopLevel::get_top_level(Some(self)).and_then(|top_level| top_level.clipboard())
2366 else {
2367 return;
2368 };
2369
2370 let this = self.to_ref();
2371
2372 ClipboardHelper::start(async move {
2373 match clipboard.set_text_async(Some(&text)).await {
2374 Ok(()) => {}
2375 Err(ex) if ClipboardHelper::is_expected_clipboard_exception(&ex) => {
2376 if let Some(logger) = Logger::try_get(LogEventLevel::Warning, LogArea::CONTROL) {
2377 let source: &dyn Any = &this;
2378 logger.log_with_values(Some(source), "Failed to write text to clipboard: {Error}", &[&ex]);
2379 }
2380 }
2381 Err(ex) => return Err(ex),
2382 }
2383
2384 Ok(())
2385 });
2386 }
2387 }
2388
2389 pub fn paste(&self) {
2393 let pasted = self.paste_core_async(TopLevel::get_top_level(Some(self)).and_then(|top_level| top_level.clipboard()));
2394
2395 ClipboardHelper::start(async move {
2396 match pasted.await {
2397 Ok(result) => result,
2398 Err(_) => Ok(()),
2400 }
2401 });
2402 }
2403
2404 fn paste_core_async(&self, clipboard: Option<Rc<dyn IClipboard>>) -> DispatcherTask<Result<(), ClipboardError>> {
2411 let this = self.to_ref();
2412
2413 Dispatcher::ui_thread().to_task_scheduler().start_local(async move {
2414 let event_args =
2415 PastingFromClipboardEventArgs::new(Self::pasting_from_clipboard_event(), clipboard.clone());
2416 this.raise_event(&event_args);
2417
2418 let Some(clipboard) = clipboard.filter(|_| !event_args.handled()) else {
2419 return Ok(());
2420 };
2421
2422 let mut text: Option<String> = None;
2423
2424 match clipboard.try_get_text_async().await {
2425 Ok(value) => text = value,
2426 Err(ex) if ClipboardHelper::is_expected_clipboard_exception(&ex) => {
2427 if let Some(logger) = Logger::try_get(LogEventLevel::Warning, LogArea::CONTROL) {
2428 let source: &dyn Any = &this;
2429 logger.log_with_values(Some(source), "Failed to read text from clipboard: {Error}", &[&ex]);
2430 }
2431 }
2432 Err(ex) => return Err(ex),
2433 }
2434
2435 let Some(text) = text.filter(|text| !text.is_empty()) else {
2436 return Ok(());
2437 };
2438
2439 this.snapshot_undo_redo(true);
2440 this.handle_text_input(Some(&text));
2441 Ok(())
2442 })
2443 }
2444
2445 fn select_word(&self, text: &[u16], caret_index: i32, mut selection_start: i32, mut selection_end: i32) {
2446 if self.is_password_box() {
2447 self.word_selection_start.set(-1);
2449
2450 self.select_all();
2451 }
2452
2453 if !StringUtils::is_start_of_word(text, caret_index) {
2454 selection_start = StringUtils::previous_word(text, caret_index);
2455 }
2456
2457 if !StringUtils::is_end_of_word(text, caret_index) {
2458 selection_end = StringUtils::next_word(text, caret_index);
2459 }
2460
2461 if selection_start != selection_end {
2462 self.word_selection_start.set(selection_start);
2463 }
2464
2465 self.set_current_value(Self::selection_start_property(), selection_start);
2466 self.set_current_value(Self::selection_end_property(), selection_end);
2467 }
2468
2469 fn update_word_selection_range(&self, caret_index: i32, selection_start: &mut i32, selection_end: &mut i32) {
2470 let text = self.text_utf16();
2471
2472 if text.is_empty() {
2473 return;
2474 }
2475
2476 let word_selection_start = self.word_selection_start.get();
2477
2478 if caret_index > word_selection_start {
2479 let next_word = StringUtils::next_word(&text, caret_index);
2480
2481 *selection_end = next_word;
2482
2483 *selection_start = word_selection_start;
2484 } else {
2485 let previous_word = StringUtils::previous_word(&text, caret_index);
2486 *selection_start = previous_word;
2487
2488 *selection_end = StringUtils::next_word(&text, word_selection_start);
2489 }
2490 }
2491
2492 pub(crate) fn coerce_caret_index(sender: &FerroObject, value: i32) -> i32 {
2497 let text = match sender.downcast_ref::<SelectableTextBlock>() {
2498 Some(text_block) if text_block.has_complex_content() => {
2499 text_block.inlines().and_then(|inlines| inlines.text())
2500 }
2501 _ => sender.get_value(Self::text_property()),
2502 };
2503
2504 let Some(text) = text else {
2505 return 0;
2506 };
2507
2508 if value < 0 {
2509 return 0;
2510 }
2511
2512 let mut length = 0;
2513 let mut previous = None;
2514 let mut current = None;
2515
2516 for unit in text.encode_utf16() {
2517 if length == value - 1 {
2518 previous = Some(unit);
2519 } else if length == value {
2520 current = Some(unit);
2521 }
2522
2523 length += 1;
2524 }
2525
2526 if value > length {
2527 length
2528 } else if previous == Some(CARRIAGE_RETURN) && current == Some(LINE_FEED) {
2529 value + 1
2530 } else {
2531 value
2532 }
2533 }
2534
2535 pub fn clear(&self) {
2537 self.set_text_from_range_edit(Some(String::new()), 0..self.text_length(), 0);
2538 }
2539
2540 fn move_horizontal(&self, direction: i32, whole_word: bool, is_selecting: bool, move_caret_position: bool) {
2541 let Some(presenter) = self.presenter() else {
2542 return;
2543 };
2544
2545 let _change = self.im_client.begin_change();
2546
2547 let selection_start = self.selection_start();
2548 let selection_end = self.selection_end();
2549
2550 let logical_direction = if direction > 0 { LogicalDirection::Forward } else { LogicalDirection::Backward };
2551
2552 if !whole_word {
2553 if is_selecting {
2554 presenter.move_caret_to_text_position(selection_end, false);
2555
2556 presenter.move_caret_horizontal(logical_direction);
2557
2558 self.set_current_value(Self::selection_end_property(), presenter.caret_index());
2559 } else {
2560 if selection_start != selection_end {
2561 self.clear_selection_and_move_caret_to_text_position(logical_direction);
2562 } else {
2563 presenter.move_caret_horizontal(logical_direction);
2564 }
2565
2566 self.set_current_value(Self::caret_index_property(), presenter.caret_index());
2567 }
2568 } else {
2569 let text = self.text_utf16();
2570
2571 let offset = if direction > 0 {
2572 StringUtils::next_word(&text, selection_end) - selection_end
2573 } else {
2574 StringUtils::previous_word(&text, selection_end) - selection_end
2575 };
2576
2577 self.set_current_value(Self::selection_end_property(), self.selection_end() + offset);
2578
2579 if move_caret_position {
2580 presenter.move_caret_to_text_position(self.selection_end(), false);
2581 }
2582
2583 if !is_selecting && move_caret_position {
2584 self.set_current_value(Self::caret_index_property(), self.selection_end());
2585 } else {
2586 self.set_current_value(Self::selection_start_property(), selection_start);
2587 }
2588 }
2589 }
2590
2591 fn move_vertical(&self, direction: LogicalDirection, is_selecting: bool) {
2592 let Some(presenter) = self.presenter() else {
2593 return;
2594 };
2595
2596 if is_selecting {
2597 let old_caret_index = presenter.caret_index();
2598 presenter.move_caret_vertical(direction);
2599 let new_caret_index = presenter.caret_index();
2600
2601 if old_caret_index == new_caret_index {
2602 let text_length = self.text_length();
2603
2604 if direction == LogicalDirection::Forward && new_caret_index < text_length {
2607 presenter.move_caret_to_text_position(text_length, false);
2608 } else if direction == LogicalDirection::Backward && new_caret_index > 0 {
2609 presenter.move_caret_to_text_position(0, false);
2610 }
2611 }
2612
2613 self.set_current_value(Self::selection_end_property(), presenter.caret_index());
2614 } else {
2615 if self.selection_start() != self.selection_end() {
2616 self.clear_selection_and_move_caret_to_text_position(direction);
2617 }
2618
2619 presenter.move_caret_vertical(direction);
2620
2621 self.set_current_value(Self::caret_index_property(), presenter.caret_index());
2622 }
2623 }
2624
2625 fn move_home(&self, document: bool) {
2626 let Some(presenter) = self.presenter() else {
2627 return;
2628 };
2629
2630 let caret_index = self.caret_index();
2631
2632 if document {
2633 presenter.move_caret_to_text_position(0, false);
2634 } else {
2635 let text_layout = presenter.text_layout();
2636 let line_index = text_layout.get_line_index_from_character_index(caret_index, false);
2637 let text_position = text_layout.text_lines()[line_index as usize].first_text_source_index();
2638
2639 presenter.move_caret_to_text_position(text_position, false);
2640 }
2641 }
2642
2643 fn move_end(&self, document: bool) {
2644 let Some(presenter) = self.presenter() else {
2645 return;
2646 };
2647
2648 let caret_index = self.caret_index();
2649
2650 if document {
2651 presenter.move_caret_to_text_position(self.text_length(), true);
2652 } else {
2653 let text_layout = presenter.text_layout();
2654 let line_index = text_layout.get_line_index_from_character_index(caret_index, false);
2655 let text_line = &text_layout.text_lines()[line_index as usize];
2656
2657 let text_position = text_line.first_text_source_index() + text_line.length() - text_line.new_line_length();
2658
2659 presenter.move_caret_to_text_position(text_position, true);
2660 }
2661 }
2662
2663 fn move_page_right(&self) {
2664 if let Some(scroll_viewer) = self.scroll_viewer() {
2665 scroll_viewer.page_right();
2666 }
2667 }
2668
2669 fn move_page_left(&self) {
2670 if let Some(scroll_viewer) = self.scroll_viewer() {
2671 scroll_viewer.page_left();
2672 }
2673 }
2674
2675 fn move_page_up(&self) {
2676 if let Some(scroll_viewer) = self.scroll_viewer() {
2677 scroll_viewer.page_up();
2678 }
2679 }
2680
2681 fn move_page_down(&self) {
2682 if let Some(scroll_viewer) = self.scroll_viewer() {
2683 scroll_viewer.page_down();
2684 }
2685 }
2686
2687 fn clear_selection_and_move_caret_to_text_position(&self, direction: LogicalDirection) {
2688 let new_position = if direction == LogicalDirection::Forward {
2689 self.selection_start().max(self.selection_end())
2690 } else {
2691 self.selection_start().min(self.selection_end())
2692 };
2693 self.set_current_value(Self::selection_start_property(), new_position);
2694 self.set_current_value(Self::selection_end_property(), new_position);
2695 if let Some(presenter) = self.presenter() {
2697 presenter.move_caret_to_text_position(new_position, false);
2698 }
2699 }
2700
2701 pub fn scroll_to_line(&self, line_index: i32) {
2708 let Some(presenter) = self.presenter() else {
2709 return;
2710 };
2711
2712 let text_layout = presenter.text_layout();
2713
2714 if line_index < 0 || line_index >= text_layout.text_lines().len() as i32 {
2715 panic!("Specified argument was out of the range of valid values. (Parameter 'lineIndex')");
2716 }
2717
2718 let text_position = text_layout.text_lines()[line_index as usize].first_text_source_index();
2719 presenter.move_caret_to_text_position(text_position, false);
2720 }
2721
2722 pub fn select_all(&self) {
2724 let _change = self.im_client.begin_change();
2725
2726 self.set_current_value(Self::selection_start_property(), 0);
2727 self.set_current_value(Self::selection_end_property(), self.text_length());
2728 }
2729
2730 fn get_selection_range(&self) -> (i32, i32) {
2731 let selection_start = self.selection_start();
2732 let selection_end = self.selection_end();
2733
2734 (selection_start.min(selection_end), selection_start.max(selection_end))
2735 }
2736
2737 pub(crate) fn delete_selection(&self) -> bool {
2740 self.delete_selection_with_kind(TextMutationKind::Edit)
2741 }
2742
2743 pub(crate) fn prepare_preedit_selection(&self, owns_update: impl Fn() -> bool) -> Result<u64, PreeditSelectionError> {
2747 if !owns_update() {
2748 return Err(PreeditSelectionError::Superseded);
2749 }
2750 if self.is_read_only() {
2751 return Ok(self.text_change_generation());
2752 }
2753 let change = self.im_client.begin_change();
2754 let (start, end) = self.get_selection_range();
2755 let selection = || (self.caret_index(), self.selection_start(), self.selection_end());
2756 let original_selection = selection();
2757 if start != end {
2758 let mut text = self.text_utf16();
2759 remove_range(&mut text, start, end - start);
2760 if !self.set_text_from_range_change(
2761 Some(String::from_utf16_lossy(&text)), start..end, 0,
2762 TextMutationKind::InternalSynchronization,
2763 ) {
2764 return Err(PreeditSelectionError::TextChanged);
2765 }
2766 let caret = Self::coerce_caret_index(self, original_selection.0);
2771 let expected = if caret != original_selection.0 {
2772 (caret, caret, caret)
2773 } else {
2774 let start = Self::coerce_caret_index(self, original_selection.1);
2775 let end = Self::coerce_caret_index(self, original_selection.2);
2776 (if start == end { start } else { caret }, start, end)
2777 };
2778 if selection() != expected {
2779 return Err(PreeditSelectionError::SelectionChanged);
2780 }
2781 }
2782 let generation = self.text_change_generation();
2783 let check_current = || {
2784 if !owns_update() { Err(PreeditSelectionError::Superseded) }
2785 else if self.text_change_generation() != generation { Err(PreeditSelectionError::TextChanged) }
2786 else { Ok(()) }
2787 };
2788 check_current()?;
2789 let caret = Self::coerce_caret_index(self, start);
2790 let previous_caret = self.caret_index();
2791 let previous_end = self.selection_end();
2792 self.set_current_value(Self::caret_index_property(), caret);
2793 check_current()?;
2794 if self.caret_index() != caret {
2795 return Err(PreeditSelectionError::SelectionChanged);
2796 }
2797 if previous_caret == caret {
2798 self.set_current_value(Self::selection_start_property(), caret);
2801 check_current()?;
2802 if selection() != (caret, caret, previous_end) {
2803 return Err(PreeditSelectionError::SelectionChanged);
2804 }
2805 self.set_current_value(Self::selection_end_property(), caret);
2806 }
2807 drop(change);
2810 check_current()?;
2811 if selection() != (caret, caret, caret) {
2812 Err(PreeditSelectionError::SelectionChanged)
2813 } else {
2814 Ok(generation)
2815 }
2816 }
2817
2818 fn delete_selection_with_kind(&self, mutation_kind: TextMutationKind) -> bool {
2819 if self.is_read_only() {
2820 return true;
2821 }
2822
2823 let _change = self.im_client.begin_change();
2824
2825 let (start, end) = self.get_selection_range();
2826
2827 if start != end {
2828 let mut text = self.text_utf16();
2829
2830 remove_range(&mut text, start, end - start);
2831
2832 if !self.set_text_from_range_change(Some(String::from_utf16_lossy(&text)), start..end, 0, mutation_kind) {
2833 return true;
2834 }
2835
2836 if let Some(presenter) = self.presenter() {
2837 presenter.move_caret_to_text_position(start, false);
2838 }
2839
2840 self.set_current_value(Self::selection_start_property(), start);
2841
2842 self.clear_selection();
2843
2844 return true;
2845 }
2846
2847 self.set_current_value(Self::caret_index_property(), self.selection_start());
2848
2849 false
2850 }
2851
2852 fn has_selection(&self) -> bool {
2855 let (start, end) = self.get_selection_range();
2856
2857 if start == end {
2858 return false;
2859 }
2860
2861 let text_length = self.text_length();
2862
2863 text_length > 0 && end <= text_length
2864 }
2865
2866 fn get_selection(&self) -> String {
2867 let Some(text) = self.text().filter(|text| !text.is_empty()) else {
2868 return String::new();
2869 };
2870
2871 let selection_start = self.selection_start();
2872 let selection_end = self.selection_end();
2873 let start = selection_start.min(selection_end);
2874 let end = selection_start.max(selection_end);
2875
2876 if start == end || utf16_length(&text) < end {
2877 return String::new();
2878 }
2879
2880 let selected_text: Vec<u16> =
2883 text.encode_utf16().skip(start.max(0) as usize).take((end - start) as usize).collect();
2884
2885 String::from_utf16_lossy(&selected_text)
2886 }
2887
2888 pub(crate) fn set_text_from_edit(&self, value: Option<String>) {
2890 self.set_text_core(value, TextMutationKind::Edit)
2891 }
2892
2893 fn set_text_from_range_edit(&self, value: Option<String>, replaced: std::ops::Range<i32>, inserted_length: i32) -> bool {
2894 self.set_text_from_range_change(value, replaced, inserted_length, TextMutationKind::Edit)
2895 }
2896
2897 fn set_text_from_range_change(&self, value: Option<String>, replaced: std::ops::Range<i32>, inserted_length: i32, mutation_kind: TextMutationKind) -> bool {
2898 let generation = self.text_change_generation.get();
2899 let change = TextBoxTextChange::new(self.text().unwrap_or_default(), replaced, inserted_length);
2900 let previous = self.native_text_change.replace(Some(change));
2901 struct Restore<'a>(&'a RefCell<Option<TextBoxTextChange>>, Option<TextBoxTextChange>);
2902 impl Drop for Restore<'_> {
2903 fn drop(&mut self) { self.0.replace(self.1.take()); }
2904 }
2905 let _restore = Restore(&self.native_text_change, previous);
2906 self.set_text_core(value.clone(), mutation_kind);
2907 self.text_change_generation.get().wrapping_sub(generation) <= 1 && self.text() == value
2910 }
2911
2912 pub(crate) fn text_change_generation(&self) -> u64 { self.text_change_generation.get() }
2913
2914 pub(crate) fn set_text_from_internal_synchronization(&self, value: Option<String>) {
2917 self.set_text_core(value, TextMutationKind::InternalSynchronization)
2918 }
2919
2920 fn set_text_core(&self, value: Option<String>, mutation_kind: TextMutationKind) {
2921 let previous_mutation_kind = self.text_mutation_kind.replace(mutation_kind);
2923
2924 struct Restore<'a>(&'a Cell<TextMutationKind>, TextMutationKind);
2926
2927 impl Drop for Restore<'_> {
2928 fn drop(&mut self) {
2929 self.0.set(self.1);
2930 }
2931 }
2932
2933 let _restore = Restore(&self.text_mutation_kind, previous_mutation_kind);
2934
2935 self.set_current_value(Self::text_property(), value);
2936 }
2937
2938 fn get_vertical_space_between_scroll_viewer_and_presenter(&self) -> f64 {
2941 let mut vertical_space = 0.0;
2942
2943 if let Some(presenter) = self.presenter() {
2944 let scroll_viewer: Option<Ref<Visual>> = self.scroll_viewer().map(Ref::upcast);
2945 let this: Ref<Visual> = self.to_ref().upcast();
2946 let mut visual: Option<Ref<Visual>> = Some(presenter.upcast());
2947
2948 while let Some(current) = visual.filter(|visual| *visual != this) {
2949 if Some(¤t) == scroll_viewer.as_ref() {
2950 let padding: Thickness = current.get_value(Decorator::padding_property());
2952 vertical_space += padding.top + padding.bottom;
2953 break;
2954 }
2955
2956 let margin: Thickness = current.get_value(Layoutable::margin_property());
2957 let padding: Thickness = current.get_value(Decorator::padding_property());
2958
2959 vertical_space += margin.top + padding.top + padding.bottom + margin.bottom;
2960
2961 visual = current.visual_parent();
2962 }
2963 }
2964
2965 vertical_space
2966 }
2967
2968 fn create_lines_text_layout(&self, lines: i32) -> TextLayout {
2970 let font_size = self.font_size();
2971 let typeface =
2972 Typeface::with_style(self.font_family(), self.font_style(), self.font_weight(), self.font_stretch());
2973
2974 let text_run_style: Rc<dyn TextRunProperties> = Rc::new(GenericTextRunProperties::with_all(
2975 typeface,
2976 font_size,
2977 None,
2978 None,
2979 None,
2980 BaselineAlignment::Baseline,
2981 None,
2982 self.font_features(),
2983 ));
2984
2985 let paragraph_properties: Rc<dyn TextParagraphProperties> = Rc::new(GenericTextParagraphProperties::with_all(
2986 FlowDirection::LeftToRight,
2987 TextAlignment::Left,
2988 true,
2989 false,
2990 text_run_style,
2991 TextWrapping::NoWrap,
2992 self.line_height(),
2993 0.0,
2994 0.0,
2995 ));
2996
2997 TextLayout::from_text_source(
2998 Rc::new(LineTextSource { lines }),
2999 paragraph_properties,
3000 None,
3001 f64::INFINITY,
3002 f64::INFINITY,
3003 0,
3004 None,
3005 )
3006 }
3007
3008 fn raise_text_change_events(&self, previous: &str, next: &str) {
3012 let change = self.native_text_change.borrow().as_ref()
3018 .filter(|change| change.matches(previous, next)).cloned();
3019 let text_changing_event_args = TextChangingEventArgs::with_event(Self::text_changing_event()).with_text_change(change);
3020 self.raise_event(&text_changing_event_args);
3021
3022 let this = self.to_ref();
3023
3024 Dispatcher::ui_thread().post_local(
3025 move || {
3026 let text_changed_event_args = TextChangedEventArgs::with_event(Self::text_changed_event());
3027 this.raise_event(&text_changed_event_args);
3028 },
3029 DispatcherPriority::NORMAL,
3030 );
3031 }
3032
3033 fn set_selection_for_control_backspace(&self) {
3034 let text = self.text_utf16();
3035 let selection_start = self.caret_index();
3036
3037 let _change = self.im_client.begin_change();
3038
3039 self.move_horizontal(-1, true, false, false);
3040
3041 if self.selection_end() > 0
3042 && selection_start < text.len() as i32
3043 && text.get(selection_start.max(0) as usize) == Some(&SPACE)
3044 {
3045 self.set_current_value(Self::selection_end_property(), self.selection_end() - 1);
3046 }
3047
3048 self.set_current_value(Self::selection_start_property(), selection_start);
3049 }
3050
3051 fn set_selection_for_control_delete(&self) {
3052 let text = self.text_utf16();
3053 let text_length = text.len() as i32;
3054 if self.presenter().is_none() || text_length == 0 {
3055 return;
3056 }
3057
3058 let _change = self.im_client.begin_change();
3059
3060 self.set_current_value(Self::selection_start_property(), self.caret_index());
3061
3062 self.move_horizontal(1, true, true, false);
3063
3064 let selection_end = self.selection_end();
3065
3066 if selection_end < text_length && text.get(selection_end.max(0) as usize) == Some(&SPACE) {
3067 self.set_current_value(Self::selection_end_property(), selection_end + 1);
3068 }
3069 }
3070
3071 fn update_pseudoclasses(&self) {
3072 let has_preedit = self.presenter().is_some_and(|presenter| {
3073 presenter.preedit_text().is_some_and(|preedit| !preedit.is_empty())
3074 });
3075 self.pseudo_classes().set(":empty", !has_preedit && self.text().is_none_or(|text| text.is_empty()));
3076 self.pseudo_classes().set(":touch-mode", self.is_in_touch_mode.get());
3077 }
3078
3079 fn is_password_box(&self) -> bool {
3080 self.password_char() != '\0' && !self.reveal_password()
3081 }
3082
3083 fn snapshot_undo_redo(&self, ignore_change_count: bool) {
3084 if self.is_undo_enabled()
3085 && (ignore_change_count
3086 || !self.has_done_snapshot_once.get()
3087 || self.selected_text_changes_made_since_last_undo_snapshot.get() >= MAX_CHARS_BEFORE_UNDO_SNAPSHOT)
3088 {
3089 self.undo_redo_helper.snapshot();
3090 self.selected_text_changes_made_since_last_undo_snapshot.set(0);
3091 self.has_done_snapshot_once.set(true);
3092 }
3093 }
3094
3095 fn clear_undo_redo(&self) {
3096 self.undo_redo_helper.clear();
3097 self.selected_text_changes_made_since_last_undo_snapshot.set(0);
3098 self.has_done_snapshot_once.set(false);
3099 }
3100
3101 pub fn undo(&self) {
3103 if self.is_undo_enabled() && self.can_undo() {
3104 self.snapshot_undo_redo(true);
3107
3108 let _undoing_redoing = UndoingRedoing::enter(&self.is_undoing_redoing);
3109 self.undo_redo_helper.undo();
3110 }
3111 }
3112
3113 pub fn redo(&self) {
3115 if self.is_undo_enabled() && self.can_redo() {
3116 let _undoing_redoing = UndoingRedoing::enter(&self.is_undoing_redoing);
3117 self.undo_redo_helper.redo();
3118 }
3119 }
3120}
3121
3122struct UndoingRedoing<'a>(&'a Cell<bool>);
3124
3125impl<'a> UndoingRedoing<'a> {
3126 fn enter(flag: &'a Cell<bool>) -> Self {
3127 flag.set(true);
3128 Self(flag)
3129 }
3130}
3131
3132impl Drop for UndoingRedoing<'_> {
3133 fn drop(&mut self) {
3134 self.0.set(false);
3135 }
3136}
3137
3138fn environment_new_line() -> &'static str {
3140 if cfg!(windows) {
3141 "\r\n"
3142 } else {
3143 "\n"
3144 }
3145}
3146
3147fn utf16_length(text: &str) -> i32 {
3149 text.encode_utf16().count() as i32
3150}
3151
3152fn remove_range(text: &mut Vec<u16>, start: i32, length: i32) {
3154 let start = (start.max(0) as usize).min(text.len());
3155 let end = (start + length.max(0) as usize).min(text.len());
3156
3157 text.drain(start..end);
3158}
3159
3160fn is_captured_by(e: &PointerEventArgs, presenter: &Ref<TextPresenter>) -> bool {
3162 e.pointer().captured().is_some_and(|captured| captured == *presenter)
3163}
3164
3165fn clamp_to_bounds(point: Point, presenter: &TextPresenter) -> Point {
3167 let bounds = presenter.bounds();
3168
3169 Point::new(
3170 MathUtilities::clamp(point.x, 0.0, (bounds.width - 1.0).max(0.0)),
3171 MathUtilities::clamp(point.y, 0.0, (bounds.height - 1.0).max(0.0)),
3172 )
3173}