1use std::collections::HashMap;
9use std::fmt;
10
11use inset_embedder::{
12 BoxHeightStyle, BoxWidthStyle, Clip, Color, FontCollection, LineMetrics, Matrix4, Offset,
13 Paint, Radius, Rect, Size, TargetPlatform, TextAffinity, TextAlign, TextBaseline,
14 TextDirection, TextHeightBehavior, TextPosition, TextRange, TextSelection, clamp_double,
15};
16use inset_foundation::{App, Handle, Listenable, Listener, ValueListenable, ValueNotifier};
17use inset_gestures::TapDownDetails;
18use inset_painting::{
19 ClipContext, InlineSpanRef, PaintingBinding, PlaceholderDimensions, TextPainter, TextScaler,
20 TextWidthBasis,
21};
22use inset_services::{AnyTextSelectionDelegate, SelectionChangedCause, TextLayoutMetrics};
23
24use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult, RenderBox, RenderBoxData};
25use crate::layer::{ClipRectLayer, ContainerLayer, LayerHandle, LayerLink, LeaderLayer};
26use crate::layout_helper::ChildLayoutHelper;
27use crate::object::{
28 AnyRenderObject, ContainerRenderObjectData, ContainerRenderObjectMixin, RenderHandle,
29 RenderObject, RenderObjectData,
30};
31use crate::painting_context::PaintingContext;
32use crate::paragraph::{RenderInlineChildrenContainerDefaults, TextParentData};
33use crate::pipeline_owner::PipelineOwner;
34use crate::relayout_when_system_fonts_change::{
35 RelayoutWhenSystemFontsChangeData, RelayoutWhenSystemFontsChangeMixin,
36};
37use crate::text_boundary::WordBoundary;
38use crate::viewport_offset::AnyViewportOffset;
39
40const K_CARET_GAP: f64 = 1.0;
41const K_CARET_HEIGHT_OFFSET: f64 = 2.0;
42
43#[derive(Clone, Copy, Debug, PartialEq)]
47pub struct TextSelectionPoint {
48 pub point: Offset,
51 pub direction: Option<TextDirection>,
53}
54
55impl TextSelectionPoint {
56 pub const fn new(point: Offset, direction: Option<TextDirection>) -> TextSelectionPoint {
58 TextSelectionPoint { point, direction }
59 }
60}
61
62impl fmt::Display for TextSelectionPoint {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self.direction {
65 Some(TextDirection::Ltr) => write!(f, "{:?}-ltr", self.point),
66 Some(TextDirection::Rtl) => write!(f, "{:?}-rtl", self.point),
67 None => write!(f, "{:?}", self.point),
68 }
69 }
70}
71
72pub struct VerticalCaretMovementRun {
77 editable: RenderHandle<RenderEditable>,
78 layout_generation: u64,
79 current_offset: Offset,
80 current_line: i32,
81 current_text_position: TextPosition,
82 line_metrics: Vec<LineMetrics>,
83 position_cache: HashMap<i32, (Offset, TextPosition)>,
84 is_valid: bool,
85}
86
87impl VerticalCaretMovementRun {
88 pub fn is_valid(&mut self, app: &App) -> bool {
90 if !self.is_valid {
91 return false;
92 }
93 if self.editable.get(app).layout_generation != self.layout_generation {
94 self.is_valid = false;
95 }
96 self.is_valid
97 }
98
99 pub fn current(&self) -> TextPosition {
101 debug_assert!(self.is_valid);
102 self.current_text_position
103 }
104
105 fn get_text_position_for_line(
106 &mut self,
107 app: &App,
108 line_number: i32,
109 ) -> (Offset, TextPosition) {
110 debug_assert!(self.is_valid);
111 debug_assert!(line_number >= 0);
112 if let Some(cached) = self.position_cache.get(&line_number) {
113 return *cached;
114 }
115 debug_assert!(line_number != self.current_line);
116 let new_offset = Offset::new(
117 self.current_offset.dx(),
118 self.line_metrics[line_number as usize].baseline,
119 );
120 let closest = self
121 .editable
122 .get(app)
123 .text_painter
124 .get_position_for_offset(new_offset);
125 self.position_cache
126 .insert(line_number, (new_offset, closest));
127 (new_offset, closest)
128 }
129
130 pub fn move_next(&mut self, app: &App) -> bool {
132 debug_assert!(self.is_valid(app));
133 if self.current_line + 1 >= self.line_metrics.len() as i32 {
134 return false;
135 }
136 let (offset, position) = self.get_text_position_for_line(app, self.current_line + 1);
137 self.current_line += 1;
138 self.current_offset = offset;
139 self.current_text_position = position;
140 true
141 }
142
143 pub fn move_previous(&mut self, app: &App) -> bool {
145 debug_assert!(self.is_valid(app));
146 if self.current_line <= 0 {
147 return false;
148 }
149 let (offset, position) = self.get_text_position_for_line(app, self.current_line - 1);
150 self.current_line -= 1;
151 self.current_offset = offset;
152 self.current_text_position = position;
153 true
154 }
155
156 pub fn move_by_offset(&mut self, app: &App, offset: f64) -> bool {
158 let initial_offset = self.current_offset;
159 if offset >= 0.0 {
160 while self.current_offset.dy() < initial_offset.dy() + offset {
161 if !self.move_next(app) {
162 break;
163 }
164 }
165 } else {
166 while self.current_offset.dy() > initial_offset.dy() + offset {
167 if !self.move_previous(app) {
168 break;
169 }
170 }
171 }
172 initial_offset != self.current_offset
173 }
174}
175
176pub struct RenderEditable {
183 render_object: RenderObjectData,
184 render_box: RenderBoxData,
185 container: ContainerRenderObjectData<AnyRenderBox>,
186 text_painter: TextPainter,
187 text_intrinsics: Option<TextPainter>,
188 placeholder_dimensions: Option<Vec<PlaceholderDimensions>>,
194 system_fonts: RelayoutWhenSystemFontsChangeData,
195 fonts_override: Option<Handle<FontCollection>>,
196 show_cursor: Handle<ValueNotifier<bool>>,
197 #[allow(dead_code)]
198 dispose_show_cursor: bool,
199 has_focus: bool,
200 start_handle_layer_link: Handle<LayerLink>,
201 end_handle_layer_link: Handle<LayerLink>,
202 max_lines: Option<i32>,
203 min_lines: Option<i32>,
204 expands: bool,
205 selection_color: Option<Color>,
206 selection: Option<TextSelection>,
207 offset: AnyViewportOffset,
208 pub ignore_pointer: bool,
211 read_only: bool,
212 force_line: bool,
213 obscuring_character: String,
214 obscure_text: bool,
215 cursor_color: Option<Color>,
216 background_cursor_color: Option<Color>,
217 cursor_width: f64,
218 cursor_height: Option<f64>,
219 cursor_radius: Option<Radius>,
220 paint_cursor_above_text: bool,
221 cursor_offset: Offset,
222 device_pixel_ratio: f64,
223 selection_height_style: BoxHeightStyle,
224 selection_width_style: BoxWidthStyle,
225 enable_interactive_selection: Option<bool>,
226 prompt_rect_range: Option<TextRange>,
227 prompt_rect_color: Option<Color>,
228 clip_behavior: Clip,
229 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
230 leader_layer_handler: LayerHandle<Handle<LeaderLayer>>,
231 text_selection_delegate: AnyTextSelectionDelegate,
232 caret_prototype: Rect,
233 max_scroll_extent: f64,
234 last_tap_down_position: Option<Offset>,
235 last_secondary_tap_down_position: Option<Offset>,
236 layout_generation: u64,
237 selection_start_in_viewport: Handle<ValueNotifier<bool>>,
238 selection_end_in_viewport: Handle<ValueNotifier<bool>>,
239}
240
241impl RenderEditable {
242 pub fn new(
247 app: &mut App,
248 text_direction: TextDirection,
249 start_handle_layer_link: Handle<LayerLink>,
250 end_handle_layer_link: Handle<LayerLink>,
251 offset: AnyViewportOffset,
252 text_selection_delegate: AnyTextSelectionDelegate,
253 ) -> RenderHandle<Self> {
254 let show_cursor = app.create(ValueNotifier::new(false));
255 let selection_start_in_viewport = app.create(ValueNotifier::new(true));
256 let selection_end_in_viewport = app.create(ValueNotifier::new(true));
257 let mut text_painter = TextPainter::new();
258 text_painter.set_text_direction(Some(text_direction));
259 text_painter.set_max_lines(Some(1));
260 RenderHandle::new_box(
261 app,
262 RenderEditable {
263 render_object: RenderObjectData::new(),
264 render_box: RenderBoxData::new(),
265 container: ContainerRenderObjectData::new(),
266 text_painter,
267 text_intrinsics: None,
268 placeholder_dimensions: None,
269 system_fonts: RelayoutWhenSystemFontsChangeData::new(),
270 fonts_override: None,
271 show_cursor,
272 dispose_show_cursor: true,
273 has_focus: false,
274 start_handle_layer_link,
275 end_handle_layer_link,
276 max_lines: Some(1),
277 min_lines: None,
278 expands: false,
279 selection_color: None,
280 selection: None,
281 offset,
282 ignore_pointer: false,
283 read_only: false,
284 force_line: true,
285 obscuring_character: "\u{2022}".into(),
286 obscure_text: false,
287 cursor_color: None,
288 background_cursor_color: None,
289 cursor_width: 1.0,
290 cursor_height: None,
291 cursor_radius: None,
292 paint_cursor_above_text: false,
293 cursor_offset: Offset::ZERO,
294 device_pixel_ratio: 1.0,
295 selection_height_style: BoxHeightStyle::Max,
296 selection_width_style: BoxWidthStyle::Max,
297 enable_interactive_selection: None,
298 prompt_rect_range: None,
299 prompt_rect_color: None,
300 clip_behavior: Clip::HardEdge,
301 clip_rect_layer: LayerHandle::new(),
302 leader_layer_handler: LayerHandle::new(),
303 text_selection_delegate,
304 caret_prototype: Rect::ZERO,
305 max_scroll_extent: 0.0,
306 last_tap_down_position: None,
307 last_secondary_tap_down_position: None,
308 layout_generation: 0,
309 selection_start_in_viewport,
310 selection_end_in_viewport,
311 },
312 )
313 }
314
315 fn fonts(self: RenderHandle<Self>, app: &mut App) -> Handle<FontCollection> {
316 match self.get(app).fonts_override {
317 Some(fonts) => fonts,
318 None => PaintingBinding::instance(app).fonts(app),
319 }
320 }
321
322 fn painter_with_fonts(
323 self: RenderHandle<Self>,
324 app: &mut App,
325 ) -> (&mut TextPainter, &mut FontCollection) {
326 let fonts = self.fonts(app);
327 let (this, fonts) = app.get_disjoint_mut(self.handle(), fonts);
328 (&mut this.text_painter, fonts)
329 }
330
331 fn text_intrinsics_with_fonts(
332 self: RenderHandle<Self>,
333 app: &mut App,
334 ) -> (&mut TextPainter, &mut FontCollection) {
335 let fonts = self.fonts(app);
336 let (this, fonts) = app.get_disjoint_mut(self.handle(), fonts);
337 let painter = &this.text_painter;
338 let intrinsics = this.text_intrinsics.get_or_insert_with(TextPainter::new);
339 intrinsics.set_text(painter.text().cloned());
340 intrinsics.set_text_align(painter.text_align());
341 intrinsics.set_text_direction(painter.text_direction());
342 intrinsics.set_text_scaler(painter.text_scaler().clone());
343 intrinsics.set_max_lines(painter.max_lines());
344 intrinsics.set_text_width_basis(painter.text_width_basis());
345 intrinsics.set_text_height_behavior(painter.text_height_behavior());
346 (intrinsics, fonts)
347 }
348
349 fn mark_needs_paint_listener(self: RenderHandle<Self>) -> Listener {
350 Listener::handle_method(self.handle(), offset_or_cursor_changed)
351 }
352
353 pub fn text(self: RenderHandle<Self>, app: &App) -> Option<InlineSpanRef> {
355 self.get(app).text_painter.text().cloned()
356 }
357
358 pub fn set_text(self: RenderHandle<Self>, app: &mut App, value: Option<InlineSpanRef>) {
360 let current = self.get(app).text_painter.text().cloned();
361 if current.as_ref().map(|s| s.as_ref() as *const _)
362 == value.as_ref().map(|s| s.as_ref() as *const _)
363 {
364 return;
365 }
366 self.get_mut(app).text_painter.set_text(value);
367 self.get_mut(app).text_intrinsics = None;
368 self.mark_needs_layout(app);
369 }
370
371 pub fn text_align(self: RenderHandle<Self>, app: &App) -> TextAlign {
373 self.get(app).text_painter.text_align()
374 }
375
376 pub fn set_text_align(self: RenderHandle<Self>, app: &mut App, value: TextAlign) {
378 if self.text_align(app) == value {
379 return;
380 }
381 self.get_mut(app).text_painter.set_text_align(value);
382 self.mark_needs_paint(app);
383 }
384
385 pub fn text_direction(self: RenderHandle<Self>, app: &App) -> TextDirection {
387 self.get(app)
388 .text_painter
389 .text_direction()
390 .expect("an editable always has a direction")
391 }
392
393 pub fn set_text_direction(self: RenderHandle<Self>, app: &mut App, value: TextDirection) {
395 if self.get(app).text_painter.text_direction() == Some(value) {
396 return;
397 }
398 self.get_mut(app)
399 .text_painter
400 .set_text_direction(Some(value));
401 self.mark_needs_layout(app);
402 }
403
404 pub fn cursor_color(self: RenderHandle<Self>, app: &App) -> Option<Color> {
406 self.get(app).cursor_color
407 }
408
409 pub fn set_cursor_color(self: RenderHandle<Self>, app: &mut App, value: Option<Color>) {
411 if self.get(app).cursor_color == value {
412 return;
413 }
414 self.get_mut(app).cursor_color = value;
415 self.mark_needs_paint(app);
416 }
417
418 pub fn background_cursor_color(self: RenderHandle<Self>, app: &App) -> Option<Color> {
421 self.get(app).background_cursor_color
422 }
423
424 pub fn set_background_cursor_color(
426 self: RenderHandle<Self>,
427 app: &mut App,
428 value: Option<Color>,
429 ) {
430 if self.get(app).background_cursor_color == value {
431 return;
432 }
433 self.get_mut(app).background_cursor_color = value;
434 self.mark_needs_paint(app);
435 }
436
437 pub fn show_cursor(self: RenderHandle<Self>, app: &App) -> Handle<ValueNotifier<bool>> {
439 self.get(app).show_cursor
440 }
441
442 pub fn set_show_cursor(
444 self: RenderHandle<Self>,
445 app: &mut App,
446 value: Handle<ValueNotifier<bool>>,
447 ) {
448 let old = self.get(app).show_cursor;
449 if old == value {
450 return;
451 }
452 if self.attached(app) {
453 old.remove_listener(app, &self.mark_needs_paint_listener());
454 }
455 self.get_mut(app).show_cursor = value;
456 self.get_mut(app).dispose_show_cursor = false;
457 if self.attached(app) {
458 value.add_listener(app, self.mark_needs_paint_listener());
459 }
460 self.mark_needs_paint(app);
461 }
462
463 pub fn has_focus(self: RenderHandle<Self>, app: &App) -> bool {
465 self.get(app).has_focus
466 }
467
468 pub fn set_has_focus(self: RenderHandle<Self>, app: &mut App, value: bool) {
470 if self.get(app).has_focus == value {
471 return;
472 }
473 self.get_mut(app).has_focus = value;
474 }
475
476 pub fn start_handle_layer_link(self: RenderHandle<Self>, app: &App) -> Handle<LayerLink> {
478 self.get(app).start_handle_layer_link
479 }
480
481 pub fn set_start_handle_layer_link(
483 self: RenderHandle<Self>,
484 app: &mut App,
485 value: Handle<LayerLink>,
486 ) {
487 if self.get(app).start_handle_layer_link == value {
488 return;
489 }
490 self.get_mut(app).start_handle_layer_link = value;
491 self.mark_needs_paint(app);
492 }
493
494 pub fn end_handle_layer_link(self: RenderHandle<Self>, app: &App) -> Handle<LayerLink> {
496 self.get(app).end_handle_layer_link
497 }
498
499 pub fn set_end_handle_layer_link(
501 self: RenderHandle<Self>,
502 app: &mut App,
503 value: Handle<LayerLink>,
504 ) {
505 if self.get(app).end_handle_layer_link == value {
506 return;
507 }
508 self.get_mut(app).end_handle_layer_link = value;
509 self.mark_needs_paint(app);
510 }
511
512 pub fn max_lines(self: RenderHandle<Self>, app: &App) -> Option<i32> {
514 self.get(app).max_lines
515 }
516
517 pub fn set_max_lines(self: RenderHandle<Self>, app: &mut App, value: Option<i32>) {
519 debug_assert!(value.is_none_or(|n| n > 0));
520 if self.get(app).max_lines == value {
521 return;
522 }
523 self.get_mut(app).max_lines = value;
524 self.get_mut(app)
525 .text_painter
526 .set_max_lines(if value == Some(1) { Some(1) } else { None });
527 self.get_mut(app).text_intrinsics = None;
528 self.mark_needs_layout(app);
529 }
530
531 pub fn min_lines(self: RenderHandle<Self>, app: &App) -> Option<i32> {
533 self.get(app).min_lines
534 }
535
536 pub fn set_min_lines(self: RenderHandle<Self>, app: &mut App, value: Option<i32>) {
538 debug_assert!(value.is_none_or(|n| n > 0));
539 if self.get(app).min_lines == value {
540 return;
541 }
542 self.get_mut(app).min_lines = value;
543 self.mark_needs_layout(app);
544 }
545
546 pub fn expands(self: RenderHandle<Self>, app: &App) -> bool {
548 self.get(app).expands
549 }
550
551 pub fn set_expands(self: RenderHandle<Self>, app: &mut App, value: bool) {
553 if self.get(app).expands == value {
554 return;
555 }
556 self.get_mut(app).expands = value;
557 self.mark_needs_layout(app);
558 }
559
560 pub fn selection_color(self: RenderHandle<Self>, app: &App) -> Option<Color> {
562 self.get(app).selection_color
563 }
564
565 pub fn set_selection_color(self: RenderHandle<Self>, app: &mut App, value: Option<Color>) {
567 if self.get(app).selection_color == value {
568 return;
569 }
570 self.get_mut(app).selection_color = value;
571 self.mark_needs_paint(app);
572 }
573
574 pub fn text_scaler(self: RenderHandle<Self>, app: &App) -> TextScaler {
576 self.get(app).text_painter.text_scaler().clone()
577 }
578
579 pub fn set_text_scaler(self: RenderHandle<Self>, app: &mut App, value: TextScaler) {
581 if self.get(app).text_painter.text_scaler() == &value {
582 return;
583 }
584 self.get_mut(app).text_painter.set_text_scaler(value);
585 self.get_mut(app).text_intrinsics = None;
586 self.mark_needs_layout(app);
587 }
588
589 pub fn selection(self: RenderHandle<Self>, app: &App) -> Option<TextSelection> {
591 self.get(app).selection
592 }
593
594 pub fn set_selection(self: RenderHandle<Self>, app: &mut App, value: Option<TextSelection>) {
596 if self.get(app).selection == value {
597 return;
598 }
599 self.get_mut(app).selection = value;
600 self.mark_needs_paint(app);
601 }
602
603 pub fn offset(self: RenderHandle<Self>, app: &App) -> AnyViewportOffset {
605 self.get(app).offset
606 }
607
608 pub fn set_offset(self: RenderHandle<Self>, app: &mut App, value: AnyViewportOffset) {
610 let old = self.get(app).offset;
611 if old == value {
612 return;
613 }
614 if self.attached(app) {
615 old.remove_listener(app, &self.mark_needs_paint_listener());
616 }
617 self.get_mut(app).offset = value;
618 if self.attached(app) {
619 value.add_listener(app, self.mark_needs_paint_listener());
620 }
621 self.mark_needs_layout(app);
622 }
623
624 pub fn read_only(self: RenderHandle<Self>, app: &App) -> bool {
626 self.get(app).read_only
627 }
628
629 pub fn set_read_only(self: RenderHandle<Self>, app: &mut App, value: bool) {
631 if self.get(app).read_only == value {
632 return;
633 }
634 self.get_mut(app).read_only = value;
635 }
636
637 pub fn force_line(self: RenderHandle<Self>, app: &App) -> bool {
639 self.get(app).force_line
640 }
641
642 pub fn set_force_line(self: RenderHandle<Self>, app: &mut App, value: bool) {
644 if self.get(app).force_line == value {
645 return;
646 }
647 self.get_mut(app).force_line = value;
648 self.mark_needs_layout(app);
649 }
650
651 pub fn text_height_behavior(self: RenderHandle<Self>, app: &App) -> Option<TextHeightBehavior> {
653 self.get(app).text_painter.text_height_behavior()
654 }
655
656 pub fn set_text_height_behavior(
658 self: RenderHandle<Self>,
659 app: &mut App,
660 value: Option<TextHeightBehavior>,
661 ) {
662 if self.text_height_behavior(app) == value {
663 return;
664 }
665 self.get_mut(app)
666 .text_painter
667 .set_text_height_behavior(value);
668 self.mark_needs_layout(app);
669 }
670
671 pub fn text_width_basis(self: RenderHandle<Self>, app: &App) -> TextWidthBasis {
673 self.get(app).text_painter.text_width_basis()
674 }
675
676 pub fn set_text_width_basis(self: RenderHandle<Self>, app: &mut App, value: TextWidthBasis) {
678 if self.text_width_basis(app) == value {
679 return;
680 }
681 self.get_mut(app).text_painter.set_text_width_basis(value);
682 self.mark_needs_layout(app);
683 }
684
685 pub fn obscuring_character(self: RenderHandle<Self>, app: &App) -> &str {
687 &self.get(app).obscuring_character
688 }
689
690 pub fn set_obscuring_character(
692 self: RenderHandle<Self>,
693 app: &mut App,
694 value: impl Into<String>,
695 ) {
696 let value = value.into();
697 if self.get(app).obscuring_character == value {
698 return;
699 }
700 self.get_mut(app).obscuring_character = value;
701 self.mark_needs_layout(app);
702 }
703
704 pub fn obscure_text(self: RenderHandle<Self>, app: &App) -> bool {
706 self.get(app).obscure_text
707 }
708
709 pub fn set_obscure_text(self: RenderHandle<Self>, app: &mut App, value: bool) {
711 if self.get(app).obscure_text == value {
712 return;
713 }
714 self.get_mut(app).obscure_text = value;
715 }
716
717 pub fn cursor_width(self: RenderHandle<Self>, app: &App) -> f64 {
719 self.get(app).cursor_width
720 }
721
722 pub fn set_cursor_width(self: RenderHandle<Self>, app: &mut App, value: f64) {
724 if self.get(app).cursor_width == value {
725 return;
726 }
727 self.get_mut(app).cursor_width = value;
728 self.mark_needs_layout(app);
729 }
730
731 pub fn cursor_height(self: RenderHandle<Self>, app: &mut App) -> f64 {
733 self.get(app)
734 .cursor_height
735 .unwrap_or_else(|| self.preferred_line_height(app))
736 }
737
738 pub fn set_cursor_height(self: RenderHandle<Self>, app: &mut App, value: Option<f64>) {
740 if self.get(app).cursor_height == value {
741 return;
742 }
743 self.get_mut(app).cursor_height = value;
744 self.mark_needs_layout(app);
745 }
746
747 pub fn cursor_radius(self: RenderHandle<Self>, app: &App) -> Option<Radius> {
749 self.get(app).cursor_radius
750 }
751
752 pub fn set_cursor_radius(self: RenderHandle<Self>, app: &mut App, value: Option<Radius>) {
754 if self.get(app).cursor_radius == value {
755 return;
756 }
757 self.get_mut(app).cursor_radius = value;
758 self.mark_needs_paint(app);
759 }
760
761 pub fn paint_cursor_above_text(self: RenderHandle<Self>, app: &App) -> bool {
763 self.get(app).paint_cursor_above_text
764 }
765
766 pub fn set_paint_cursor_above_text(self: RenderHandle<Self>, app: &mut App, value: bool) {
768 if self.get(app).paint_cursor_above_text == value {
769 return;
770 }
771 self.get_mut(app).paint_cursor_above_text = value;
772 self.mark_needs_paint(app);
773 }
774
775 pub fn cursor_offset(self: RenderHandle<Self>, app: &App) -> Offset {
777 self.get(app).cursor_offset
778 }
779
780 pub fn set_cursor_offset(self: RenderHandle<Self>, app: &mut App, value: Offset) {
782 if self.get(app).cursor_offset == value {
783 return;
784 }
785 self.get_mut(app).cursor_offset = value;
786 self.mark_needs_paint(app);
787 }
788
789 pub fn device_pixel_ratio(self: RenderHandle<Self>, app: &App) -> f64 {
791 self.get(app).device_pixel_ratio
792 }
793
794 pub fn set_device_pixel_ratio(self: RenderHandle<Self>, app: &mut App, value: f64) {
796 if self.get(app).device_pixel_ratio == value {
797 return;
798 }
799 self.get_mut(app).device_pixel_ratio = value;
800 self.mark_needs_layout(app);
801 }
802
803 pub fn selection_height_style(self: RenderHandle<Self>, app: &App) -> BoxHeightStyle {
805 self.get(app).selection_height_style
806 }
807
808 pub fn set_selection_height_style(
810 self: RenderHandle<Self>,
811 app: &mut App,
812 value: BoxHeightStyle,
813 ) {
814 if self.get(app).selection_height_style == value {
815 return;
816 }
817 self.get_mut(app).selection_height_style = value;
818 self.mark_needs_paint(app);
819 }
820
821 pub fn selection_width_style(self: RenderHandle<Self>, app: &App) -> BoxWidthStyle {
823 self.get(app).selection_width_style
824 }
825
826 pub fn set_selection_width_style(
828 self: RenderHandle<Self>,
829 app: &mut App,
830 value: BoxWidthStyle,
831 ) {
832 if self.get(app).selection_width_style == value {
833 return;
834 }
835 self.get_mut(app).selection_width_style = value;
836 self.mark_needs_paint(app);
837 }
838
839 pub fn enable_interactive_selection(self: RenderHandle<Self>, app: &App) -> Option<bool> {
841 self.get(app).enable_interactive_selection
842 }
843
844 pub fn set_enable_interactive_selection(
846 self: RenderHandle<Self>,
847 app: &mut App,
848 value: Option<bool>,
849 ) {
850 if self.get(app).enable_interactive_selection == value {
851 return;
852 }
853 self.get_mut(app).enable_interactive_selection = value;
854 self.mark_needs_paint(app);
855 }
856
857 pub fn text_selection_delegate(
860 self: RenderHandle<Self>,
861 app: &App,
862 ) -> AnyTextSelectionDelegate {
863 self.get(app).text_selection_delegate
864 }
865
866 pub fn set_text_selection_delegate(
868 self: RenderHandle<Self>,
869 app: &mut App,
870 value: AnyTextSelectionDelegate,
871 ) {
872 self.get_mut(app).text_selection_delegate = value;
873 }
874
875 pub fn prompt_rect_color(self: RenderHandle<Self>, app: &App) -> Option<Color> {
877 self.get(app).prompt_rect_color
878 }
879
880 pub fn set_prompt_rect_color(self: RenderHandle<Self>, app: &mut App, value: Option<Color>) {
882 if self.get(app).prompt_rect_color == value {
883 return;
884 }
885 self.get_mut(app).prompt_rect_color = value;
886 self.mark_needs_paint(app);
887 }
888
889 pub fn set_prompt_rect_range(
891 self: RenderHandle<Self>,
892 app: &mut App,
893 value: Option<TextRange>,
894 ) {
895 if self.get(app).prompt_rect_range == value {
896 return;
897 }
898 self.get_mut(app).prompt_rect_range = value;
899 self.mark_needs_paint(app);
900 }
901
902 pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
904 self.get(app).clip_behavior
905 }
906
907 pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
909 if self.get(app).clip_behavior == value {
910 return;
911 }
912 self.get_mut(app).clip_behavior = value;
913 self.mark_needs_paint(app);
914 }
915
916 pub fn selection_start_in_viewport(
919 self: RenderHandle<Self>,
920 app: &App,
921 ) -> Handle<ValueNotifier<bool>> {
922 self.get(app).selection_start_in_viewport
923 }
924
925 pub fn selection_end_in_viewport(
928 self: RenderHandle<Self>,
929 app: &App,
930 ) -> Handle<ValueNotifier<bool>> {
931 self.get(app).selection_end_in_viewport
932 }
933
934 fn update_selection_extents_visibility(
935 self: RenderHandle<Self>,
936 app: &mut App,
937 effective_offset: Offset,
938 ) {
939 let Some(selection) = self.selection(app) else {
940 return;
941 };
942 let start_notifier = self.get(app).selection_start_in_viewport;
943 let end_notifier = self.get(app).selection_end_in_viewport;
944 if !selection.is_valid() {
945 start_notifier.set_value(app, false);
946 end_notifier.set_value(app, false);
947 return;
948 }
949 let visible_region = Offset::ZERO & self.size(app);
950 let caret_prototype = self.get(app).caret_prototype;
951 let start = TextPosition::with_affinity(selection.start(), selection.affinity);
952 let end = TextPosition::with_affinity(selection.end(), selection.affinity);
953 let (start_offset, end_offset) = {
954 let (painter, fonts) = self.painter_with_fonts(app);
955 (
956 painter.get_offset_for_caret(fonts, start, caret_prototype),
957 painter.get_offset_for_caret(fonts, end, caret_prototype),
958 )
959 };
960 const VISIBLE_REGION_SLOP: f64 = 0.5;
961 let region = visible_region.inflate(VISIBLE_REGION_SLOP);
962 start_notifier.set_value(app, region.contains(start_offset + effective_offset));
963 end_notifier.set_value(app, region.contains(end_offset + effective_offset));
964 }
965
966 pub fn plain_text(self: RenderHandle<Self>, app: &mut App) -> String {
968 self.get_mut(app).text_painter.plain_text().to_owned()
969 }
970
971 pub fn preferred_line_height(self: RenderHandle<Self>, app: &mut App) -> f64 {
973 let (painter, fonts) = self.painter_with_fonts(app);
974 painter.preferred_line_height(fonts)
975 }
976
977 fn caret_margin(self: RenderHandle<Self>, app: &App) -> f64 {
978 K_CARET_GAP + self.get(app).cursor_width
979 }
980
981 fn is_multiline(self: RenderHandle<Self>, app: &App) -> bool {
982 self.get(app).max_lines != Some(1)
983 }
984
985 fn paint_offset(self: RenderHandle<Self>, app: &App) -> Offset {
986 let pixels = self.get(app).offset.pixels(app);
987 if self.is_multiline(app) {
988 Offset::new(0.0, -pixels)
989 } else {
990 Offset::new(-pixels, 0.0)
991 }
992 }
993
994 fn viewport_extent(self: RenderHandle<Self>, app: &App) -> f64 {
995 let size = self.size(app);
996 if self.is_multiline(app) {
997 size.height()
998 } else {
999 size.width()
1000 }
1001 }
1002
1003 fn get_max_scroll_extent(self: RenderHandle<Self>, app: &App, content_size: Size) -> f64 {
1004 let size = self.size(app);
1005 if self.is_multiline(app) {
1006 (content_size.height() - size.height()).max(0.0)
1007 } else {
1008 (content_size.width() - size.width()).max(0.0)
1009 }
1010 }
1011
1012 fn has_visual_overflow(self: RenderHandle<Self>, app: &App) -> bool {
1013 self.get(app).max_scroll_extent > 0.0 || self.paint_offset(app) != Offset::ZERO
1014 }
1015
1016 fn adjust_constraints(
1017 self: RenderHandle<Self>,
1018 app: &App,
1019 min_width: f64,
1020 max_width: f64,
1021 ) -> (f64, f64) {
1022 let available_max_width = (max_width - self.caret_margin(app)).max(0.0);
1023 let available_min_width = min_width.min(available_max_width);
1024 let min = if self.get(app).force_line {
1025 available_max_width
1026 } else {
1027 available_min_width
1028 };
1029 let max = if self.is_multiline(app) {
1030 available_max_width
1031 } else {
1032 f64::INFINITY
1033 };
1034 (min, max)
1035 }
1036
1037 fn compute_text_metrics_if_needed(self: RenderHandle<Self>, app: &mut App) {
1038 let constraints = self.constraints(app);
1039 let (min_width, max_width) =
1040 self.adjust_constraints(app, constraints.min_width, constraints.max_width);
1041 let (painter, fonts) = self.painter_with_fonts(app);
1042 painter.layout(fonts, min_width, max_width);
1043 }
1044
1045 fn compute_caret_prototype(self: RenderHandle<Self>, app: &mut App) {
1046 let cursor_width = self.get(app).cursor_width;
1047 let cursor_height = self.cursor_height(app);
1048 let prototype = match app.platform().target_platform() {
1049 TargetPlatform::IOS | TargetPlatform::MacOS => {
1050 Rect::from_ltwh(0.0, 0.0, cursor_width, cursor_height + 2.0)
1051 }
1052 TargetPlatform::Android
1053 | TargetPlatform::Fuchsia
1054 | TargetPlatform::Linux
1055 | TargetPlatform::Windows => Rect::from_ltwh(
1056 0.0,
1057 K_CARET_HEIGHT_OFFSET,
1058 cursor_width,
1059 cursor_height - 2.0 * K_CARET_HEIGHT_OFFSET,
1060 ),
1061 };
1062 self.get_mut(app).caret_prototype = prototype;
1063 }
1064
1065 fn snap_to_physical_pixel(
1066 self: RenderHandle<Self>,
1067 app: &App,
1068 source_offset: Offset,
1069 ) -> Offset {
1070 let global = self.as_box().local_to_global(app, source_offset, None);
1071 let pixel_multiple = 1.0 / self.get(app).device_pixel_ratio;
1072 Offset::new(
1073 if global.dx().is_finite() {
1074 (global.dx() / pixel_multiple).round() * pixel_multiple - global.dx()
1075 } else {
1076 0.0
1077 },
1078 if global.dy().is_finite() {
1079 (global.dy() / pixel_multiple).round() * pixel_multiple - global.dy()
1080 } else {
1081 0.0
1082 },
1083 )
1084 }
1085
1086 pub fn get_endpoints_for_selection(
1088 self: RenderHandle<Self>,
1089 app: &mut App,
1090 selection: TextSelection,
1091 ) -> Vec<TextSelectionPoint> {
1092 self.compute_text_metrics_if_needed(app);
1093 let paint_offset = self.paint_offset(app);
1094 let height_style = self.get(app).selection_height_style;
1095 let width_style = self.get(app).selection_width_style;
1096 let boxes = if selection.is_collapsed() {
1097 Vec::new()
1098 } else {
1099 self.get(app)
1100 .text_painter
1101 .get_boxes_for_selection(selection, height_style, width_style)
1102 };
1103 if boxes.is_empty() {
1104 let caret_prototype = self.get(app).caret_prototype;
1105 let (painter, fonts) = self.painter_with_fonts(app);
1106 let caret_offset =
1107 painter.get_offset_for_caret(fonts, selection.extent(), caret_prototype);
1108 let line_height = painter.preferred_line_height(fonts);
1109 let start = Offset::new(0.0, line_height) + caret_offset + paint_offset;
1110 return vec![TextSelectionPoint::new(start, None)];
1111 }
1112 let painter_width = self.get(app).text_painter.size().width();
1113 let start = Offset::new(
1114 clamp_double(boxes[0].start(), 0.0, painter_width),
1115 boxes[0].bottom,
1116 ) + paint_offset;
1117 let last = boxes[boxes.len() - 1];
1118 let end =
1119 Offset::new(clamp_double(last.end(), 0.0, painter_width), last.bottom) + paint_offset;
1120 vec![
1121 TextSelectionPoint::new(start, Some(boxes[0].direction)),
1122 TextSelectionPoint::new(end, Some(last.direction)),
1123 ]
1124 }
1125
1126 pub fn get_rect_for_composing_range(
1129 self: RenderHandle<Self>,
1130 app: &mut App,
1131 range: TextRange,
1132 ) -> Option<Rect> {
1133 if !range.is_valid() || range.is_collapsed() {
1134 return None;
1135 }
1136 self.compute_text_metrics_if_needed(app);
1137 let boxes = self.get(app).text_painter.get_boxes_for_selection(
1138 TextSelection::new(range.start, range.end),
1139 self.get(app).selection_height_style,
1140 self.get(app).selection_width_style,
1141 );
1142 if boxes.is_empty() {
1143 return None;
1144 }
1145 let paint_offset = self.paint_offset(app);
1146 let mut rect = boxes[0].to_rect();
1147 for box_ in boxes.iter().skip(1) {
1148 rect = rect.expand_to_include(box_.to_rect());
1149 }
1150 Some(rect.shift(paint_offset))
1151 }
1152
1153 pub fn get_position_for_point(
1155 self: RenderHandle<Self>,
1156 app: &mut App,
1157 global_position: Offset,
1158 ) -> TextPosition {
1159 self.compute_text_metrics_if_needed(app);
1160 let local =
1161 self.as_box().global_to_local(app, global_position, None) - self.paint_offset(app);
1162 self.get(app).text_painter.get_position_for_offset(local)
1163 }
1164
1165 pub fn get_local_rect_for_caret(
1168 self: RenderHandle<Self>,
1169 app: &mut App,
1170 caret_position: TextPosition,
1171 ) -> Rect {
1172 self.compute_text_metrics_if_needed(app);
1173 let caret_prototype = self.get(app).caret_prototype;
1174 let cursor_offset = self.get(app).cursor_offset;
1175 let (painter, fonts) = self.painter_with_fonts(app);
1176 let caret_offset = painter.get_offset_for_caret(fonts, caret_position, caret_prototype);
1177 let mut caret_rect = caret_prototype.shift(caret_offset + cursor_offset);
1178 let painter_width = painter.width();
1179 let size = self.size(app);
1180 let caret_margin = self.caret_margin(app);
1181 let scrollable_width = (painter_width + caret_margin).max(size.width());
1182 let caret_x = clamp_double(
1183 caret_rect.left,
1184 0.0,
1185 (scrollable_width - caret_margin).max(0.0),
1186 );
1187 caret_rect = Offset::new(caret_x, caret_rect.top) & caret_rect.size();
1188 let (painter, fonts) = self.painter_with_fonts(app);
1189 let full_height = painter.get_full_height_for_caret(fonts, caret_position, caret_prototype);
1190 caret_rect = match app.platform().target_platform() {
1191 TargetPlatform::IOS | TargetPlatform::MacOS => {
1192 let height_diff = full_height - caret_rect.height();
1193 Rect::from_ltwh(
1194 caret_rect.left,
1195 caret_rect.top + height_diff / 2.0,
1196 caret_rect.width(),
1197 caret_rect.height(),
1198 )
1199 }
1200 TargetPlatform::Android
1201 | TargetPlatform::Fuchsia
1202 | TargetPlatform::Linux
1203 | TargetPlatform::Windows => {
1204 let caret_height = self.cursor_height(app);
1205 let height_diff = full_height - caret_height;
1206 Rect::from_ltwh(
1207 caret_rect.left,
1208 caret_rect.top - K_CARET_HEIGHT_OFFSET + height_diff / 2.0,
1209 caret_rect.width(),
1210 caret_height,
1211 )
1212 }
1213 };
1214 caret_rect = caret_rect.shift(self.paint_offset(app));
1215 caret_rect.shift(self.snap_to_physical_pixel(app, caret_rect.top_left()))
1216 }
1217
1218 fn set_text_editing_value(
1219 self: RenderHandle<Self>,
1220 app: &mut App,
1221 new_value: inset_embedder::TextEditingValue,
1222 cause: SelectionChangedCause,
1223 ) {
1224 self.get(app)
1225 .text_selection_delegate
1226 .user_update_text_editing_value(app, new_value, cause);
1227 }
1228
1229 fn apply_selection(
1230 self: RenderHandle<Self>,
1231 app: &mut App,
1232 mut next_selection: TextSelection,
1233 cause: SelectionChangedCause,
1234 ) {
1235 if next_selection.is_valid() {
1236 let text_length = utf16_len(
1237 &self
1238 .get(app)
1239 .text_selection_delegate
1240 .text_editing_value(app)
1241 .text,
1242 );
1243 next_selection = next_selection
1244 .copy_with()
1245 .base_offset(next_selection.base_offset.min(text_length))
1246 .extent_offset(next_selection.extent_offset.min(text_length));
1247 }
1248 let value = self
1249 .get(app)
1250 .text_selection_delegate
1251 .text_editing_value(app)
1252 .copy_with()
1253 .selection(next_selection);
1254 self.set_text_editing_value(app, value, cause);
1255 }
1256
1257 pub fn handle_tap_down(self: RenderHandle<Self>, _app: &mut App, details: &TapDownDetails) {
1260 self.get_mut(_app).last_tap_down_position = Some(details.global_position);
1261 }
1262
1263 pub fn last_secondary_tap_down_position(self: RenderHandle<Self>, app: &App) -> Option<Offset> {
1266 self.get(app).last_secondary_tap_down_position
1267 }
1268
1269 pub fn handle_secondary_tap_down(
1274 self: RenderHandle<Self>,
1275 app: &mut App,
1276 details: &TapDownDetails,
1277 ) {
1278 self.get_mut(app).last_tap_down_position = Some(details.global_position);
1279 self.get_mut(app).last_secondary_tap_down_position = Some(details.global_position);
1280 }
1281
1282 pub fn handle_tap(self: RenderHandle<Self>, app: &mut App) {
1284 self.select_position(app, SelectionChangedCause::Tap);
1285 }
1286
1287 pub fn handle_double_tap(self: RenderHandle<Self>, app: &mut App) {
1289 self.select_word(app, SelectionChangedCause::DoubleTap);
1290 }
1291
1292 pub fn handle_long_press(self: RenderHandle<Self>, app: &mut App) {
1294 self.select_word(app, SelectionChangedCause::LongPress);
1295 }
1296
1297 pub fn select_position(self: RenderHandle<Self>, app: &mut App, cause: SelectionChangedCause) {
1299 let from = self
1300 .get(app)
1301 .last_tap_down_position
1302 .expect("select_position needs a tap-down");
1303 self.select_position_at(app, from, None, cause);
1304 }
1305
1306 pub fn select_position_at(
1308 self: RenderHandle<Self>,
1309 app: &mut App,
1310 from: Offset,
1311 to: Option<Offset>,
1312 cause: SelectionChangedCause,
1313 ) {
1314 self.compute_text_metrics_if_needed(app);
1315 let paint_offset = self.paint_offset(app);
1316 let from_position = self
1317 .get(app)
1318 .text_painter
1319 .get_position_for_offset(self.as_box().global_to_local(app, from, None) - paint_offset);
1320 let to_position = to.map(|to| {
1321 self.get(app).text_painter.get_position_for_offset(
1322 self.as_box().global_to_local(app, to, None) - paint_offset,
1323 )
1324 });
1325 let new_selection = TextSelection {
1326 base_offset: from_position.offset,
1327 extent_offset: to_position.unwrap_or(from_position).offset,
1328 affinity: from_position.affinity,
1329 is_directional: false,
1330 };
1331 self.apply_selection(app, new_selection, cause);
1332 }
1333
1334 pub fn select_word(self: RenderHandle<Self>, app: &mut App, cause: SelectionChangedCause) {
1336 let from = self
1337 .get(app)
1338 .last_tap_down_position
1339 .expect("select_word needs a tap-down");
1340 self.select_words_in_range(app, from, None, cause);
1341 }
1342
1343 pub fn select_words_in_range(
1345 self: RenderHandle<Self>,
1346 app: &mut App,
1347 from: Offset,
1348 to: Option<Offset>,
1349 cause: SelectionChangedCause,
1350 ) {
1351 self.compute_text_metrics_if_needed(app);
1352 let paint_offset = self.paint_offset(app);
1353 let from_position = self
1354 .get(app)
1355 .text_painter
1356 .get_position_for_offset(self.as_box().global_to_local(app, from, None) - paint_offset);
1357 let from_word = self.get_word_at_offset(app, from_position);
1358 let to_position = match to {
1359 None => from_position,
1360 Some(to) => self.get(app).text_painter.get_position_for_offset(
1361 self.as_box().global_to_local(app, to, None) - paint_offset,
1362 ),
1363 };
1364 let to_word = if to_position == from_position {
1365 from_word
1366 } else {
1367 self.get_word_at_offset(app, to_position)
1368 };
1369 let is_from_word_before_to_word = from_word.start() < to_word.end();
1370 self.apply_selection(
1371 app,
1372 TextSelection {
1373 base_offset: if is_from_word_before_to_word {
1374 from_word.base().offset
1375 } else {
1376 from_word.extent().offset
1377 },
1378 extent_offset: if is_from_word_before_to_word {
1379 to_word.extent().offset
1380 } else {
1381 to_word.base().offset
1382 },
1383 affinity: from_word.affinity,
1384 is_directional: false,
1385 },
1386 cause,
1387 );
1388 }
1389
1390 pub fn select_word_edge(self: RenderHandle<Self>, app: &mut App, cause: SelectionChangedCause) {
1392 self.compute_text_metrics_if_needed(app);
1393 let from = self
1394 .get(app)
1395 .last_tap_down_position
1396 .expect("select_word_edge needs a tap-down");
1397 let paint_offset = self.paint_offset(app);
1398 let position = self
1399 .get(app)
1400 .text_painter
1401 .get_position_for_offset(self.as_box().global_to_local(app, from, None) - paint_offset);
1402 let word = self.get(app).text_painter.get_word_boundary(position);
1403 let new_selection = if position.offset <= word.start {
1404 TextSelection::collapsed(word.start, TextAffinity::Downstream)
1405 } else {
1406 TextSelection::collapsed(word.end, TextAffinity::Upstream)
1407 };
1408 self.apply_selection(app, new_selection, cause);
1409 }
1410
1411 pub fn get_word_at_offset(
1414 self: RenderHandle<Self>,
1415 app: &mut App,
1416 position: TextPosition,
1417 ) -> TextSelection {
1418 let plain = self.plain_text(app);
1419 let length = utf16_len(&plain);
1420 if position.offset >= length {
1421 return TextSelection::from_position(TextPosition::with_affinity(
1422 length,
1423 TextAffinity::Upstream,
1424 ));
1425 }
1426 if self.get(app).obscure_text {
1427 return TextSelection::new(0, length);
1428 }
1429 let word = self.get(app).text_painter.get_word_boundary(position);
1430 let effective_offset = match position.affinity {
1431 TextAffinity::Upstream => position.offset - 1,
1432 TextAffinity::Downstream => position.offset,
1433 };
1434 debug_assert!(effective_offset >= 0);
1435 if effective_offset > 0
1436 && code_unit_at(&plain, effective_offset).is_some_and(TextLayoutMetrics::is_whitespace)
1437 {
1438 if let Some(previous_word) = self.get_previous_word(app, word.start) {
1439 match app.platform().target_platform() {
1440 TargetPlatform::IOS => {
1441 return TextSelection::new(previous_word.start, position.offset);
1442 }
1443 TargetPlatform::Android if self.get(app).read_only => {
1444 return TextSelection::new(previous_word.start, position.offset);
1445 }
1446 _ => {}
1447 }
1448 } else {
1449 match app.platform().target_platform() {
1450 TargetPlatform::IOS => {
1451 if let Some(next_word) = self.get_next_word(app, word.start) {
1452 return TextSelection::new(position.offset, next_word.end);
1453 }
1454 return TextSelection::collapsed(position.offset, position.affinity);
1455 }
1456 TargetPlatform::Android if self.get(app).read_only => {
1457 return TextSelection::new(position.offset, position.offset + 1);
1458 }
1459 _ => {}
1460 }
1461 }
1462 }
1463 TextSelection::new(word.start, word.end)
1464 }
1465
1466 fn get_next_word(
1467 self: RenderHandle<Self>,
1468 app: &mut App,
1469 mut offset: i32,
1470 ) -> Option<TextRange> {
1471 loop {
1472 let range = self
1473 .get(app)
1474 .text_painter
1475 .get_word_boundary(TextPosition::new(offset));
1476 if !range.is_valid() || range.is_collapsed() {
1477 return None;
1478 }
1479 if !self.only_whitespace(app, range) {
1480 return Some(range);
1481 }
1482 offset = range.end;
1483 }
1484 }
1485
1486 fn get_previous_word(
1487 self: RenderHandle<Self>,
1488 app: &mut App,
1489 mut offset: i32,
1490 ) -> Option<TextRange> {
1491 while offset >= 0 {
1492 let range = self
1493 .get(app)
1494 .text_painter
1495 .get_word_boundary(TextPosition::new(offset));
1496 if !range.is_valid() || range.is_collapsed() {
1497 return None;
1498 }
1499 if !self.only_whitespace(app, range) {
1500 return Some(range);
1501 }
1502 offset = range.start - 1;
1503 }
1504 None
1505 }
1506
1507 fn only_whitespace(self: RenderHandle<Self>, app: &mut App, range: TextRange) -> bool {
1508 let plain = self.plain_text(app);
1509 for i in range.start..range.end {
1510 if let Some(unit) = code_unit_at(&plain, i)
1511 && !TextLayoutMetrics::is_whitespace(unit)
1512 {
1513 return false;
1514 }
1515 }
1516 true
1517 }
1518
1519 pub fn get_line_at_offset(
1521 self: RenderHandle<Self>,
1522 app: &mut App,
1523 position: TextPosition,
1524 ) -> TextSelection {
1525 self.compute_text_metrics_if_needed(app);
1526 let line = self.get(app).text_painter.get_line_boundary(position);
1527 if self.get(app).obscure_text {
1528 return TextSelection::new(0, utf16_len(&self.plain_text(app)));
1529 }
1530 TextSelection::new(line.start, line.end)
1531 }
1532
1533 pub fn word_boundaries(self: RenderHandle<Self>, app: &App) -> WordBoundary {
1537 WordBoundary::new(
1538 self.text(app).expect("RenderEditable always has text"),
1539 self,
1540 )
1541 }
1542
1543 pub fn get_word_boundary(
1545 self: RenderHandle<Self>,
1546 app: &mut App,
1547 position: TextPosition,
1548 ) -> TextRange {
1549 self.compute_text_metrics_if_needed(app);
1550 self.get(app).text_painter.get_word_boundary(position)
1551 }
1552
1553 pub fn get_text_position_above(
1555 self: RenderHandle<Self>,
1556 app: &mut App,
1557 position: TextPosition,
1558 ) -> TextPosition {
1559 self.compute_text_metrics_if_needed(app);
1560 let line_height = self.preferred_line_height(app);
1561 self.get_text_position_vertical(app, position, -0.5 * line_height)
1562 }
1563
1564 pub fn get_text_position_below(
1566 self: RenderHandle<Self>,
1567 app: &mut App,
1568 position: TextPosition,
1569 ) -> TextPosition {
1570 self.compute_text_metrics_if_needed(app);
1571 let line_height = self.preferred_line_height(app);
1572 self.get_text_position_vertical(app, position, 1.5 * line_height)
1573 }
1574
1575 fn get_text_position_vertical(
1576 self: RenderHandle<Self>,
1577 app: &mut App,
1578 position: TextPosition,
1579 vertical_offset: f64,
1580 ) -> TextPosition {
1581 let caret_prototype = self.get(app).caret_prototype;
1582 let (painter, fonts) = self.painter_with_fonts(app);
1583 let caret_offset = painter.get_offset_for_caret(fonts, position, caret_prototype);
1584 painter.get_position_for_offset(caret_offset.translate(0.0, vertical_offset))
1585 }
1586
1587 pub fn start_vertical_caret_movement(
1589 self: RenderHandle<Self>,
1590 app: &mut App,
1591 start_position: TextPosition,
1592 ) -> VerticalCaretMovementRun {
1593 self.compute_text_metrics_if_needed(app);
1594 let metrics = self.get_mut(app).text_painter.compute_line_metrics();
1595 let (painter, fonts) = self.painter_with_fonts(app);
1596 let (line, offset) = line_number_for(painter, fonts, start_position, &metrics);
1597 VerticalCaretMovementRun {
1598 editable: self,
1599 layout_generation: self.get(app).layout_generation,
1600 current_offset: offset,
1601 current_line: line,
1602 current_text_position: start_position,
1603 line_metrics: metrics,
1604 position_cache: HashMap::new(),
1605 is_valid: true,
1606 }
1607 }
1608
1609 fn paint_highlights(
1610 self: RenderHandle<Self>,
1611 app: &App,
1612 canvas: &mut inset_embedder::Canvas,
1613 offset: Offset,
1614 ) {
1615 let paint_offset = offset + self.paint_offset(app);
1616 let painter_size = self.get(app).text_painter.size();
1617 let clip = Rect::from_ltwh(0.0, 0.0, painter_size.width(), painter_size.height());
1618 if let (Some(color), Some(range)) = (
1619 self.get(app).prompt_rect_color,
1620 self.get(app).prompt_rect_range,
1621 ) && !range.is_collapsed()
1622 {
1623 paint_range(
1624 canvas,
1625 &self.get(app).text_painter,
1626 range,
1627 color,
1628 paint_offset,
1629 clip,
1630 self.get(app).selection_height_style,
1631 self.get(app).selection_width_style,
1632 );
1633 }
1634 if let (Some(color), Some(selection)) =
1635 (self.get(app).selection_color, self.get(app).selection)
1636 && !selection.is_collapsed()
1637 {
1638 paint_range(
1639 canvas,
1640 &self.get(app).text_painter,
1641 selection.range(),
1642 color,
1643 paint_offset,
1644 clip,
1645 self.get(app).selection_height_style,
1646 self.get(app).selection_width_style,
1647 );
1648 }
1649 }
1650
1651 fn paint_caret(
1652 self: RenderHandle<Self>,
1653 app: &mut App,
1654 canvas: &mut inset_embedder::Canvas,
1655 offset: Offset,
1656 ) {
1657 let Some(selection) = self.get(app).selection else {
1658 return;
1659 };
1660 let show_cursor = self.get(app).show_cursor;
1661 if !selection.is_collapsed() || !*show_cursor.value(app) {
1662 return;
1663 }
1664 let Some(color) = self.get(app).cursor_color else {
1665 return;
1666 };
1667 let caret = self
1668 .get_local_rect_for_caret(app, selection.extent())
1669 .shift(offset);
1670 canvas.draw_rect(
1671 caret,
1672 &Paint {
1673 color: color.into(),
1674 ..Paint::default()
1675 },
1676 );
1677 }
1678
1679 fn paint_contents(
1680 self: RenderHandle<Self>,
1681 app: &mut App,
1682 context: &mut PaintingContext,
1683 offset: Offset,
1684 ) {
1685 let effective = offset + self.paint_offset(app);
1686 if self.selection(app).is_some() {
1687 self.update_selection_extents_visibility(app, effective);
1688 }
1689 let above = self.get(app).paint_cursor_above_text;
1690 if !above {
1691 self.paint_caret(app, context.canvas(), offset);
1692 }
1693 self.paint_highlights(app, context.canvas(), offset);
1694 let (painter, fonts) = self.painter_with_fonts(app);
1695 painter.paint(fonts, context.canvas(), effective);
1696 self.paint_inline_children(app, context, effective);
1697 if above {
1698 self.paint_caret(app, context.canvas(), offset);
1699 }
1700 }
1701
1702 fn preferred_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
1703 let max_lines = self.get(app).max_lines;
1704 let min_lines = self.get(app).min_lines.or(max_lines);
1705 let line_height = self.preferred_line_height(app);
1706 let min_height = line_height * f64::from(min_lines.unwrap_or(0));
1707 match max_lines {
1708 None => {
1709 let estimated = if width.is_infinite() {
1710 line_height * f64::from(count_hard_line_breaks(&self.plain_text(app)) + 1)
1711 } else {
1712 let (min_width, max_width) = self.adjust_constraints(app, 0.0, width);
1713 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
1714 intrinsics.layout(fonts, min_width, max_width);
1715 intrinsics.height()
1716 };
1717 estimated.max(min_height)
1718 }
1719 Some(1) => {
1720 let (min_width, max_width) = self.adjust_constraints(app, 0.0, width);
1721 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
1722 intrinsics.layout(fonts, min_width, max_width);
1723 intrinsics.height()
1724 }
1725 Some(max) if min_lines == Some(max) => min_height,
1726 Some(max) => {
1727 let max_height = line_height * f64::from(max);
1728 let (min_width, max_width) = self.adjust_constraints(app, 0.0, width);
1729 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
1730 intrinsics.layout(fonts, min_width, max_width);
1731 clamp_double(intrinsics.height(), min_height, max_height)
1732 }
1733 }
1734 }
1735}
1736
1737fn offset_or_cursor_changed(this: Handle<RenderEditable>, app: &mut App) {
1738 RenderHandle::from_handle(this).mark_needs_paint(app);
1739}
1740
1741fn utf16_len(text: &str) -> i32 {
1742 text.encode_utf16().count() as i32
1743}
1744
1745fn code_unit_at(text: &str, offset: i32) -> Option<i32> {
1746 text.encode_utf16().nth(offset as usize).map(i32::from)
1747}
1748
1749fn count_hard_line_breaks(text: &str) -> i32 {
1750 text.encode_utf16()
1751 .filter(|unit| matches!(*unit, 0x000A | 0x0085 | 0x000B | 0x000C | 0x2028 | 0x2029))
1752 .count() as i32
1753}
1754
1755fn line_number_for(
1756 painter: &mut TextPainter,
1757 fonts: &mut FontCollection,
1758 start_position: TextPosition,
1759 metrics: &[LineMetrics],
1760) -> (i32, Offset) {
1761 let offset = painter.get_offset_for_caret(fonts, start_position, Rect::ZERO);
1762 for line in metrics {
1763 if line.baseline > offset.dy() {
1764 return (line.line_number, Offset::new(offset.dx(), line.baseline));
1765 }
1766 }
1767 let last = metrics.last();
1768 (
1769 last.map(|m| m.line_number).unwrap_or(0).max(0),
1770 Offset::new(
1771 offset.dx(),
1772 last.map(|m| m.baseline + m.descent).unwrap_or(0.0),
1773 ),
1774 )
1775}
1776
1777#[allow(clippy::too_many_arguments)]
1778fn paint_range(
1779 canvas: &mut inset_embedder::Canvas,
1780 painter: &TextPainter,
1781 range: TextRange,
1782 color: Color,
1783 paint_offset: Offset,
1784 clip: Rect,
1785 height_style: BoxHeightStyle,
1786 width_style: BoxWidthStyle,
1787) {
1788 if !range.is_valid() || range.is_collapsed() {
1789 return;
1790 }
1791 let boxes = painter.get_boxes_for_selection(
1792 TextSelection::new(range.start, range.end),
1793 height_style,
1794 width_style,
1795 );
1796 let paint = Paint {
1797 color: color.into(),
1798 ..Paint::default()
1799 };
1800 for box_ in boxes {
1801 let rect = box_
1802 .to_rect()
1803 .shift(paint_offset)
1804 .intersect(clip.shift(paint_offset));
1805 if !rect.is_empty() {
1806 canvas.draw_rect(rect, &paint);
1807 }
1808 }
1809}
1810
1811impl RelayoutWhenSystemFontsChangeMixin for RenderEditable {
1812 fn relayout_when_system_fonts_change_data(
1813 self: RenderHandle<Self>,
1814 app: &App,
1815 ) -> &RelayoutWhenSystemFontsChangeData {
1816 &self.get(app).system_fonts
1817 }
1818
1819 fn relayout_when_system_fonts_change_data_mut(
1820 self: RenderHandle<Self>,
1821 app: &mut App,
1822 ) -> &mut RelayoutWhenSystemFontsChangeData {
1823 &mut self.get_mut(app).system_fonts
1824 }
1825
1826 fn system_fonts_did_change(self: RenderHandle<Self>, app: &mut App) {
1827 self.as_render_object(app).mark_needs_layout(app);
1828 self.get_mut(app).text_painter.mark_needs_layout();
1829 }
1830}
1831
1832impl ContainerRenderObjectMixin for RenderEditable {
1833 type ChildType = AnyRenderBox;
1834 type ParentDataType = TextParentData;
1835
1836 fn container_data(
1837 self: RenderHandle<Self>,
1838 app: &App,
1839 ) -> &ContainerRenderObjectData<AnyRenderBox> {
1840 &self.get(app).container
1841 }
1842
1843 fn container_data_mut(
1844 self: RenderHandle<Self>,
1845 app: &mut App,
1846 ) -> &mut ContainerRenderObjectData<AnyRenderBox> {
1847 &mut self.get_mut(app).container
1848 }
1849}
1850
1851impl RenderInlineChildrenContainerDefaults for RenderEditable {}
1852
1853impl RenderObject for RenderEditable {
1854 crate::render_object_accessors!();
1855
1856 fn visit_children(
1857 self: RenderHandle<Self>,
1858 app: &App,
1859 visitor: &mut dyn FnMut(AnyRenderObject),
1860 ) {
1861 ContainerRenderObjectMixin::visit_children(self, app, visitor)
1863 }
1864
1865 fn redepth_children(self: RenderHandle<Self>, app: &mut App) {
1866 ContainerRenderObjectMixin::redepth_children(self, app)
1867 }
1868
1869 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
1870 RelayoutWhenSystemFontsChangeMixin::attach_system_fonts(self, app);
1871 ContainerRenderObjectMixin::did_attach(self, app, owner);
1872 let offset = self.get(app).offset;
1873 offset.add_listener(app, self.mark_needs_paint_listener());
1874 let show_cursor = self.get(app).show_cursor;
1875 show_cursor.add_listener(app, self.mark_needs_paint_listener());
1876 }
1877
1878 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
1879 let offset = self.get(app).offset;
1880 offset.remove_listener(app, &self.mark_needs_paint_listener());
1881 let show_cursor = self.get(app).show_cursor;
1882 show_cursor.remove_listener(app, &self.mark_needs_paint_listener());
1883 RelayoutWhenSystemFontsChangeMixin::detach_system_fonts(self, app);
1884 ContainerRenderObjectMixin::did_detach(self, app);
1885 }
1886
1887 fn dispose(self: RenderHandle<Self>, app: &mut App) {
1888 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).leader_layer_handler, None);
1889 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, None);
1890 let start_in_viewport = self.get(app).selection_start_in_viewport;
1891 let end_in_viewport = self.get(app).selection_end_in_viewport;
1892 app.get_mut(start_in_viewport).dispose();
1893 app.get_mut(end_in_viewport).dispose();
1894 crate::object::RenderObjectBase::dispose(self, app);
1895 }
1896
1897 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
1898 let constraints = self.constraints(app);
1899 let placeholder_dimensions = self.layout_inline_children(
1900 app,
1901 constraints.max_width,
1902 ChildLayoutHelper::layout_child,
1903 ChildLayoutHelper::get_baseline,
1904 );
1905 self.get_mut(app).placeholder_dimensions = Some(placeholder_dimensions.clone());
1906 let (min_width, max_width) =
1907 self.adjust_constraints(app, constraints.min_width, constraints.max_width);
1908 let (painter, fonts) = self.painter_with_fonts(app);
1909 painter.set_placeholder_dimensions(Some(placeholder_dimensions));
1910 painter.layout(fonts, min_width, max_width);
1911 let boxes = self
1912 .get_mut(app)
1913 .text_painter
1914 .inline_placeholder_boxes()
1915 .expect("laid out just now");
1916 self.position_inline_children(app, &boxes);
1917 self.compute_caret_prototype(app);
1918 let caret_margin = self.caret_margin(app);
1919 let painter_width = self.get(app).text_painter.width();
1920 let painter_height = self.get(app).text_painter.height();
1921 let width = if self.get(app).force_line {
1922 constraints.max_width
1923 } else {
1924 constraints.constrain_width(painter_width + caret_margin)
1925 };
1926 let preferred_height = match self.get(app).max_lines {
1927 None => {
1928 let min_lines = self.get(app).min_lines.unwrap_or(0);
1929 painter_height.max(self.preferred_line_height(app) * f64::from(min_lines))
1930 }
1931 Some(1) => painter_height,
1932 Some(max_lines) => {
1933 let line_height = self.preferred_line_height(app);
1934 let min_lines = self.get(app).min_lines.unwrap_or(max_lines);
1935 clamp_double(
1936 painter_height,
1937 line_height * f64::from(min_lines),
1938 line_height * f64::from(max_lines),
1939 )
1940 }
1941 };
1942 let size = Size::new(width, constraints.constrain_height(preferred_height));
1943 self.set_size(app, size);
1944 let content_size = Size::new(painter_width + caret_margin, painter_height);
1945 self.get_mut(app).max_scroll_extent = self.get_max_scroll_extent(app, content_size);
1946 self.get_mut(app).layout_generation += 1;
1947 let viewport_extent = self.viewport_extent(app);
1948 let max_scroll = self.get(app).max_scroll_extent;
1949 let offset = self.get(app).offset;
1950 offset.apply_viewport_dimension(app, viewport_extent);
1951 offset.apply_content_dimensions(app, 0.0, max_scroll);
1952 }
1953
1954 fn paint(
1955 self: RenderHandle<Self>,
1956 app: &mut App,
1957 context: &mut PaintingContext,
1958 offset: Offset,
1959 ) {
1960 self.compute_text_metrics_if_needed(app);
1961 if self.has_visual_overflow(app) && self.get(app).clip_behavior != Clip::None {
1962 let size = self.size(app);
1963 let clip_behavior = self.get(app).clip_behavior;
1964 let old = self.get(app).clip_rect_layer.layer();
1965 let layer = context.push_clip_rect(
1966 app,
1967 self.as_object().needs_compositing(app),
1968 offset,
1969 Offset::ZERO & size,
1970 |app, context, offset| {
1971 self.paint_contents(app, context, offset);
1972 },
1973 clip_behavior,
1974 old,
1975 );
1976 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, layer);
1977 } else {
1978 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, None);
1979 self.paint_contents(app, context, offset);
1980 }
1981 if let Some(selection) = self.selection(app)
1982 && selection.is_valid()
1983 {
1984 let endpoints = self.get_endpoints_for_selection(app, selection);
1985 self.paint_handle_layers(app, context, &endpoints, offset);
1986 }
1987 }
1988}
1989
1990impl RenderBox for RenderEditable {
1991 crate::render_box_accessors!();
1992
1993 fn setup_parent_data(self: RenderHandle<Self>, app: &mut App, child: AnyRenderObject) {
1994 RenderInlineChildrenContainerDefaults::setup_parent_data(self, app, child);
1995 }
1996
1997 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
1998 let placeholder_dimensions = self.layout_inline_children(
1999 app,
2000 f64::INFINITY,
2001 |app, child, _| Size::new(child.get_min_intrinsic_width(app, f64::INFINITY), 0.0),
2002 ChildLayoutHelper::get_dry_baseline,
2003 );
2004 let (min_width, max_width) = self.adjust_constraints(app, 0.0, f64::INFINITY);
2005 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
2006 intrinsics.set_placeholder_dimensions(Some(placeholder_dimensions));
2007 intrinsics.layout(fonts, min_width, max_width);
2008 intrinsics.min_intrinsic_width()
2009 }
2010
2011 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, _height: f64) -> f64 {
2012 let placeholder_dimensions = self.layout_inline_children(
2013 app,
2014 f64::INFINITY,
2015 |app, child, _| Size::new(child.get_max_intrinsic_width(app, f64::INFINITY), 0.0),
2018 ChildLayoutHelper::get_dry_baseline,
2019 );
2020 let (min_width, max_width) = self.adjust_constraints(app, 0.0, f64::INFINITY);
2021 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
2022 intrinsics.set_placeholder_dimensions(Some(placeholder_dimensions));
2023 intrinsics.layout(fonts, min_width, max_width);
2024 intrinsics.max_intrinsic_width() + self.caret_margin(app)
2025 }
2026
2027 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2028 self.compute_max_intrinsic_height(app, width)
2029 }
2030
2031 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
2032 let placeholder_dimensions = self.layout_inline_children(
2033 app,
2034 width,
2035 ChildLayoutHelper::dry_layout_child,
2036 ChildLayoutHelper::get_dry_baseline,
2037 );
2038 let (intrinsics, _) = self.text_intrinsics_with_fonts(app);
2039 intrinsics.set_placeholder_dimensions(Some(placeholder_dimensions));
2040 self.preferred_height(app, width)
2041 }
2042
2043 fn compute_dry_layout(
2044 self: RenderHandle<Self>,
2045 app: &mut App,
2046 constraints: BoxConstraints,
2047 ) -> Size {
2048 let placeholder_dimensions = self.layout_inline_children(
2049 app,
2050 constraints.max_width,
2051 ChildLayoutHelper::dry_layout_child,
2052 ChildLayoutHelper::get_dry_baseline,
2053 );
2054 let (min_width, max_width) =
2055 self.adjust_constraints(app, constraints.min_width, constraints.max_width);
2056 let force_line = self.get(app).force_line;
2057 let caret_margin = self.caret_margin(app);
2058 let (intrinsics, fonts) = self.text_intrinsics_with_fonts(app);
2059 intrinsics.set_placeholder_dimensions(Some(placeholder_dimensions));
2060 intrinsics.layout(fonts, min_width, max_width);
2061 let painter_width = intrinsics.width();
2062 let width = if force_line {
2063 constraints.max_width
2064 } else {
2065 constraints.constrain_width(painter_width + caret_margin)
2066 };
2067 let height = self.preferred_height(app, constraints.max_width);
2068 Size::new(width, constraints.constrain_height(height))
2069 }
2070
2071 fn compute_distance_to_actual_baseline(
2072 self: RenderHandle<Self>,
2073 app: &mut App,
2074 baseline: TextBaseline,
2075 ) -> Option<f64> {
2076 self.compute_text_metrics_if_needed(app);
2077 Some(
2078 self.get(app)
2079 .text_painter
2080 .compute_distance_to_actual_baseline(baseline),
2081 )
2082 }
2083
2084 fn hit_test_self(self: RenderHandle<Self>, _app: &App, _position: Offset) -> bool {
2085 true
2086 }
2087
2088 fn hit_test_children(
2089 self: RenderHandle<Self>,
2090 app: &mut App,
2091 result: &mut BoxHitTestResult<'_>,
2092 position: Offset,
2093 ) -> bool {
2094 let effective_position = position - self.paint_offset(app);
2096 self.hit_test_inline_children(app, result, effective_position)
2097 }
2098
2099 fn apply_paint_transform(
2100 self: RenderHandle<Self>,
2101 app: &App,
2102 child: AnyRenderObject,
2103 transform: &mut Matrix4,
2104 ) {
2105 self.default_apply_paint_transform(app, child, transform);
2107 }
2108}
2109
2110impl RenderEditable {
2111 fn paint_handle_layers(
2114 self: RenderHandle<Self>,
2115 app: &mut App,
2116 context: &mut PaintingContext,
2117 endpoints: &[TextSelectionPoint],
2118 offset: Offset,
2119 ) {
2120 let size = self.size(app);
2121 let clamp = |point: Offset| {
2122 Offset::new(
2123 point.dx().clamp(0.0, size.width()),
2124 point.dy().clamp(0.0, size.height()),
2125 )
2126 };
2127 let Some(start) = endpoints.first() else {
2128 return;
2129 };
2130 let start_point = clamp(start.point);
2131 let (start_link, end_link) = {
2132 let this = self.get(app);
2133 (this.start_handle_layer_link, this.end_handle_layer_link)
2134 };
2135 let start_layer = LeaderLayer::new(app, start_link);
2136 start_layer.set_offset(app, start_point + offset);
2137 LayerHandle::set_layer(
2138 app,
2139 |app| &mut self.get_mut(app).leader_layer_handler,
2140 Some(start_layer),
2141 );
2142 context.push_layer(
2143 app,
2144 start_layer.as_container_layer(),
2145 |_, _, _| {},
2146 Offset::ZERO,
2147 None,
2148 );
2149 if let Some(end) = endpoints.get(1) {
2150 let end_point = clamp(end.point);
2151 let end_layer = LeaderLayer::new(app, end_link);
2152 end_layer.set_offset(app, end_point + offset);
2153 context.push_layer(
2154 app,
2155 end_layer.as_container_layer(),
2156 |_, _, _| {},
2157 Offset::ZERO,
2158 None,
2159 );
2160 } else if self
2161 .selection(app)
2162 .is_some_and(|selection| selection.is_collapsed())
2163 {
2164 let end_layer = LeaderLayer::new(app, end_link);
2165 end_layer.set_offset(app, start_point + offset);
2166 context.push_layer(
2167 app,
2168 end_layer.as_container_layer(),
2169 |_, _, _| {},
2170 Offset::ZERO,
2171 None,
2172 );
2173 }
2174 }
2175}
2176
2177#[cfg(test)]
2178mod tests {
2179 use std::cell::Cell;
2180 use std::rc::Rc;
2181
2182 use inset_embedder::{FontSource, TextAffinity};
2183 use inset_foundation::{AppCell, Task, ValueListenable};
2184 use inset_painting::{PaintingBinding, TextSpan, TextStyle};
2185 use inset_scheduler::SchedulerBinding;
2186 use inset_services::{TextEditingValue, TextSelectionDelegate};
2187 use valo::{Font, FontAttrs};
2188
2189 use super::*;
2190 use crate::layer::{ContainerLayer, ErasedLayer, OffsetLayer};
2191 use crate::pipeline_owner::PipelineOwner;
2192 use crate::proxy_box::RenderRepaintBoundary;
2193 use crate::viewport_offset::{FixedViewportOffset, ViewportOffset};
2194
2195 #[derive(Default)]
2196 struct TestDelegate {
2197 value: TextEditingValue,
2198 }
2199
2200 impl TextSelectionDelegate for TestDelegate {
2201 fn text_editing_value(self: Handle<Self>, app: &App) -> TextEditingValue {
2202 app.get(self).value.clone()
2203 }
2204
2205 fn user_update_text_editing_value(
2206 self: Handle<Self>,
2207 app: &mut App,
2208 value: TextEditingValue,
2209 _cause: SelectionChangedCause,
2210 ) {
2211 app.get_mut(self).value = value;
2212 }
2213
2214 fn hide_toolbar(self: Handle<Self>, _app: &mut App, _hide_handles: bool) {}
2215
2216 fn bring_into_view(self: Handle<Self>, _app: &mut App, _position: TextPosition) {}
2217
2218 fn cut_selection(self: Handle<Self>, _app: &mut App, _cause: SelectionChangedCause) {}
2219
2220 fn paste_text(
2221 self: Handle<Self>,
2222 _app: &mut App,
2223 _cause: SelectionChangedCause,
2224 ) -> Task<()> {
2225 Task::ready(())
2226 }
2227
2228 fn select_all(self: Handle<Self>, _app: &mut App, _cause: SelectionChangedCause) {}
2229
2230 fn copy_selection(self: Handle<Self>, _app: &mut App, _cause: SelectionChangedCause) {}
2231 }
2232
2233 fn install_fonts(app: &mut App) {
2234 let binding = PaintingBinding::instance(app);
2235 if !binding.has_fonts(app) {
2236 binding.install_fonts(app, |fonts| {
2237 fonts.add_source(valo_system_fonts::SystemFonts::load());
2238 });
2239 }
2240 }
2241
2242 fn span(text: &str) -> InlineSpanRef {
2243 TextSpan::new()
2244 .text(text)
2245 .style(TextStyle::new().font_size(20.0))
2246 .into_span()
2247 }
2248
2249 fn laid_out(app: &mut App, text: &str) -> RenderHandle<RenderEditable> {
2250 install_fonts(app);
2251 let delegate = app.create(TestDelegate {
2252 value: TextEditingValue::new()
2253 .text(text)
2254 .selection(TextSelection::collapsed(0, TextAffinity::Downstream)),
2255 });
2256 let start = LayerLink::new(app);
2257 let end = LayerLink::new(app);
2258 let offset = FixedViewportOffset::zero(app).as_viewport_offset();
2259 let editable = RenderEditable::new(
2260 app,
2261 TextDirection::Ltr,
2262 start,
2263 end,
2264 offset,
2265 delegate.as_text_selection_delegate(),
2266 );
2267 editable.set_text(app, Some(span(text)));
2268 editable.set_cursor_color(app, Some(Color::from_argb(255, 0, 0, 0)));
2269 let root = RenderRepaintBoundary::new(app, Some(editable.as_box()));
2270 let owner = PipelineOwner::new(app, None, None);
2271 owner.set_root_node(app, Some(root.as_object()));
2272 root.schedule_initial_layout(app);
2273 root.layout(
2274 app,
2275 BoxConstraints::tight_for(Some(200.0), Some(40.0)),
2276 false,
2277 );
2278 let paint_root = OffsetLayer::new(app, Offset::ZERO);
2279 paint_root.as_layer().attach(app, root.as_object().id());
2280 root.as_object()
2281 .schedule_initial_paint(app, paint_root.as_container_layer());
2282 editable
2283 }
2284
2285 #[test]
2286 fn move_by_word_boundary_skips_spaces_and_punctuation() {
2287 let cell = AppCell::new();
2288 let mut app = cell.borrow_mut();
2289 let text = concat!(
2290 "ABC ABC\n", "A\u{41}\u{301} \u{41}\u{301}\n", " \n", "ABC!!!ABC\n", " !ABC !!\n", "A \u{115CB}\u{115CB} A\n", );
2297 let editable = laid_out(&mut app, text);
2298 let boundary = editable.word_boundaries(&app).move_by_word_boundary();
2299
2300 assert_eq!(boundary.get_leading_text_boundary_at(&mut app, 4), Some(0));
2303 assert_eq!(boundary.get_trailing_text_boundary_at(&mut app, 4), Some(9));
2304
2305 assert_eq!(
2307 boundary.get_leading_text_boundary_at(&mut app, 14),
2308 Some(10)
2309 );
2310 assert_eq!(
2311 boundary.get_trailing_text_boundary_at(&mut app, 14),
2312 Some(19)
2313 );
2314
2315 assert_eq!(
2317 boundary.get_leading_text_boundary_at(&mut app, 24),
2318 Some(20)
2319 );
2320 assert_eq!(
2321 boundary.get_trailing_text_boundary_at(&mut app, 24),
2322 Some(29)
2323 );
2324
2325 assert_eq!(
2327 boundary.get_leading_text_boundary_at(&mut app, 34),
2328 Some(30)
2329 );
2330 assert_eq!(
2331 boundary.get_trailing_text_boundary_at(&mut app, 34),
2332 Some(39)
2333 );
2334
2335 assert_eq!(
2337 boundary.get_leading_text_boundary_at(&mut app, 44),
2338 Some(43)
2339 );
2340 assert_eq!(
2341 boundary.get_trailing_text_boundary_at(&mut app, 44),
2342 Some(46)
2343 );
2344
2345 assert_eq!(
2347 boundary.get_leading_text_boundary_at(&mut app, 54),
2348 Some(50)
2349 );
2350 assert_eq!(
2351 boundary.get_trailing_text_boundary_at(&mut app, 54),
2352 Some(59)
2353 );
2354 }
2355
2356 #[test]
2357 fn display_names_the_direction() {
2358 let point = Offset::new(1.0, 2.0);
2359 assert_eq!(
2360 TextSelectionPoint::new(point, Some(TextDirection::Ltr)).to_string(),
2361 format!("{point:?}-ltr")
2362 );
2363 assert_eq!(
2364 TextSelectionPoint::new(point, None).to_string(),
2365 format!("{point:?}")
2366 );
2367 }
2368
2369 #[test]
2370 fn layout_gives_the_text_a_size() {
2371 let cell = AppCell::new();
2372 let mut app = cell.borrow_mut();
2373 let editable = laid_out(&mut app, "hi");
2374 let size = editable.size(&app);
2375 assert!(size.width() > 0.0);
2376 assert!(size.height() > 0.0);
2377 }
2378
2379 #[test]
2380 fn select_position_updates_the_delegate() {
2381 let cell = AppCell::new();
2382 let mut app = cell.borrow_mut();
2383 let editable = laid_out(&mut app, "hello");
2384 let origin = editable.as_box().local_to_global(&app, Offset::ZERO, None);
2385 editable.select_position_at(
2386 &mut app,
2387 origin + Offset::new(2.0, 2.0),
2388 None,
2389 SelectionChangedCause::Tap,
2390 );
2391 let value = editable
2392 .text_selection_delegate(&app)
2393 .text_editing_value(&app);
2394 assert!(value.selection.is_valid());
2395 assert!(value.selection.is_collapsed());
2396 }
2397
2398 #[test]
2399 fn handle_secondary_tap_down_records_the_position() {
2400 let cell = AppCell::new();
2401 let mut app = cell.borrow_mut();
2402 let editable = laid_out(&mut app, "hello");
2403 let at = Offset::new(12.0, 8.0);
2404 editable.handle_secondary_tap_down(&mut app, &TapDownDetails::new(at, None, None));
2405 assert_eq!(editable.last_secondary_tap_down_position(&app), Some(at));
2406 }
2407
2408 #[test]
2409 fn selection_extents_default_in_the_viewport() {
2410 let cell = AppCell::new();
2411 let mut app = cell.borrow_mut();
2412 let editable = laid_out(&mut app, "hello");
2413 assert!(
2414 *editable.selection_start_in_viewport(&app).value(&app),
2415 "Flutter defaults selectionStartInViewport to true"
2416 );
2417 assert!(
2418 *editable.selection_end_in_viewport(&app).value(&app),
2419 "Flutter defaults selectionEndInViewport to true"
2420 );
2421 }
2422
2423 #[test]
2424 fn system_fonts_change_marks_the_editable_dirty() {
2425 let cell = AppCell::new();
2426 let mut app = cell.borrow_mut();
2427 let editable = laid_out(&mut app, "hello");
2428 assert!(!editable.as_object().debug_needs_layout(&app));
2429 PaintingBinding::instance(&mut app).handle_system_fonts_did_change(&mut app);
2430 SchedulerBinding::handle_begin_frame(&mut app, &[], None);
2431 assert!(
2432 editable.as_object().debug_needs_layout(&app),
2433 "RenderEditable listens to PaintingBinding.system_fonts"
2434 );
2435 }
2436
2437 struct GatedFonts {
2440 inner: valo_system_fonts::SystemFonts,
2441 allow_non_ascii: Rc<Cell<bool>>,
2442 }
2443
2444 impl FontSource for GatedFonts {
2445 fn family(&mut self, _name: &str) -> Vec<Font> {
2446 Vec::new()
2449 }
2450
2451 fn face_for_codepoint(&mut self, codepoint: char, attrs: FontAttrs) -> Option<Font> {
2452 if !self.allow_non_ascii.get() && !codepoint.is_ascii() {
2453 return None;
2454 }
2455 self.inner.face_for_codepoint(codepoint, attrs)
2456 }
2457 }
2458
2459 #[test]
2460 fn system_fonts_change_rebuilds_glyphs_once_a_fallback_face_arrives() {
2461 let allow_non_ascii = Rc::new(Cell::new(false));
2462 let cell = AppCell::new();
2463 let mut app = cell.borrow_mut();
2464 PaintingBinding::instance(&mut app).install_fonts(&mut app, {
2465 let allow_non_ascii = Rc::clone(&allow_non_ascii);
2466 move |fonts| {
2467 fonts.add_source(GatedFonts {
2468 inner: valo_system_fonts::SystemFonts::load(),
2469 allow_non_ascii,
2470 });
2471 }
2472 });
2473
2474 let text = "a你";
2475 let delegate = app.create(TestDelegate {
2476 value: TextEditingValue::new()
2477 .text(text)
2478 .selection(TextSelection::collapsed(0, TextAffinity::Downstream)),
2479 });
2480 let start = LayerLink::new(&mut app);
2481 let end = LayerLink::new(&mut app);
2482 let offset = FixedViewportOffset::zero(&mut app).as_viewport_offset();
2483 let editable = RenderEditable::new(
2484 &mut app,
2485 TextDirection::Ltr,
2486 start,
2487 end,
2488 offset,
2489 delegate.as_text_selection_delegate(),
2490 );
2491 editable.set_text(&mut app, Some(span(text)));
2492 let root = RenderRepaintBoundary::new(&mut app, Some(editable.as_box()));
2493 let owner = PipelineOwner::new(&mut app, None, None);
2494 owner.set_root_node(&mut app, Some(root.as_object()));
2495 root.schedule_initial_layout(&mut app);
2496 let constraints = BoxConstraints::tight_for(Some(200.0), Some(40.0));
2497 root.layout(&mut app, constraints, false);
2498
2499 let cjk = TextRange::new(1, 2);
2500 let before = editable
2501 .get_rect_for_composing_range(&mut app, cjk)
2502 .expect("an uncovered CJK character still occupies .notdef space");
2503
2504 allow_non_ascii.set(true);
2505 PaintingBinding::instance(&mut app).handle_system_fonts_did_change(&mut app);
2506 SchedulerBinding::handle_begin_frame(&mut app, &[], None);
2507 root.layout(&mut app, constraints, false);
2508
2509 let after = editable
2510 .get_rect_for_composing_range(&mut app, cjk)
2511 .expect("systemFontsDidChange rebuilds the painter against the new face");
2512 assert_ne!(
2513 before.width(),
2514 after.width(),
2515 "the CJK glyph replaces .notdef once its face is available"
2516 );
2517 }
2518}