zng_wgt_text/node/
layout.rs

1use std::{mem, sync::Arc};
2
3use atomic::Atomic;
4use parking_lot::RwLock;
5use zng_app::{
6    DInstant,
7    event::{AnyEventArgs as _, CommandHandle, EventHandle, EventHandles},
8    widget::{
9        WIDGET,
10        node::{UiNode, UiNodeOp, match_node},
11    },
12};
13use zng_ext_font::{CaretIndex, FontFaceList, Hyphens, SegmentedText, ShapedText, TextShapingArgs, font_features::FontVariations};
14use zng_ext_input::{
15    focus::FOCUS,
16    keyboard::{KEY_INPUT_EVENT, KEYBOARD},
17    mouse::{MOUSE, MOUSE_INPUT_EVENT, MOUSE_MOVE_EVENT},
18    pointer_capture::{POINTER_CAPTURE, POINTER_CAPTURE_EVENT},
19    touch::{TOUCH_INPUT_EVENT, TOUCH_LONG_PRESS_EVENT, TOUCH_TAP_EVENT},
20};
21use zng_ext_l10n::LANG_VAR;
22use zng_ext_undo::UNDO;
23use zng_ext_window::WidgetInfoBuilderImeArea as _;
24use zng_layout::{
25    context::{InlineConstraints, InlineConstraintsMeasure, InlineSegment, LAYOUT, LayoutMetrics},
26    unit::{DipPoint, FactorUnits as _, Px, PxBox, PxConstraints2d, PxRect, PxSize, PxTransform, Rect, Size},
27};
28use zng_view_api::keyboard::{Key, KeyState};
29use zng_wgt::prelude::*;
30use zng_wgt_scroll::{SCROLL, cmd::ScrollToMode};
31
32use crate::{
33    ACCEPTS_ENTER_VAR, ACCEPTS_TAB_VAR, AUTO_SELECTION_VAR, AutoSelection, FONT_FAMILY_VAR, FONT_FEATURES_VAR, FONT_SIZE_VAR,
34    FONT_STRETCH_VAR, FONT_STYLE_VAR, FONT_VARIATIONS_VAR, FONT_WEIGHT_VAR, HYPHEN_CHAR_VAR, HYPHENS_VAR, IME_UNDERLINE_THICKNESS_VAR,
35    JUSTIFY_MODE_VAR, LETTER_SPACING_VAR, LINE_BREAK_VAR, LINE_HEIGHT_VAR, LINE_SPACING_VAR, OBSCURE_TXT_VAR, OBSCURING_CHAR_VAR,
36    OVERLINE_THICKNESS_VAR, STRIKETHROUGH_THICKNESS_VAR, TAB_LENGTH_VAR, TEXT_ALIGN_VAR, TEXT_EDITABLE_VAR, TEXT_OVERFLOW_ALIGN_VAR,
37    TEXT_OVERFLOW_VAR, TEXT_SELECTABLE_VAR, TEXT_WRAP_VAR, TextOverflow, UNDERLINE_POSITION_VAR, UNDERLINE_SKIP_VAR,
38    UNDERLINE_THICKNESS_VAR, UnderlinePosition, UnderlineSkip, WORD_BREAK_VAR, WORD_SPACING_VAR,
39    cmd::{SELECT_ALL_CMD, SELECT_CMD, TextSelectOp},
40    node::SelectionBy,
41};
42
43use super::{LAIDOUT_TEXT, LaidoutText, PendingLayout, RenderInfo, TEXT};
44
45/// An UI node that layouts the parent [`ResolvedText`] defined by the text context vars.
46///
47/// This node setups the [`LaidoutText`] for all inner nodes, the `Text!` widget includes this
48/// node in the `NestGroup::CHILD_LAYOUT + 100` nest group, so all properties in [`NestGroup::CHILD_LAYOUT`]
49/// can affect the layout normally and custom properties can be created to be inside this group and have access
50/// to the [`TEXT::laidout`] method.
51///
52/// [`ResolvedText`]: super::ResolvedText
53///
54/// [`NestGroup::CHILD_LAYOUT`]: zng_wgt::prelude::NestGroup::CHILD_LAYOUT
55pub fn layout_text(child: impl UiNode) -> impl UiNode {
56    let child = layout_text_edit(child);
57    let child = layout_text_layout(child);
58    layout_text_context(child)
59}
60fn layout_text_context(child: impl UiNode) -> impl UiNode {
61    let mut laidout = None;
62    match_node(child, move |child, op| match op {
63        UiNodeOp::Init => {
64            let fonts = FontFaceList::empty().sized(Px(10), vec![]);
65            laidout = Some(Arc::new(RwLock::new(LaidoutText {
66                shaped_text: ShapedText::new(fonts.best()),
67                fonts,
68                overflow: None,
69                overflow_suffix: None,
70                shaped_text_version: 0,
71                overlines: vec![],
72                overline_thickness: Px(0),
73                strikethroughs: vec![],
74                strikethrough_thickness: Px(0),
75                underlines: vec![],
76                underline_thickness: Px(0),
77                ime_underlines: vec![],
78                ime_underline_thickness: Px(0),
79                caret_origin: None,
80                caret_selection_origin: None,
81                caret_retained_x: Px(0),
82                render_info: RenderInfo {
83                    transform: PxTransform::identity(),
84                    scale_factor: 1.fct(),
85                },
86                viewport: PxSize::zero(),
87            })));
88
89            LAIDOUT_TEXT.with_context(&mut laidout, || child.init());
90        }
91        UiNodeOp::Deinit => {
92            LAIDOUT_TEXT.with_context(&mut laidout, || child.deinit());
93
94            laidout = None;
95        }
96        op => LAIDOUT_TEXT.with_context(&mut laidout, || child.op(op)),
97    })
98}
99fn layout_text_layout(child: impl UiNode) -> impl UiNode {
100    let mut txt = LayoutTextFinal {
101        shaping_args: TextShapingArgs::default(),
102        pending: PendingLayout::empty(),
103        txt_is_measured: false,
104        last_layout: (LayoutMetrics::new(1.fct(), PxSize::zero(), Px(0)), None),
105        baseline: Px(0),
106    };
107
108    match_node(child, move |child, op| match op {
109        UiNodeOp::Init => {
110            WIDGET
111                .sub_var(&FONT_SIZE_VAR)
112                .sub_var(&FONT_VARIATIONS_VAR)
113                .sub_var(&LETTER_SPACING_VAR)
114                .sub_var(&WORD_SPACING_VAR)
115                .sub_var(&LINE_SPACING_VAR)
116                .sub_var(&LINE_HEIGHT_VAR)
117                .sub_var(&TAB_LENGTH_VAR);
118            WIDGET
119                .sub_var(&UNDERLINE_POSITION_VAR)
120                .sub_var(&UNDERLINE_SKIP_VAR)
121                .sub_var_layout(&OVERLINE_THICKNESS_VAR)
122                .sub_var_layout(&STRIKETHROUGH_THICKNESS_VAR)
123                .sub_var_layout(&UNDERLINE_THICKNESS_VAR);
124            WIDGET
125                .sub_var(&LINE_BREAK_VAR)
126                .sub_var(&WORD_BREAK_VAR)
127                .sub_var(&HYPHENS_VAR)
128                .sub_var(&HYPHEN_CHAR_VAR)
129                .sub_var(&TEXT_WRAP_VAR)
130                .sub_var(&TEXT_OVERFLOW_VAR);
131            WIDGET
132                .sub_var_layout(&TEXT_ALIGN_VAR)
133                .sub_var_layout(&JUSTIFY_MODE_VAR)
134                .sub_var_layout(&TEXT_OVERFLOW_ALIGN_VAR);
135
136            WIDGET.sub_var(&FONT_FEATURES_VAR);
137
138            WIDGET.sub_var(&OBSCURE_TXT_VAR).sub_var(&OBSCURING_CHAR_VAR);
139
140            // LANG_VAR already subscribed by resolve_text
141
142            txt.shaping_args.lang = LANG_VAR.with(|l| l.best().clone());
143            txt.shaping_args.direction = txt.shaping_args.lang.direction();
144            txt.shaping_args.line_break = LINE_BREAK_VAR.get();
145            txt.shaping_args.word_break = WORD_BREAK_VAR.get();
146            txt.shaping_args.hyphens = HYPHENS_VAR.get();
147            txt.shaping_args.hyphen_char = HYPHEN_CHAR_VAR.get();
148            txt.shaping_args.font_features = FONT_FEATURES_VAR.with(|f| f.finalize());
149
150            if OBSCURE_TXT_VAR.get() {
151                txt.shaping_args.obscuring_char = Some(OBSCURING_CHAR_VAR.get());
152            }
153        }
154        UiNodeOp::Deinit => {
155            txt.shaping_args = TextShapingArgs::default();
156        }
157        UiNodeOp::Update { .. } => {
158            if FONT_SIZE_VAR.is_new() || FONT_VARIATIONS_VAR.is_new() {
159                txt.pending.insert(PendingLayout::RESHAPE);
160                TEXT.layout().overflow_suffix = None;
161                WIDGET.layout();
162            }
163
164            if LETTER_SPACING_VAR.is_new()
165                || WORD_SPACING_VAR.is_new()
166                || LINE_SPACING_VAR.is_new()
167                || LINE_HEIGHT_VAR.is_new()
168                || TAB_LENGTH_VAR.is_new()
169                || LANG_VAR.is_new()
170            {
171                txt.shaping_args.lang = LANG_VAR.with(|l| l.best().clone());
172                txt.shaping_args.direction = txt.shaping_args.lang.direction(); // will be set in layout too.
173                TEXT.layout().overflow_suffix = None;
174                txt.pending.insert(PendingLayout::RESHAPE);
175                WIDGET.layout();
176            }
177
178            if UNDERLINE_POSITION_VAR.is_new() || UNDERLINE_SKIP_VAR.is_new() {
179                txt.pending.insert(PendingLayout::UNDERLINE);
180                WIDGET.layout();
181            }
182
183            if let Some(lb) = LINE_BREAK_VAR.get_new() {
184                if txt.shaping_args.line_break != lb {
185                    txt.shaping_args.line_break = lb;
186                    txt.pending.insert(PendingLayout::RESHAPE);
187                    WIDGET.layout();
188                }
189            }
190            if let Some(wb) = WORD_BREAK_VAR.get_new() {
191                if txt.shaping_args.word_break != wb {
192                    txt.shaping_args.word_break = wb;
193                    txt.pending.insert(PendingLayout::RESHAPE);
194                    WIDGET.layout();
195                }
196            }
197            if let Some(h) = HYPHENS_VAR.get_new() {
198                if txt.shaping_args.hyphens != h {
199                    txt.shaping_args.hyphens = h;
200                    txt.pending.insert(PendingLayout::RESHAPE);
201                    WIDGET.layout();
202                }
203            }
204            if let Some(c) = HYPHEN_CHAR_VAR.get_new() {
205                txt.shaping_args.hyphen_char = c;
206                if Hyphens::None != txt.shaping_args.hyphens {
207                    txt.pending.insert(PendingLayout::RESHAPE);
208                    WIDGET.layout();
209                }
210            }
211
212            if OBSCURE_TXT_VAR.is_new() || OBSCURING_CHAR_VAR.is_new() {
213                let c = if OBSCURE_TXT_VAR.get() {
214                    Some(OBSCURING_CHAR_VAR.get())
215                } else {
216                    None
217                };
218                if txt.shaping_args.obscuring_char != c {
219                    txt.shaping_args.obscuring_char = c;
220                    txt.pending.insert(PendingLayout::RESHAPE);
221                    WIDGET.layout();
222                }
223            }
224
225            if TEXT_WRAP_VAR.is_new() {
226                txt.pending.insert(PendingLayout::RESHAPE);
227                WIDGET.layout();
228            }
229            if TEXT_OVERFLOW_VAR.is_new() {
230                TEXT.layout().overflow_suffix = None;
231                txt.pending.insert(PendingLayout::OVERFLOW);
232                WIDGET.layout();
233            }
234
235            FONT_FEATURES_VAR.with_new(|f| {
236                txt.shaping_args.font_features = f.finalize();
237                txt.pending.insert(PendingLayout::RESHAPE);
238                WIDGET.layout();
239            });
240
241            if FONT_FAMILY_VAR.is_new()
242                || FONT_STYLE_VAR.is_new()
243                || FONT_STRETCH_VAR.is_new()
244                || FONT_WEIGHT_VAR.is_new()
245                || LANG_VAR.is_new()
246            {
247                // resolve_text already requests RESHAPE
248                TEXT.layout().overflow_suffix = None;
249            }
250        }
251        UiNodeOp::Measure { wm, desired_size } => {
252            child.delegated();
253
254            let metrics = LAYOUT.metrics();
255
256            *desired_size = if let Some(size) = txt.measure(&metrics) {
257                size
258            } else {
259                let size = txt.layout(&metrics, true);
260
261                if let Some(inline) = wm.inline() {
262                    let ctx = TEXT.laidout();
263
264                    if let Some(first_line) = ctx.shaped_text.line(0) {
265                        inline.first = first_line.original_size();
266                        inline.with_first_segs(|i| {
267                            for seg in first_line.segs() {
268                                i.push(InlineSegment {
269                                    width: seg.advance(),
270                                    kind: seg.kind(),
271                                });
272                            }
273                        });
274                    } else {
275                        inline.first = PxSize::zero();
276                        inline.with_first_segs(|i| i.clear());
277                    }
278
279                    if ctx.shaped_text.lines_len() == 1 {
280                        inline.last = inline.first;
281                        inline.last_segs = inline.first_segs.clone();
282                    } else if let Some(last_line) = ctx.shaped_text.line(ctx.shaped_text.lines_len().saturating_sub(1)) {
283                        inline.last = last_line.original_size();
284                        inline.with_last_segs(|i| {
285                            for seg in last_line.segs() {
286                                i.push(InlineSegment {
287                                    width: seg.advance(),
288                                    kind: seg.kind(),
289                                })
290                            }
291                        })
292                    } else {
293                        inline.last = PxSize::zero();
294                        inline.with_last_segs(|i| i.clear());
295                    }
296
297                    inline.first_wrapped = ctx.shaped_text.first_wrapped();
298                    inline.last_wrapped = ctx.shaped_text.lines_len() > 1;
299                }
300                size
301            };
302
303            LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*desired_size), || {
304                // foreign nodes in the CHILD_LAYOUT+100 ..= CHILD range may change the size
305                *desired_size = child.measure(wm);
306            });
307        }
308        UiNodeOp::Layout { wl, final_size } => {
309            child.delegated();
310
311            let metrics = LAYOUT.metrics();
312
313            TEXT.layout().viewport = metrics.viewport();
314
315            *final_size = txt.layout(&metrics, false);
316
317            if txt.pending != PendingLayout::empty() {
318                WIDGET.render();
319                txt.pending = PendingLayout::empty();
320            }
321
322            if let Some(inline) = wl.inline() {
323                let ctx = TEXT.laidout();
324
325                let last_line = ctx.shaped_text.lines_len().saturating_sub(1);
326
327                inline.first_segs.clear();
328                inline.last_segs.clear();
329
330                for (i, line) in ctx.shaped_text.lines().enumerate() {
331                    if i == 0 {
332                        let info = ctx.shaped_text.line(0).unwrap().segs().map(|s| s.inline_info());
333                        if LAYOUT.direction().is_rtl() {
334                            // help sort
335                            inline.set_first_segs(info.rev());
336                        } else {
337                            inline.set_first_segs(info);
338                        }
339                    } else if i == last_line {
340                        let info = ctx
341                            .shaped_text
342                            .line(ctx.shaped_text.lines_len().saturating_sub(1))
343                            .unwrap()
344                            .segs()
345                            .map(|s| s.inline_info());
346                        if LAYOUT.direction().is_rtl() {
347                            // help sort
348                            inline.set_last_segs(info.rev());
349                        } else {
350                            inline.set_last_segs(info);
351                        }
352                    }
353
354                    inline.rows.push(line.rect());
355                }
356            }
357
358            wl.set_baseline(txt.baseline);
359
360            LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*final_size), || {
361                // foreign nodes in the CHILD_LAYOUT+100 ..= CHILD range may change the size
362                *final_size = child.layout(wl);
363            });
364        }
365        UiNodeOp::Render { .. } => {
366            txt.ensure_layout_for_render();
367        }
368        UiNodeOp::RenderUpdate { .. } => {
369            txt.ensure_layout_for_render();
370        }
371        _ => {}
372    })
373}
374struct LayoutTextFinal {
375    shaping_args: TextShapingArgs,
376    pending: PendingLayout,
377
378    txt_is_measured: bool,
379    last_layout: (LayoutMetrics, Option<InlineConstraintsMeasure>),
380    baseline: Px,
381}
382impl LayoutTextFinal {
383    fn measure(&mut self, metrics: &LayoutMetrics) -> Option<PxSize> {
384        if metrics.inline_constraints().is_some() {
385            return None;
386        }
387
388        metrics.constraints().fill_or_exact()
389    }
390
391    fn layout(&mut self, metrics: &LayoutMetrics, is_measure: bool) -> PxSize {
392        let mut resolved = TEXT.resolved();
393
394        self.pending |= resolved.pending_layout;
395
396        let font_size = metrics.font_size();
397
398        let mut ctx = TEXT.layout();
399
400        if font_size != ctx.fonts.requested_size() || !ctx.fonts.is_sized_from(&resolved.faces) {
401            ctx.fonts = resolved.faces.sized(font_size, FONT_VARIATIONS_VAR.with(FontVariations::finalize));
402            self.pending.insert(PendingLayout::RESHAPE);
403        }
404
405        if TEXT_WRAP_VAR.get() && !metrics.constraints().x.is_unbounded() {
406            let max_width = metrics.constraints().x.max().unwrap();
407            if self.shaping_args.max_width != max_width {
408                self.shaping_args.max_width = max_width;
409
410                if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(max_width) {
411                    self.pending.insert(PendingLayout::RESHAPE);
412                }
413            }
414        } else if self.shaping_args.max_width != Px::MAX {
415            self.shaping_args.max_width = Px::MAX;
416            if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(Px::MAX) {
417                self.pending.insert(PendingLayout::RESHAPE);
418            }
419        }
420
421        if ctx.caret_origin.is_none() {
422            self.pending.insert(PendingLayout::CARET);
423        }
424
425        if let Some(inline) = metrics.inline_constraints() {
426            match inline {
427                InlineConstraints::Measure(m) => {
428                    if self.shaping_args.inline_constraints != Some(m) {
429                        self.shaping_args.inline_constraints = Some(m);
430                        self.pending.insert(PendingLayout::RESHAPE);
431                    }
432                }
433                InlineConstraints::Layout(l) => {
434                    if !self.pending.contains(PendingLayout::RESHAPE)
435                        && (Some(l.first_segs.len()) != ctx.shaped_text.line(0).map(|l| l.segs_len())
436                            || Some(l.last_segs.len())
437                                != ctx
438                                    .shaped_text
439                                    .line(ctx.shaped_text.lines_len().saturating_sub(1))
440                                    .map(|l| l.segs_len()))
441                    {
442                        self.pending.insert(PendingLayout::RESHAPE);
443                    }
444
445                    if !self.pending.contains(PendingLayout::RESHAPE_LINES)
446                        && (ctx.shaped_text.mid_clear() != l.mid_clear
447                            || ctx.shaped_text.line(0).map(|l| l.rect()) != Some(l.first)
448                            || ctx
449                                .shaped_text
450                                .line(ctx.shaped_text.lines_len().saturating_sub(1))
451                                .map(|l| l.rect())
452                                != Some(l.last))
453                    {
454                        self.pending.insert(PendingLayout::RESHAPE_LINES);
455                    }
456                }
457            }
458        } else if self.shaping_args.inline_constraints.is_some() {
459            self.shaping_args.inline_constraints = None;
460            self.pending.insert(PendingLayout::RESHAPE);
461        }
462
463        if !self.pending.contains(PendingLayout::RESHAPE_LINES) {
464            let size = ctx.shaped_text.size();
465            if metrics.constraints().fill_size_or(size) != ctx.shaped_text.align_size() {
466                self.pending.insert(PendingLayout::RESHAPE_LINES);
467            }
468        }
469
470        let font = ctx.fonts.best();
471
472        let space_len = font.space_x_advance();
473        let dft_tab_len = space_len * 3;
474
475        let (letter_spacing, word_spacing, tab_length) = {
476            LAYOUT.with_constraints(PxConstraints2d::new_exact(space_len, space_len), || {
477                (
478                    LETTER_SPACING_VAR.layout_x(),
479                    WORD_SPACING_VAR.layout_x(),
480                    TAB_LENGTH_VAR.layout_dft_x(dft_tab_len),
481                )
482            })
483        };
484
485        let dft_line_height = font.metrics().line_height();
486        let line_height = {
487            LAYOUT.with_constraints(PxConstraints2d::new_exact(dft_line_height, dft_line_height), || {
488                LINE_HEIGHT_VAR.layout_dft_y(dft_line_height)
489            })
490        };
491        let line_spacing =
492            { LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || LINE_SPACING_VAR.layout_y()) };
493
494        if !self.pending.contains(PendingLayout::RESHAPE)
495            && (letter_spacing != self.shaping_args.letter_spacing
496                || word_spacing != self.shaping_args.word_spacing
497                || tab_length != self.shaping_args.tab_x_advance)
498        {
499            self.pending.insert(PendingLayout::RESHAPE);
500        }
501        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
502            && (line_spacing != self.shaping_args.line_spacing || line_height != self.shaping_args.line_height)
503        {
504            self.pending.insert(PendingLayout::RESHAPE_LINES);
505        }
506
507        self.shaping_args.letter_spacing = letter_spacing;
508        self.shaping_args.word_spacing = word_spacing;
509        self.shaping_args.tab_x_advance = tab_length;
510        self.shaping_args.line_height = line_height;
511        self.shaping_args.line_spacing = line_spacing;
512
513        let dft_thickness = font.metrics().underline_thickness;
514        let (overline, strikethrough, underline, ime_underline) = {
515            LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || {
516                (
517                    OVERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
518                    STRIKETHROUGH_THICKNESS_VAR.layout_dft_y(dft_thickness),
519                    UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
520                    IME_UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
521                )
522            })
523        };
524
525        if !self.pending.contains(PendingLayout::OVERLINE) && (ctx.overline_thickness == 0) != (overline == 0) {
526            self.pending.insert(PendingLayout::OVERLINE);
527        }
528        if !self.pending.contains(PendingLayout::STRIKETHROUGH) && (ctx.strikethrough_thickness == 0) != (strikethrough == 0) {
529            self.pending.insert(PendingLayout::STRIKETHROUGH);
530        }
531        if !self.pending.contains(PendingLayout::UNDERLINE)
532            && ((ctx.underline_thickness == 0) != (underline == 0) || (ctx.ime_underline_thickness != 0) != (ime_underline != 0))
533        {
534            self.pending.insert(PendingLayout::UNDERLINE);
535        }
536        ctx.overline_thickness = overline;
537        ctx.strikethrough_thickness = strikethrough;
538        ctx.underline_thickness = underline;
539        ctx.ime_underline_thickness = ime_underline;
540
541        let align = TEXT_ALIGN_VAR.get();
542        let justify = JUSTIFY_MODE_VAR.get();
543        let overflow_align = TEXT_OVERFLOW_ALIGN_VAR.get();
544        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
545            && (align != ctx.shaped_text.align()
546                || justify != ctx.shaped_text.justify_mode().unwrap_or_default()
547                || overflow_align != ctx.shaped_text.overflow_align())
548        {
549            self.pending.insert(PendingLayout::RESHAPE_LINES);
550        }
551
552        /*
553            APPLY
554        */
555
556        if self.pending.contains(PendingLayout::RESHAPE) {
557            ctx.shaped_text = ctx.fonts.shape_text(&resolved.segmented_text, &self.shaping_args);
558            self.pending = self.pending.intersection(PendingLayout::RESHAPE_LINES);
559        }
560
561        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
562            && ctx.shaped_text.align_size() != metrics.constraints().fill_size_or(ctx.shaped_text.block_size())
563        {
564            self.pending.insert(PendingLayout::RESHAPE_LINES);
565        }
566
567        if self.pending.contains(PendingLayout::RESHAPE_LINES) && metrics.inline_constraints().is_none() {
568            // Affects block size in measure too
569            //
570            // This breaks inline context, so it is avoided here and called later in the `if !is_measure` block.
571            // This is needed to measure the same block size a layout call would output.
572            //
573            // Not sure if it is a bug that it does not work inlining, but it is not needed there anyway, so for now
574            // this fix is sufficient.
575            ctx.shaped_text.reshape_lines(
576                metrics.constraints(),
577                metrics.inline_constraints().map(|c| c.layout()),
578                align,
579                overflow_align,
580                line_height,
581                line_spacing,
582                metrics.direction(),
583            );
584        }
585
586        if !is_measure {
587            self.last_layout = (metrics.clone(), self.shaping_args.inline_constraints);
588
589            if self.pending.contains(PendingLayout::RESHAPE_LINES) {
590                if metrics.inline_constraints().is_some() {
591                    // when inlining, only reshape lines in layout passes
592                    ctx.shaped_text.reshape_lines(
593                        metrics.constraints(),
594                        metrics.inline_constraints().map(|c| c.layout()),
595                        align,
596                        overflow_align,
597                        line_height,
598                        line_spacing,
599                        metrics.direction(),
600                    );
601                }
602                ctx.shaped_text.reshape_lines_justify(justify, &self.shaping_args.lang);
603
604                ctx.shaped_text_version = ctx.shaped_text_version.wrapping_add(1);
605                drop(resolved);
606                self.baseline = ctx.shaped_text.baseline();
607                resolved = TEXT.resolved();
608                ctx.caret_origin = None;
609                ctx.caret_selection_origin = None;
610            }
611            if self.pending.contains(PendingLayout::OVERFLOW) {
612                let txt_size = ctx.shaped_text.size();
613                let max_size = metrics.constraints().fill_size_or(txt_size);
614                if txt_size.width > max_size.width || txt_size.height > max_size.height {
615                    let suf_width = ctx.overflow_suffix.as_ref().map(|s| s.size().width).unwrap_or(Px(0));
616                    ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf_width);
617
618                    if ctx.overflow.is_some() && ctx.overflow_suffix.is_none() && !TEXT_EDITABLE_VAR.get() {
619                        match TEXT_OVERFLOW_VAR.get() {
620                            TextOverflow::Truncate(suf) if !suf.is_empty() => {
621                                let suf = SegmentedText::new(suf, self.shaping_args.direction);
622                                let suf = ctx.fonts.shape_text(&suf, &self.shaping_args);
623
624                                ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf.size().width);
625                                ctx.overflow_suffix = Some(suf);
626                            }
627                            _ => {}
628                        }
629                    }
630                } else {
631                    ctx.overflow = None;
632                }
633            }
634            if self.pending.contains(PendingLayout::OVERLINE) {
635                if ctx.overline_thickness > Px(0) {
636                    ctx.overlines = ctx.shaped_text.lines().map(|l| l.overline()).collect();
637                } else {
638                    ctx.overlines = vec![];
639                }
640            }
641            if self.pending.contains(PendingLayout::STRIKETHROUGH) {
642                if ctx.strikethrough_thickness > Px(0) {
643                    ctx.strikethroughs = ctx.shaped_text.lines().map(|l| l.strikethrough()).collect();
644                } else {
645                    ctx.strikethroughs = vec![];
646                }
647            }
648
649            if self.pending.contains(PendingLayout::UNDERLINE) {
650                let ime_range = if let Some(ime) = &resolved.ime_preview {
651                    let start = ime.prev_selection.unwrap_or(ime.prev_caret).index.min(ime.prev_caret.index);
652                    start..start + ime.txt.len()
653                } else {
654                    0..0
655                };
656                let caret_ime_range = if !ime_range.is_empty() && (ctx.underline_thickness > Px(0) || ctx.ime_underline_thickness > Px(0)) {
657                    let start = ctx.shaped_text.snap_caret_line(CaretIndex {
658                        index: ime_range.start,
659                        line: 0,
660                    });
661                    let end = ctx.shaped_text.snap_caret_line(CaretIndex {
662                        index: ime_range.end,
663                        line: 0,
664                    });
665
666                    start..end
667                } else {
668                    CaretIndex::ZERO..CaretIndex::ZERO
669                };
670
671                if ctx.underline_thickness > Px(0) {
672                    let mut underlines = vec![];
673
674                    let skip = UNDERLINE_SKIP_VAR.get();
675                    match UNDERLINE_POSITION_VAR.get() {
676                        UnderlinePosition::Font => {
677                            if skip == UnderlineSkip::GLYPHS | UnderlineSkip::SPACES {
678                                for line in ctx.shaped_text.lines() {
679                                    for und in line.underline_skip_glyphs_and_spaces(ctx.underline_thickness) {
680                                        underlines.push(und);
681                                    }
682                                }
683                            } else if skip.contains(UnderlineSkip::GLYPHS) {
684                                for line in ctx.shaped_text.lines() {
685                                    for und in line.underline_skip_glyphs(ctx.underline_thickness) {
686                                        underlines.push(und);
687                                    }
688                                }
689                            } else if skip.contains(UnderlineSkip::SPACES) {
690                                for line in ctx.shaped_text.lines() {
691                                    for und in line.underline_skip_spaces() {
692                                        underlines.push(und);
693                                    }
694                                }
695                            } else {
696                                for line in ctx.shaped_text.lines() {
697                                    let und = line.underline();
698                                    underlines.push(und);
699                                }
700                            }
701                        }
702                        UnderlinePosition::Descent => {
703                            // descent clears all glyphs, so we only need to care about spaces
704                            if skip.contains(UnderlineSkip::SPACES) {
705                                for line in ctx.shaped_text.lines() {
706                                    for und in line.underline_descent_skip_spaces() {
707                                        underlines.push(und);
708                                    }
709                                }
710                            } else {
711                                for line in ctx.shaped_text.lines() {
712                                    let und = line.underline_descent();
713                                    underlines.push(und);
714                                }
715                            }
716                        }
717                    }
718
719                    if !ime_range.is_empty() {
720                        underlines = ctx.shaped_text.clip_lines(
721                            caret_ime_range.clone(),
722                            true,
723                            resolved.segmented_text.text(),
724                            underlines.into_iter(),
725                        );
726                    }
727
728                    ctx.underlines = underlines;
729                } else {
730                    ctx.underlines = vec![];
731                }
732
733                if ctx.ime_underline_thickness > Px(0) && !ime_range.is_empty() {
734                    let mut ime_underlines = vec![];
735
736                    // collects underlines for all segments that intersect with the IME text.
737                    for line in ctx.shaped_text.lines() {
738                        let line_range = line.text_range();
739                        if line_range.start < ime_range.end && line_range.end > ime_range.start {
740                            for seg in line.segs() {
741                                let seg_range = seg.text_range();
742                                if seg_range.start < ime_range.end && seg_range.end > ime_range.start {
743                                    for und in seg.underline_skip_glyphs(ctx.ime_underline_thickness) {
744                                        ime_underlines.push(und);
745                                    }
746                                }
747                            }
748                        }
749                    }
750
751                    ctx.ime_underlines =
752                        ctx.shaped_text
753                            .clip_lines(caret_ime_range, false, resolved.segmented_text.text(), ime_underlines.into_iter());
754                } else {
755                    ctx.ime_underlines = vec![];
756                }
757            }
758
759            if self.pending.contains(PendingLayout::CARET) {
760                drop(resolved);
761                let mut resolved_mut = TEXT.resolve();
762                let resolved_mut = &mut *resolved_mut;
763                let caret = &mut resolved_mut.caret;
764                if let Some(index) = &mut caret.index {
765                    *index = ctx.shaped_text.snap_caret_line(*index);
766
767                    let p = ctx.shaped_text.caret_origin(*index, resolved_mut.segmented_text.text());
768                    if !caret.used_retained_x {
769                        ctx.caret_retained_x = p.x;
770                    }
771                    ctx.caret_origin = Some(p);
772
773                    if let Some(sel) = &mut caret.selection_index {
774                        *sel = ctx.shaped_text.snap_caret_line(*sel);
775                        ctx.caret_selection_origin = Some(ctx.shaped_text.caret_origin(*sel, resolved_mut.segmented_text.text()));
776                    }
777
778                    if !mem::take(&mut caret.skip_next_scroll) && SCROLL.try_id().is_some() {
779                        let line_height = ctx
780                            .shaped_text
781                            .line(index.line)
782                            .map(|l| l.rect().height())
783                            .unwrap_or_else(|| ctx.shaped_text.line_height());
784
785                        if let Some(p) = ctx.render_info.transform.transform_point(p) {
786                            let p = p - WIDGET.info().inner_bounds().origin;
787                            let min_rect = Rect::new(p.to_point(), Size::new(Px(1), line_height * 2 + ctx.shaped_text.line_spacing()));
788                            SCROLL.scroll_to(ScrollToMode::minimal_rect(min_rect));
789                        }
790                    }
791                }
792            }
793
794            // self.pending is cleared in the node layout, after this method call
795        }
796        self.txt_is_measured = is_measure;
797
798        metrics.constraints().fill_size_or(ctx.shaped_text.size())
799    }
800
801    fn ensure_layout_for_render(&mut self) {
802        if self.txt_is_measured {
803            let metrics = self.last_layout.0.clone();
804            self.shaping_args.inline_constraints = self.last_layout.1;
805            LAYOUT.with_context(metrics.clone(), || {
806                self.layout(&metrics, false);
807            });
808
809            debug_assert!(!self.txt_is_measured);
810        }
811    }
812}
813
814fn layout_text_edit(child: impl UiNode) -> impl UiNode {
815    // Use `LayoutTextEdit::get` to access.
816    let mut edit = None::<Box<LayoutTextEdit>>;
817
818    match_node(child, move |child, op| {
819        let mut enable = false;
820        match op {
821            UiNodeOp::Init => {
822                enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
823            }
824            UiNodeOp::Deinit => {
825                edit = None;
826            }
827            UiNodeOp::Info { info } => {
828                if let Some(e) = &edit {
829                    info.set_ime_area(e.ime_area.clone());
830                }
831            }
832            UiNodeOp::Event { update } => {
833                child.event(update);
834
835                if let Some(e) = &mut edit {
836                    layout_text_edit_events(update, e);
837                }
838            }
839            UiNodeOp::Update { .. } => {
840                if TEXT_EDITABLE_VAR.is_new() || TEXT_SELECTABLE_VAR.is_new() {
841                    enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
842                    if !enable && edit.is_some() {
843                        edit = None;
844                    }
845                } else if let Some(edit) = &edit {
846                    TEXT.resolved().txt.with_new(|t| {
847                        edit.select_all.set_enabled(!t.is_empty());
848                    });
849                }
850
851                if OBSCURE_TXT_VAR.is_new() || OBSCURING_CHAR_VAR.is_new() {
852                    if let Some(obscure) = OBSCURE_TXT_VAR.get_new() {
853                        if edit.is_none() && WINDOW.info().access_enabled().is_enabled() {
854                            WIDGET.info();
855                        }
856
857                        if obscure {
858                            UNDO.clear();
859                        }
860                    }
861                }
862            }
863            UiNodeOp::Render { frame } => {
864                child.render(frame);
865                if let Some(e) = &edit {
866                    e.update_ime(&mut TEXT.layout());
867                }
868            }
869            UiNodeOp::RenderUpdate { update } => {
870                child.render_update(update);
871                if let Some(e) = &edit {
872                    e.update_ime(&mut TEXT.layout());
873                }
874            }
875            _ => {}
876        }
877
878        if enable {
879            let edit = LayoutTextEdit::get(&mut edit);
880
881            let editable = TEXT_EDITABLE_VAR.get();
882            let selectable = TEXT_SELECTABLE_VAR.get();
883
884            if selectable || editable {
885                let id = WIDGET.id();
886
887                edit.events[0] = MOUSE_INPUT_EVENT.subscribe(id);
888                edit.events[1] = TOUCH_TAP_EVENT.subscribe(id);
889                edit.events[2] = TOUCH_LONG_PRESS_EVENT.subscribe(id);
890                edit.events[3] = TOUCH_INPUT_EVENT.subscribe(id);
891                // KEY_INPUT_EVENT subscribed by `resolve_text`.
892            }
893
894            if selectable {
895                let id = WIDGET.id();
896
897                edit.select = SELECT_CMD.scoped(id).subscribe(true);
898                let is_empty = TEXT.resolved().txt.with(|t| t.is_empty());
899                edit.select_all = SELECT_ALL_CMD.scoped(id).subscribe(!is_empty);
900            }
901        }
902    })
903}
904/// Data allocated only when `editable`.
905#[derive(Default)]
906struct LayoutTextEdit {
907    events: [EventHandle; 4],
908    caret_animation: VarHandle,
909    select: CommandHandle,
910    select_all: CommandHandle,
911    ime_area: Arc<Atomic<PxRect>>,
912    click_count: u8,
913    selection_mouse_down: Option<SelectionMouseDown>,
914    auto_select: bool,
915    selection_move_handles: EventHandles,
916}
917struct SelectionMouseDown {
918    position: DipPoint,
919    timestamp: DInstant,
920    count: u8,
921}
922impl LayoutTextEdit {
923    fn get(edit_data: &mut Option<Box<Self>>) -> &mut Self {
924        &mut *edit_data.get_or_insert_with(Default::default)
925    }
926
927    fn update_ime(&self, txt: &mut LaidoutText) {
928        let transform = txt.render_info.transform;
929        let area;
930
931        if let Some(a) = txt.caret_origin {
932            let (ac, bc) = {
933                let ctx = TEXT.resolved();
934                let c = &ctx.caret;
935                (c.index, c.selection_index)
936            };
937            let ac = ac.unwrap_or(CaretIndex::ZERO);
938            let mut a_line = PxRect::new(a, PxSize::new(Px(1), txt.shaped_text.line(ac.line).unwrap().height())).to_box2d();
939
940            if let Some(b) = txt.caret_selection_origin {
941                let bc = bc.unwrap_or(CaretIndex::ZERO);
942                let b_line = PxRect::new(b, PxSize::new(Px(1), txt.shaped_text.line(bc.line).unwrap().height())).to_box2d();
943
944                a_line.min = a_line.min.min(b_line.min);
945                a_line.max = a_line.max.min(b_line.max);
946            }
947            area = a_line;
948        } else {
949            area = PxBox::from_size(txt.shaped_text.size());
950        }
951
952        if let Some(area) = transform.outer_transformed(area) {
953            self.ime_area.store(area.to_rect(), atomic::Ordering::Relaxed);
954        }
955    }
956}
957fn layout_text_edit_events(update: &EventUpdate, edit: &mut LayoutTextEdit) {
958    let resolved = TEXT.resolved();
959    let editable = TEXT_EDITABLE_VAR.get() && resolved.txt.capabilities().can_modify();
960    let selectable = TEXT_SELECTABLE_VAR.get();
961
962    if !editable && !selectable {
963        return;
964    }
965    let widget = WIDGET.info();
966    if !widget.interactivity().is_enabled() {
967        return;
968    }
969
970    let prev_caret_index = {
971        let caret = &resolved.caret;
972        (caret.index, caret.index_version, caret.selection_index)
973    };
974    drop(resolved);
975
976    if let Some(args) = KEY_INPUT_EVENT.on_unhandled(update) {
977        if args.state == KeyState::Pressed && args.target.widget_id() == widget.id() {
978            match &args.key {
979                Key::Tab => {
980                    if editable && args.modifiers.is_empty() && ACCEPTS_TAB_VAR.get() {
981                        args.propagation().stop();
982                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
983                    }
984                }
985                Key::Enter => {
986                    if editable && args.modifiers.is_empty() && ACCEPTS_ENTER_VAR.get() {
987                        args.propagation().stop();
988                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
989                    }
990                }
991                Key::ArrowRight => {
992                    let mut modifiers = args.modifiers;
993                    let select = selectable && modifiers.take_shift();
994                    let word = modifiers.take_ctrl();
995                    if modifiers.is_empty() && (editable || select) {
996                        args.propagation().stop();
997
998                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
999
1000                        if select {
1001                            if word {
1002                                TextSelectOp::select_next_word()
1003                            } else {
1004                                TextSelectOp::select_next()
1005                            }
1006                        } else if word {
1007                            TextSelectOp::next_word()
1008                        } else {
1009                            TextSelectOp::next()
1010                        }
1011                        .call();
1012                    }
1013                }
1014                Key::ArrowLeft => {
1015                    let mut modifiers = args.modifiers;
1016                    let select = selectable && modifiers.take_shift();
1017                    let word = modifiers.take_ctrl();
1018                    if modifiers.is_empty() && (editable || select) {
1019                        args.propagation().stop();
1020
1021                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
1022
1023                        if select {
1024                            if word {
1025                                TextSelectOp::select_prev_word()
1026                            } else {
1027                                TextSelectOp::select_prev()
1028                            }
1029                        } else if word {
1030                            TextSelectOp::prev_word()
1031                        } else {
1032                            TextSelectOp::prev()
1033                        }
1034                        .call();
1035                    }
1036                }
1037                Key::ArrowUp => {
1038                    if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1039                        let mut modifiers = args.modifiers;
1040                        let select = selectable && modifiers.take_shift();
1041                        if modifiers.is_empty() && (editable || select) {
1042                            args.propagation().stop();
1043
1044                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1045
1046                            if select {
1047                                TextSelectOp::select_line_up()
1048                            } else {
1049                                TextSelectOp::line_up()
1050                            }
1051                            .call();
1052                        }
1053                    }
1054                }
1055                Key::ArrowDown => {
1056                    if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1057                        let mut modifiers = args.modifiers;
1058                        let select = selectable && modifiers.take_shift();
1059                        if modifiers.is_empty() && (editable || select) {
1060                            args.propagation().stop();
1061
1062                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1063
1064                            if select {
1065                                TextSelectOp::select_line_down()
1066                            } else {
1067                                TextSelectOp::line_down()
1068                            }
1069                            .call();
1070                        }
1071                    }
1072                }
1073                Key::PageUp => {
1074                    if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1075                        let mut modifiers = args.modifiers;
1076                        let select = selectable && modifiers.take_shift();
1077                        if modifiers.is_empty() && (editable || select) {
1078                            args.propagation().stop();
1079
1080                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1081
1082                            if select {
1083                                TextSelectOp::select_page_up()
1084                            } else {
1085                                TextSelectOp::page_up()
1086                            }
1087                            .call();
1088                        }
1089                    }
1090                }
1091                Key::PageDown => {
1092                    if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1093                        let mut modifiers = args.modifiers;
1094                        let select = selectable && modifiers.take_shift();
1095                        if modifiers.is_empty() && (editable || select) {
1096                            args.propagation().stop();
1097
1098                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1099
1100                            if select {
1101                                TextSelectOp::select_page_down()
1102                            } else {
1103                                TextSelectOp::page_down()
1104                            }
1105                            .call();
1106                        }
1107                    }
1108                }
1109                Key::Home => {
1110                    let mut modifiers = args.modifiers;
1111                    let select = selectable && modifiers.take_shift();
1112                    let full_text = modifiers.take_ctrl();
1113                    if modifiers.is_empty() && (editable || select) {
1114                        args.propagation().stop();
1115
1116                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
1117
1118                        if select {
1119                            if full_text {
1120                                TextSelectOp::select_text_start()
1121                            } else {
1122                                TextSelectOp::select_line_start()
1123                            }
1124                        } else if full_text {
1125                            TextSelectOp::text_start()
1126                        } else {
1127                            TextSelectOp::line_start()
1128                        }
1129                        .call();
1130                    }
1131                }
1132                Key::End => {
1133                    let mut modifiers = args.modifiers;
1134                    let select = selectable && modifiers.take_shift();
1135                    let full_text = modifiers.take_ctrl();
1136                    if modifiers.is_empty() && (editable || select) {
1137                        args.propagation().stop();
1138
1139                        TEXT.resolve().selection_by = SelectionBy::Keyboard;
1140
1141                        if select {
1142                            if full_text {
1143                                TextSelectOp::select_text_end()
1144                            } else {
1145                                TextSelectOp::select_line_end()
1146                            }
1147                        } else if full_text {
1148                            TextSelectOp::text_end()
1149                        } else {
1150                            TextSelectOp::line_end()
1151                        }
1152                        .call();
1153                    }
1154                }
1155                _ => {}
1156            }
1157        }
1158    } else if let Some(args) = MOUSE_INPUT_EVENT.on_unhandled(update) {
1159        if args.is_primary() && args.is_mouse_down() && args.target.widget_id() == widget.id() {
1160            let mut modifiers = args.modifiers;
1161            let select = selectable && modifiers.take_shift();
1162
1163            if modifiers.is_empty() {
1164                args.propagation().stop();
1165
1166                TEXT.resolve().selection_by = SelectionBy::Mouse;
1167
1168                edit.click_count = if let Some(info) = &mut edit.selection_mouse_down {
1169                    let cfg = MOUSE.multi_click_config().get();
1170
1171                    let double_allowed = args.timestamp.duration_since(info.timestamp) <= cfg.time && {
1172                        let dist = (info.position.to_vector() - args.position.to_vector()).abs();
1173                        let area = cfg.area;
1174                        dist.x <= area.width && dist.y <= area.height
1175                    };
1176
1177                    if double_allowed {
1178                        info.timestamp = args.timestamp;
1179                        info.count += 1;
1180                        info.count = info.count.min(4);
1181                    } else {
1182                        *info = SelectionMouseDown {
1183                            position: args.position,
1184                            timestamp: args.timestamp,
1185                            count: 1,
1186                        };
1187                    }
1188
1189                    info.count
1190                } else {
1191                    edit.selection_mouse_down = Some(SelectionMouseDown {
1192                        position: args.position,
1193                        timestamp: args.timestamp,
1194                        count: 1,
1195                    });
1196                    1
1197                };
1198
1199                match edit.click_count {
1200                    1 => {
1201                        if select {
1202                            TextSelectOp::select_nearest_to(args.position).call()
1203                        } else {
1204                            TextSelectOp::nearest_to(args.position).call();
1205
1206                            // select all on mouse-up if only acquire focus
1207                            edit.auto_select = selectable
1208                                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1209                                && !FOCUS.is_focused(widget.id()).get()
1210                                && TEXT.resolved().caret.selection_range().is_none();
1211                        }
1212                    }
1213                    2 => {
1214                        if selectable {
1215                            TextSelectOp::select_word_nearest_to(!select, args.position).call()
1216                        }
1217                    }
1218                    3 => {
1219                        if selectable {
1220                            TextSelectOp::select_line_nearest_to(!select, args.position).call()
1221                        }
1222                    }
1223                    4 => {
1224                        if selectable {
1225                            TextSelectOp::select_all().call()
1226                        }
1227                    }
1228                    _ => unreachable!(),
1229                };
1230                if selectable {
1231                    let id = widget.id();
1232                    edit.selection_move_handles.push(MOUSE_MOVE_EVENT.subscribe(id));
1233                    edit.selection_move_handles.push(POINTER_CAPTURE_EVENT.subscribe(id));
1234                    POINTER_CAPTURE.capture_widget(id);
1235                }
1236            }
1237        } else {
1238            if mem::take(&mut edit.auto_select)
1239                && selectable
1240                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1241                && args.is_primary()
1242                && args.is_mouse_up()
1243                && FOCUS.is_focused(widget.id()).get()
1244                && TEXT.resolved().caret.selection_range().is_none()
1245            {
1246                TextSelectOp::select_all().call()
1247            }
1248            edit.selection_move_handles.clear();
1249        }
1250    } else if let Some(args) = TOUCH_INPUT_EVENT.on_unhandled(update) {
1251        if args.modifiers.is_empty() && args.target.widget_id() == widget.id() {
1252            edit.auto_select = selectable
1253                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1254                && args.modifiers.is_empty()
1255                && args.is_touch_start()
1256                && !FOCUS.is_focused(widget.id()).get()
1257                && TEXT.resolved().caret.selection_range().is_none();
1258        }
1259    } else if let Some(args) = TOUCH_TAP_EVENT.on_unhandled(update) {
1260        if args.modifiers.is_empty() && args.target.widget_id() == widget.id() {
1261            args.propagation().stop();
1262
1263            TEXT.resolve().selection_by = SelectionBy::Touch;
1264
1265            TextSelectOp::nearest_to(args.position).call();
1266
1267            if mem::take(&mut edit.auto_select)
1268                && selectable
1269                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1270                && FOCUS.is_focused(WIDGET.id()).get()
1271                && TEXT.resolved().caret.selection_range().is_none()
1272            {
1273                TextSelectOp::select_all().call()
1274            }
1275        }
1276    } else if let Some(args) = TOUCH_LONG_PRESS_EVENT.on_unhandled(update) {
1277        if args.modifiers.is_empty() && selectable && args.target.widget_id() == widget.id() {
1278            args.propagation().stop();
1279
1280            TEXT.resolve().selection_by = SelectionBy::Touch;
1281
1282            TextSelectOp::select_word_nearest_to(true, args.position).call();
1283        }
1284    } else if let Some(args) = MOUSE_MOVE_EVENT.on(update) {
1285        if !edit.selection_move_handles.is_dummy() && selectable && args.target.widget_id() == widget.id() {
1286            args.propagation().stop();
1287
1288            match edit.click_count {
1289                1 => TextSelectOp::select_nearest_to(args.position).call(),
1290                2 => TextSelectOp::select_word_nearest_to(false, args.position).call(),
1291                3 => TextSelectOp::select_line_nearest_to(false, args.position).call(),
1292                4 => {}
1293                _ => unreachable!(),
1294            }
1295        }
1296    } else if let Some(args) = POINTER_CAPTURE_EVENT.on(update) {
1297        if args.is_lost(widget.id()) {
1298            edit.selection_move_handles.clear();
1299            edit.auto_select = false;
1300        }
1301    } else if selectable {
1302        if let Some(args) = SELECT_CMD.scoped(widget.id()).on_unhandled(update) {
1303            if let Some(op) = args.param::<TextSelectOp>() {
1304                args.propagation().stop();
1305
1306                op.clone().call();
1307            }
1308        } else if let Some(args) = SELECT_ALL_CMD.scoped(widget.id()).on_unhandled(update) {
1309            args.propagation().stop();
1310            TextSelectOp::select_all().call();
1311        }
1312    }
1313
1314    let mut resolve = TEXT.resolve();
1315    let caret = &mut resolve.caret;
1316    if (caret.index, caret.index_version, caret.selection_index) != prev_caret_index {
1317        if !editable || caret.index.is_none() || !FOCUS.is_focused(widget.id()).get() {
1318            edit.caret_animation = VarHandle::dummy();
1319            caret.opacity = var(0.fct()).read_only();
1320        } else {
1321            caret.opacity = KEYBOARD.caret_animation();
1322            edit.caret_animation = caret.opacity.subscribe(UpdateOp::RenderUpdate, widget.id());
1323        }
1324        resolve.pending_layout |= PendingLayout::CARET;
1325        WIDGET.layout(); // update caret_origin
1326    }
1327}