Skip to main content

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