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::{
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, Rect, Size},
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::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);
279
280                if let Some(inline) = wm.inline() {
281                    let ctx = TEXT.laidout();
282
283                    if let Some(first_line) = ctx.shaped_text.line(0) {
284                        inline.first = first_line.original_size();
285                        inline.with_first_segs(|i| {
286                            for seg in first_line.segs() {
287                                i.push(InlineSegment::new(seg.advance(), seg.kind()));
288                            }
289                        });
290                    } else {
291                        inline.first = PxSize::zero();
292                        inline.with_first_segs(|i| i.clear());
293                    }
294
295                    if ctx.shaped_text.lines_len() == 1 {
296                        inline.last = inline.first;
297                        inline.last_segs = inline.first_segs.clone();
298                    } else if let Some(last_line) = ctx.shaped_text.line(ctx.shaped_text.lines_len().saturating_sub(1)) {
299                        inline.last = last_line.original_size();
300                        inline.with_last_segs(|i| {
301                            for seg in last_line.segs() {
302                                i.push(InlineSegment::new(seg.advance(), seg.kind()));
303                            }
304                        })
305                    } else {
306                        inline.last = PxSize::zero();
307                        inline.with_last_segs(|i| i.clear());
308                    }
309
310                    inline.first_wrapped = ctx.shaped_text.first_wrapped();
311                    inline.last_wrapped = ctx.shaped_text.lines_len() > 1;
312                }
313                size
314            };
315
316            LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*desired_size), || {
317                // foreign nodes in the CHILD_LAYOUT+100 ..= CHILD range may change the size
318                *desired_size = child.measure(wm);
319            });
320        }
321        UiNodeOp::Layout { wl, final_size } => {
322            child.delegated();
323
324            let metrics = LAYOUT.metrics();
325
326            TEXT.layout().viewport = metrics.viewport();
327
328            *final_size = txt.layout(&metrics, false);
329
330            if txt.pending != PendingLayout::empty() {
331                WIDGET.render();
332                txt.pending = PendingLayout::empty();
333            }
334
335            if let Some(inline) = wl.inline() {
336                let ctx = TEXT.laidout();
337
338                let last_line = ctx.shaped_text.lines_len().saturating_sub(1);
339
340                inline.first_segs.clear();
341                inline.last_segs.clear();
342
343                for (i, line) in ctx.shaped_text.lines().enumerate() {
344                    if i == 0 {
345                        let info = ctx.shaped_text.line(0).unwrap().segs().map(|s| s.inline_info());
346                        if LAYOUT.direction().is_rtl() {
347                            // help sort
348                            inline.set_first_segs(info.rev());
349                        } else {
350                            inline.set_first_segs(info);
351                        }
352                    } else if i == last_line {
353                        let info = ctx
354                            .shaped_text
355                            .line(ctx.shaped_text.lines_len().saturating_sub(1))
356                            .unwrap()
357                            .segs()
358                            .map(|s| s.inline_info());
359                        if LAYOUT.direction().is_rtl() {
360                            // help sort
361                            inline.set_last_segs(info.rev());
362                        } else {
363                            inline.set_last_segs(info);
364                        }
365                    }
366
367                    inline.rows.push(line.rect());
368                }
369            }
370
371            wl.set_baseline(txt.baseline);
372
373            LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*final_size), || {
374                // foreign nodes in the CHILD_LAYOUT+100 ..= CHILD range may change the size
375                *final_size = child.layout(wl);
376            });
377        }
378        UiNodeOp::Render { .. } => {
379            txt.ensure_layout_for_render();
380        }
381        UiNodeOp::RenderUpdate { .. } => {
382            txt.ensure_layout_for_render();
383        }
384        _ => {}
385    })
386}
387struct LayoutTextFinal {
388    shaping_args: TextShapingArgs,
389    pending: PendingLayout,
390
391    txt_is_measured: bool,
392    last_layout: (LayoutMetrics, Option<InlineConstraintsMeasure>),
393    baseline: Px,
394}
395impl LayoutTextFinal {
396    /// Fast measure.
397    ///
398    /// If the metrics fully define the size the text does not need to be shaped.
399    fn measure(&mut self, metrics: &LayoutMetrics) -> Option<PxSize> {
400        if metrics.inline_constraints().is_some() {
401            return None;
402        }
403
404        metrics.constraints().fill_or_exact()
405    }
406
407    /// Slow measure/layout.
408    ///
409    /// If `is_measure` the text will be reshaped just like layout, it only skips render only state update.
410    /// Text shaping is state heavy and there is significant perf gains for caching and "reshaping", reusing
411    /// the same layout state for measure is the least bad option as it does not cause allocation and full shaping.
412    ///
413    /// Because measure affects the state the `ensure_layout_for_render` method must be called because the measure API
414    /// is not supposed to change state a parent panel can measure and discard the results and go directly to render, expecting
415    /// the widget to still have the last layout state.
416    fn layout(&mut self, metrics: &LayoutMetrics, is_measure: bool) -> PxSize {
417        let mut resolved = TEXT.resolved();
418
419        self.pending |= resolved.pending_layout;
420
421        let font_size = metrics.font_size();
422
423        let mut ctx = TEXT.layout();
424
425        if font_size != ctx.fonts.requested_size() || !ctx.fonts.is_sized_from(&resolved.faces) {
426            ctx.fonts = resolved.faces.sized(font_size, FONT_VARIATIONS_VAR.with(FontVariations::finalize));
427            self.pending.insert(PendingLayout::RESHAPE);
428        }
429
430        if TEXT_WRAP_VAR.get() && !metrics.constraints().x.is_unbounded() {
431            let max_width = metrics.constraints().x.max().unwrap();
432            if self.shaping_args.max_width != max_width {
433                self.shaping_args.max_width = max_width;
434
435                if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(max_width) {
436                    self.pending.insert(PendingLayout::RESHAPE);
437                }
438            }
439        } else if self.shaping_args.max_width != Px::MAX {
440            self.shaping_args.max_width = Px::MAX;
441            if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(Px::MAX) {
442                self.pending.insert(PendingLayout::RESHAPE);
443            }
444        }
445
446        if ctx.caret_origin.is_none() {
447            self.pending.insert(PendingLayout::CARET);
448        }
449
450        if let Some(inline) = metrics.inline_constraints() {
451            match inline {
452                InlineConstraints::Measure(m) => {
453                    if self.shaping_args.inline_constraints != Some(m) {
454                        self.shaping_args.inline_constraints = Some(m);
455                        self.pending.insert(PendingLayout::RESHAPE);
456                    }
457                }
458                InlineConstraints::Layout(l) => {
459                    if !self.pending.contains(PendingLayout::RESHAPE)
460                        && (Some(l.first_segs.len()) != ctx.shaped_text.line(0).map(|l| l.segs_len())
461                            || Some(l.last_segs.len())
462                                != ctx
463                                    .shaped_text
464                                    .line(ctx.shaped_text.lines_len().saturating_sub(1))
465                                    .map(|l| l.segs_len()))
466                    {
467                        self.pending.insert(PendingLayout::RESHAPE);
468                    }
469
470                    if !self.pending.contains(PendingLayout::RESHAPE_LINES)
471                        && (ctx.shaped_text.mid_clear() != l.mid_clear
472                            || ctx.shaped_text.line(0).map(|l| l.rect()) != Some(l.first)
473                            || ctx
474                                .shaped_text
475                                .line(ctx.shaped_text.lines_len().saturating_sub(1))
476                                .map(|l| l.rect())
477                                != Some(l.last))
478                    {
479                        self.pending.insert(PendingLayout::RESHAPE_LINES);
480                    }
481                }
482            }
483        } else if self.shaping_args.inline_constraints.is_some() {
484            self.shaping_args.inline_constraints = None;
485            self.pending.insert(PendingLayout::RESHAPE);
486        }
487
488        if !self.pending.contains(PendingLayout::RESHAPE_LINES) {
489            let size = ctx.shaped_text.size();
490            if metrics.constraints().fill_size_or(size) != ctx.shaped_text.align_size() {
491                self.pending.insert(PendingLayout::RESHAPE_LINES);
492            }
493        }
494
495        let font = ctx.fonts.best();
496
497        let space_len = font.space_x_advance();
498        let dft_tab_len = space_len * 3;
499
500        let (letter_spacing, word_spacing, tab_length, paragraph_indent) = {
501            LAYOUT.with_constraints(PxConstraints2d::new_exact(space_len, space_len), || {
502                (
503                    LETTER_SPACING_VAR.layout_x(),
504                    WORD_SPACING_VAR.layout_x(),
505                    TAB_LENGTH_VAR.layout_dft_x(dft_tab_len),
506                    PARAGRAPH_INDENT_VAR.with(|i| i.spacing.layout_x()),
507                )
508            })
509        };
510        let paragraph_indent = (paragraph_indent, PARAGRAPH_INDENT_VAR.with(|i| i.invert));
511
512        let dft_line_height = font.metrics().line_height();
513        let line_height = LAYOUT.with_constraints(PxConstraints2d::new_exact(dft_line_height, dft_line_height), || {
514            LINE_HEIGHT_VAR.layout_dft_y(dft_line_height)
515        });
516        let line_spacing = LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || LINE_SPACING_VAR.layout_y());
517
518        let paragraph_spacing = LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || {
519            PARAGRAPH_SPACING_VAR.layout_y()
520        });
521
522        if !self.pending.contains(PendingLayout::RESHAPE)
523            && (letter_spacing != self.shaping_args.letter_spacing
524                || word_spacing != self.shaping_args.word_spacing
525                || tab_length != self.shaping_args.tab_x_advance
526                || paragraph_indent != self.shaping_args.paragraph_indent)
527        {
528            self.pending.insert(PendingLayout::RESHAPE);
529        }
530        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
531            && (line_spacing != self.shaping_args.line_spacing
532                || line_height != self.shaping_args.line_height
533                || paragraph_spacing != self.shaping_args.paragraph_spacing)
534        {
535            self.pending.insert(PendingLayout::RESHAPE_LINES);
536        }
537
538        self.shaping_args.letter_spacing = letter_spacing;
539        self.shaping_args.word_spacing = word_spacing;
540        self.shaping_args.tab_x_advance = tab_length;
541        self.shaping_args.line_height = line_height;
542        self.shaping_args.line_spacing = line_spacing;
543        self.shaping_args.paragraph_spacing = paragraph_spacing;
544        self.shaping_args.paragraph_indent = paragraph_indent;
545
546        let dft_thickness = font.metrics().underline_thickness;
547        let (overline, strikethrough, underline, ime_underline) = {
548            LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || {
549                (
550                    OVERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
551                    STRIKETHROUGH_THICKNESS_VAR.layout_dft_y(dft_thickness),
552                    UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
553                    IME_UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
554                )
555            })
556        };
557
558        if !self.pending.contains(PendingLayout::OVERLINE) && (ctx.overline_thickness == 0) != (overline == 0) {
559            self.pending.insert(PendingLayout::OVERLINE);
560        }
561        if !self.pending.contains(PendingLayout::STRIKETHROUGH) && (ctx.strikethrough_thickness == 0) != (strikethrough == 0) {
562            self.pending.insert(PendingLayout::STRIKETHROUGH);
563        }
564        if !self.pending.contains(PendingLayout::UNDERLINE)
565            && ((ctx.underline_thickness == 0) != (underline == 0) || (ctx.ime_underline_thickness != 0) != (ime_underline != 0))
566        {
567            self.pending.insert(PendingLayout::UNDERLINE);
568        }
569        ctx.overline_thickness = overline;
570        ctx.strikethrough_thickness = strikethrough;
571        ctx.underline_thickness = underline;
572        ctx.ime_underline_thickness = ime_underline;
573
574        let align = TEXT_ALIGN_VAR.get();
575        let justify = JUSTIFY_MODE_VAR.get();
576        let overflow_align = TEXT_OVERFLOW_ALIGN_VAR.get();
577        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
578            && (align != ctx.shaped_text.align()
579                || justify != ctx.shaped_text.justify_mode().unwrap_or_default()
580                || overflow_align != ctx.shaped_text.overflow_align())
581        {
582            self.pending.insert(PendingLayout::RESHAPE_LINES);
583        }
584
585        /*
586            APPLY
587        */
588
589        if self.pending.contains(PendingLayout::RESHAPE) {
590            ctx.shaped_text = ctx.fonts.shape_text(&resolved.segmented_text, &self.shaping_args);
591            self.pending = self.pending.intersection(PendingLayout::RESHAPE_LINES);
592        }
593
594        if !self.pending.contains(PendingLayout::RESHAPE_LINES)
595            && ctx.shaped_text.align_size() != metrics.constraints().fill_size_or(ctx.shaped_text.block_size())
596        {
597            self.pending.insert(PendingLayout::RESHAPE_LINES);
598        }
599
600        if self.pending.contains(PendingLayout::RESHAPE_LINES) && metrics.inline_constraints().is_none() {
601            // Affects block size in measure too
602            //
603            // This breaks inline context, so it is avoided here and called later in the `if !is_measure` block.
604            // This is needed to measure the same block size a layout call would output.
605            //
606            // Not sure if it is a bug that it does not work inlining, but it is not needed there anyway, so for now
607            // this fix is sufficient.
608            let mut args = TextReshapingArgs::default();
609            args.constraints = metrics.constraints();
610            args.inline_constraints = metrics.inline_constraints().map(|c| c.layout());
611            args.align = align;
612            args.overflow_align = overflow_align;
613            args.direction = metrics.direction();
614            args.line_height = line_height;
615            args.line_spacing = line_spacing;
616            args.paragraph_spacing = paragraph_spacing;
617            args.paragraph_indent = paragraph_indent;
618            args.paragraph_break = PARAGRAPH_BREAK_VAR.get();
619            ctx.shaped_text.reshape_lines2(&args);
620        }
621
622        if !is_measure {
623            self.last_layout = (metrics.clone(), self.shaping_args.inline_constraints);
624
625            if self.pending.contains(PendingLayout::RESHAPE_LINES) {
626                if metrics.inline_constraints().is_some() {
627                    // when inlining, only reshape lines in layout passes
628                    let mut args = TextReshapingArgs::default();
629                    args.constraints = metrics.constraints();
630                    args.inline_constraints = metrics.inline_constraints().map(|c| c.layout());
631                    args.align = align;
632                    args.overflow_align = overflow_align;
633                    args.direction = metrics.direction();
634                    args.line_height = line_height;
635                    args.line_spacing = line_spacing;
636                    args.paragraph_spacing = paragraph_spacing;
637                    args.paragraph_indent = paragraph_indent;
638                    args.paragraph_break = PARAGRAPH_BREAK_VAR.get();
639                    ctx.shaped_text.reshape_lines2(&args);
640                }
641                ctx.shaped_text.reshape_lines_justify(justify, &self.shaping_args.lang);
642
643                ctx.shaped_text_version = ctx.shaped_text_version.wrapping_add(1);
644                drop(resolved);
645                self.baseline = ctx.shaped_text.baseline();
646                resolved = TEXT.resolved();
647                ctx.caret_origin = None;
648                ctx.caret_selection_origin = None;
649            }
650            if self.pending.contains(PendingLayout::OVERFLOW) {
651                let txt_size = ctx.shaped_text.size();
652                let max_size = metrics.constraints().fill_size_or(txt_size);
653                if txt_size.width > max_size.width || txt_size.height > max_size.height {
654                    let suf_width = ctx.overflow_suffix.as_ref().map(|s| s.size().width).unwrap_or(Px(0));
655                    ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf_width);
656
657                    if ctx.overflow.is_some() && ctx.overflow_suffix.is_none() && !TEXT_EDITABLE_VAR.get() {
658                        match TEXT_OVERFLOW_VAR.get() {
659                            TextOverflow::Truncate(suf) if !suf.is_empty() => {
660                                let suf = SegmentedText::new(suf, self.shaping_args.direction);
661                                let suf = ctx.fonts.shape_text(&suf, &self.shaping_args);
662
663                                ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf.size().width);
664                                ctx.overflow_suffix = Some(suf);
665                            }
666                            _ => {}
667                        }
668                    }
669                } else {
670                    ctx.overflow = None;
671                }
672            }
673            if self.pending.contains(PendingLayout::OVERLINE) {
674                if ctx.overline_thickness > Px(0) {
675                    ctx.overlines = ctx.shaped_text.lines().map(|l| l.overline()).collect();
676                } else {
677                    ctx.overlines = vec![];
678                }
679            }
680            if self.pending.contains(PendingLayout::STRIKETHROUGH) {
681                if ctx.strikethrough_thickness > Px(0) {
682                    ctx.strikethroughs = ctx.shaped_text.lines().map(|l| l.strikethrough()).collect();
683                } else {
684                    ctx.strikethroughs = vec![];
685                }
686            }
687
688            if self.pending.contains(PendingLayout::UNDERLINE) {
689                let ime_range = if let Some(ime) = &resolved.ime_preview {
690                    let start = ime.prev_selection.unwrap_or(ime.prev_caret).index.min(ime.prev_caret.index);
691                    start..start + ime.txt.len()
692                } else {
693                    0..0
694                };
695                let caret_ime_range = if !ime_range.is_empty() && (ctx.underline_thickness > Px(0) || ctx.ime_underline_thickness > Px(0)) {
696                    let start = ctx.shaped_text.snap_caret_line(CaretIndex {
697                        index: ime_range.start,
698                        line: 0,
699                    });
700                    let end = ctx.shaped_text.snap_caret_line(CaretIndex {
701                        index: ime_range.end,
702                        line: 0,
703                    });
704
705                    start..end
706                } else {
707                    CaretIndex::ZERO..CaretIndex::ZERO
708                };
709
710                if ctx.underline_thickness > Px(0) {
711                    let mut underlines = vec![];
712
713                    let skip = UNDERLINE_SKIP_VAR.get();
714                    match UNDERLINE_POSITION_VAR.get() {
715                        UnderlinePosition::Font => {
716                            if skip == UnderlineSkip::GLYPHS | UnderlineSkip::SPACES {
717                                for line in ctx.shaped_text.lines() {
718                                    for und in line.underline_skip_glyphs_and_spaces(ctx.underline_thickness) {
719                                        underlines.push(und);
720                                    }
721                                }
722                            } else if skip.contains(UnderlineSkip::GLYPHS) {
723                                for line in ctx.shaped_text.lines() {
724                                    for und in line.underline_skip_glyphs(ctx.underline_thickness) {
725                                        underlines.push(und);
726                                    }
727                                }
728                            } else if skip.contains(UnderlineSkip::SPACES) {
729                                for line in ctx.shaped_text.lines() {
730                                    for und in line.underline_skip_spaces() {
731                                        underlines.push(und);
732                                    }
733                                }
734                            } else {
735                                for line in ctx.shaped_text.lines() {
736                                    let und = line.underline();
737                                    underlines.push(und);
738                                }
739                            }
740                        }
741                        UnderlinePosition::Descent => {
742                            // descent clears all glyphs, so we only need to care about spaces
743                            if skip.contains(UnderlineSkip::SPACES) {
744                                for line in ctx.shaped_text.lines() {
745                                    for und in line.underline_descent_skip_spaces() {
746                                        underlines.push(und);
747                                    }
748                                }
749                            } else {
750                                for line in ctx.shaped_text.lines() {
751                                    let und = line.underline_descent();
752                                    underlines.push(und);
753                                }
754                            }
755                        }
756                    }
757
758                    if !ime_range.is_empty() {
759                        underlines = ctx.shaped_text.clip_lines(
760                            caret_ime_range.clone(),
761                            true,
762                            resolved.segmented_text.text(),
763                            underlines.into_iter(),
764                        );
765                    }
766
767                    ctx.underlines = underlines;
768                } else {
769                    ctx.underlines = vec![];
770                }
771
772                if ctx.ime_underline_thickness > Px(0) && !ime_range.is_empty() {
773                    let mut ime_underlines = vec![];
774
775                    // collects underlines for all segments that intersect with the IME text.
776                    for line in ctx.shaped_text.lines() {
777                        let line_range = line.text_range();
778                        if line_range.start < ime_range.end && line_range.end > ime_range.start {
779                            for seg in line.segs() {
780                                let seg_range = seg.text_range();
781                                if seg_range.start < ime_range.end && seg_range.end > ime_range.start {
782                                    for und in seg.underline_skip_glyphs(ctx.ime_underline_thickness) {
783                                        ime_underlines.push(und);
784                                    }
785                                }
786                            }
787                        }
788                    }
789
790                    ctx.ime_underlines =
791                        ctx.shaped_text
792                            .clip_lines(caret_ime_range, false, resolved.segmented_text.text(), ime_underlines.into_iter());
793                } else {
794                    ctx.ime_underlines = vec![];
795                }
796            }
797
798            if self.pending.contains(PendingLayout::CARET) {
799                drop(resolved);
800                let mut resolved_mut = TEXT.resolve();
801                let resolved_mut = &mut *resolved_mut;
802                let caret = &mut resolved_mut.caret;
803                if let Some(index) = &mut caret.index {
804                    *index = ctx.shaped_text.snap_caret_line(*index);
805
806                    let p = ctx.shaped_text.caret_origin(*index, resolved_mut.segmented_text.text());
807                    if !caret.used_retained_x {
808                        ctx.caret_retained_x = p.x;
809                    }
810                    ctx.caret_origin = Some(p);
811
812                    if let Some(sel) = &mut caret.selection_index {
813                        *sel = ctx.shaped_text.snap_caret_line(*sel);
814                        ctx.caret_selection_origin = Some(ctx.shaped_text.caret_origin(*sel, resolved_mut.segmented_text.text()));
815                    }
816
817                    if !mem::take(&mut caret.skip_next_scroll)
818                        && SCROLL.try_id().is_some()
819                        && let Some(focused) = FOCUS.focused().get()
820                        && focused.contains(TEXT.try_rich().map(|r| r.root_id).unwrap_or_else(|| WIDGET.id()))
821                    {
822                        let line_height = ctx
823                            .shaped_text
824                            .line(index.line)
825                            .map(|l| l.rect().height())
826                            .unwrap_or_else(|| ctx.shaped_text.line_height());
827
828                        if let Some(p) = ctx.render_info.transform.transform_point(p) {
829                            let p = p - WIDGET.info().inner_bounds().origin;
830                            let min_rect = Rect::new(p.to_point(), Size::new(Px(1), line_height * 2 + ctx.shaped_text.line_spacing()));
831                            SCROLL.scroll_to(ScrollToMode::minimal_rect(min_rect));
832                        }
833                    }
834                }
835            }
836
837            // self.pending is cleared in the node layout, after this method call
838        }
839        self.txt_is_measured = is_measure;
840
841        metrics.constraints().inner().fill_size_or(ctx.shaped_text.size())
842    }
843
844    /// See `layout` docs.
845    fn ensure_layout_for_render(&mut self) {
846        if self.txt_is_measured {
847            let metrics = self.last_layout.0.clone();
848            self.shaping_args.inline_constraints = self.last_layout.1;
849            LAYOUT.with_context(metrics.clone(), || {
850                self.layout(&metrics, false);
851            });
852
853            debug_assert!(!self.txt_is_measured);
854        }
855    }
856}
857
858fn layout_text_edit(child: impl IntoUiNode) -> UiNode {
859    // Use `LayoutTextEdit::get` to access.
860    let mut edit = None::<Box<LayoutTextEdit>>;
861
862    match_node(child, move |child, op| {
863        let mut enable = false;
864        match op {
865            UiNodeOp::Init => {
866                child.init(); // let other nodes subscribe to events first (needed for `only_alt_...`)
867                enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
868            }
869            UiNodeOp::Deinit => {
870                edit = None;
871            }
872            UiNodeOp::Info { info } => {
873                if let Some(e) = &edit {
874                    info.set_ime_area(e.ime_area.clone());
875                }
876            }
877            UiNodeOp::Event { update } => {
878                child.event(update);
879
880                if let Some(e) = &mut edit {
881                    layout_text_edit_events(update, e);
882                }
883            }
884            UiNodeOp::Update { .. } => {
885                if TEXT_EDITABLE_VAR.is_new() || TEXT_SELECTABLE_VAR.is_new() {
886                    enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
887                    if !enable && edit.is_some() {
888                        edit = None;
889                    }
890                } else if let Some(edit) = &edit {
891                    TEXT.resolved().txt.with_new(|t| {
892                        edit.select_all.set_enabled(!t.is_empty());
893                    });
894                }
895
896                if (OBSCURE_TXT_VAR.is_new() || OBSCURING_CHAR_VAR.is_new())
897                    && let Some(obscure) = OBSCURE_TXT_VAR.get_new()
898                {
899                    if edit.is_none() && WINDOW.info().access_enabled().is_enabled() {
900                        WIDGET.info();
901                    }
902
903                    if obscure {
904                        UNDO.clear();
905                    }
906                }
907            }
908            UiNodeOp::Render { frame } => {
909                child.render(frame);
910                if let Some(e) = &edit {
911                    e.update_ime(&mut TEXT.layout());
912                }
913            }
914            UiNodeOp::RenderUpdate { update } => {
915                child.render_update(update);
916                if let Some(e) = &edit {
917                    e.update_ime(&mut TEXT.layout());
918                }
919            }
920            _ => {}
921        }
922
923        if enable {
924            let edit = LayoutTextEdit::get(&mut edit);
925
926            let editable = TEXT_EDITABLE_VAR.get();
927            let selectable = TEXT_SELECTABLE_VAR.get();
928
929            if selectable || editable {
930                let id = WIDGET.id();
931
932                edit.events[0] = MOUSE_INPUT_EVENT.subscribe(id);
933                edit.events[1] = TOUCH_TAP_EVENT.subscribe(id);
934                edit.events[2] = TOUCH_LONG_PRESS_EVENT.subscribe(id);
935                edit.events[3] = TOUCH_INPUT_EVENT.subscribe(id);
936                // KEY_INPUT_EVENT subscribed by `resolve_text`.
937            } else {
938                edit.events = Default::default();
939            }
940
941            if selectable {
942                let id = WIDGET.id();
943
944                edit.select = SELECT_CMD.scoped(id).subscribe(true);
945                let is_empty = TEXT.resolved().txt.with(|t| t.is_empty());
946                edit.select_all = SELECT_ALL_CMD.scoped(id).subscribe(!is_empty);
947            } else {
948                edit.select = Default::default();
949                edit.select_all = Default::default();
950            }
951        }
952    })
953}
954/// Data allocated only when `editable`.
955#[derive(Default)]
956struct LayoutTextEdit {
957    events: [EventHandle; 4],
958    caret_animation: VarHandle,
959    select: CommandHandle,
960    select_all: CommandHandle,
961    ime_area: Arc<Atomic<PxRect>>,
962    click_count: u8,
963    selection_mouse_down: Option<SelectionMouseDown>,
964    auto_select: bool,
965    selection_move_handles: EventHandles,
966    selection_started_by_alt: bool,
967}
968struct SelectionMouseDown {
969    position: DipPoint,
970    timestamp: DInstant,
971    count: u8,
972}
973impl LayoutTextEdit {
974    fn get(edit_data: &mut Option<Box<Self>>) -> &mut Self {
975        &mut *edit_data.get_or_insert_with(Default::default)
976    }
977
978    fn update_ime(&self, txt: &mut LaidoutText) {
979        let transform = txt.render_info.transform;
980        let area;
981
982        if let Some(a) = txt.caret_origin {
983            let (ac, bc) = {
984                let ctx = TEXT.resolved();
985                let c = &ctx.caret;
986                (c.index, c.selection_index)
987            };
988            let ac = ac.unwrap_or(CaretIndex::ZERO);
989            let mut a_line = PxRect::new(a, PxSize::new(Px(1), txt.shaped_text.line(ac.line).unwrap().height())).to_box2d();
990
991            if let Some(b) = txt.caret_selection_origin {
992                let bc = bc.unwrap_or(CaretIndex::ZERO);
993                let b_line = PxRect::new(b, PxSize::new(Px(1), txt.shaped_text.line(bc.line).unwrap().height())).to_box2d();
994
995                a_line.min = a_line.min.min(b_line.min);
996                a_line.max = a_line.max.min(b_line.max);
997            }
998            area = a_line;
999        } else {
1000            area = PxBox::from_size(txt.shaped_text.size());
1001        }
1002
1003        if let Some(area) = transform.outer_transformed(area) {
1004            self.ime_area.store(area.to_rect(), atomic::Ordering::Relaxed);
1005        }
1006    }
1007}
1008
1009fn layout_text_edit_events(update: &EventUpdate, edit: &mut LayoutTextEdit) {
1010    let resolved = TEXT.resolved();
1011    let editable = TEXT_EDITABLE_VAR.get() && resolved.txt.capabilities().can_modify();
1012    let selectable = TEXT_SELECTABLE_VAR.get();
1013
1014    if !editable && !selectable {
1015        return;
1016    }
1017    let widget = WIDGET.info();
1018    if !widget.interactivity().is_enabled() {
1019        return;
1020    }
1021
1022    let selectable_alt_only = selectable && !editable && TEXT_SELECTABLE_ALT_ONLY_VAR.get();
1023
1024    let prev_caret_index = {
1025        let caret = &resolved.caret;
1026        (caret.index, caret.index_version, caret.selection_index)
1027    };
1028    drop(resolved);
1029
1030    if let Some(args) = KEY_INPUT_EVENT.on_unhandled(update) {
1031        if args.state == KeyState::Pressed {
1032            if args.target.widget_id() == widget.id() {
1033                match &args.key {
1034                    Key::ArrowRight => {
1035                        let mut modifiers = args.modifiers;
1036                        let select = selectable && modifiers.take_shift();
1037                        let word = modifiers.take_ctrl();
1038                        if modifiers.is_empty() && (editable || select) {
1039                            args.propagation().stop();
1040
1041                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1042
1043                            if select {
1044                                if word {
1045                                    TextSelectOp::select_next_word()
1046                                } else {
1047                                    TextSelectOp::select_next()
1048                                }
1049                            } else if word {
1050                                TextSelectOp::next_word()
1051                            } else {
1052                                TextSelectOp::next()
1053                            }
1054                            .call();
1055                        }
1056                    }
1057                    Key::ArrowLeft => {
1058                        let mut modifiers = args.modifiers;
1059                        let select = selectable && modifiers.take_shift();
1060                        let word = modifiers.take_ctrl();
1061                        if modifiers.is_empty() && (editable || select) {
1062                            args.propagation().stop();
1063
1064                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1065
1066                            if select {
1067                                if word {
1068                                    TextSelectOp::select_prev_word()
1069                                } else {
1070                                    TextSelectOp::select_prev()
1071                                }
1072                            } else if word {
1073                                TextSelectOp::prev_word()
1074                            } else {
1075                                TextSelectOp::prev()
1076                            }
1077                            .call();
1078                        }
1079                    }
1080                    Key::ArrowUp => {
1081                        if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 || TEXT.try_rich().is_some() {
1082                            let mut modifiers = args.modifiers;
1083                            let select = selectable && modifiers.take_shift();
1084                            if modifiers.is_empty() && (editable || select) {
1085                                args.propagation().stop();
1086
1087                                TEXT.resolve().selection_by = SelectionBy::Keyboard;
1088
1089                                if select {
1090                                    TextSelectOp::select_line_up()
1091                                } else {
1092                                    TextSelectOp::line_up()
1093                                }
1094                                .call();
1095                            }
1096                        }
1097                    }
1098                    Key::ArrowDown => {
1099                        if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 || TEXT.try_rich().is_some() {
1100                            let mut modifiers = args.modifiers;
1101                            let select = selectable && modifiers.take_shift();
1102                            if modifiers.is_empty() && (editable || select) {
1103                                args.propagation().stop();
1104
1105                                TEXT.resolve().selection_by = SelectionBy::Keyboard;
1106
1107                                if select {
1108                                    TextSelectOp::select_line_down()
1109                                } else {
1110                                    TextSelectOp::line_down()
1111                                }
1112                                .call();
1113                            }
1114                        }
1115                    }
1116                    Key::PageUp => {
1117                        if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 || TEXT.try_rich().is_some() {
1118                            let mut modifiers = args.modifiers;
1119                            let select = selectable && modifiers.take_shift();
1120                            if modifiers.is_empty() && (editable || select) {
1121                                args.propagation().stop();
1122
1123                                TEXT.resolve().selection_by = SelectionBy::Keyboard;
1124
1125                                if select {
1126                                    TextSelectOp::select_page_up()
1127                                } else {
1128                                    TextSelectOp::page_up()
1129                                }
1130                                .call();
1131                            }
1132                        }
1133                    }
1134                    Key::PageDown => {
1135                        if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 || TEXT.try_rich().is_some() {
1136                            let mut modifiers = args.modifiers;
1137                            let select = selectable && modifiers.take_shift();
1138                            if modifiers.is_empty() && (editable || select) {
1139                                args.propagation().stop();
1140
1141                                TEXT.resolve().selection_by = SelectionBy::Keyboard;
1142
1143                                if select {
1144                                    TextSelectOp::select_page_down()
1145                                } else {
1146                                    TextSelectOp::page_down()
1147                                }
1148                                .call();
1149                            }
1150                        }
1151                    }
1152                    Key::Home => {
1153                        let mut modifiers = args.modifiers;
1154                        let select = selectable && modifiers.take_shift();
1155                        let full_text = modifiers.take_ctrl();
1156                        if modifiers.is_empty() && (editable || select) {
1157                            args.propagation().stop();
1158
1159                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1160
1161                            if select {
1162                                if full_text {
1163                                    TextSelectOp::select_text_start()
1164                                } else {
1165                                    TextSelectOp::select_line_start()
1166                                }
1167                            } else if full_text {
1168                                TextSelectOp::text_start()
1169                            } else {
1170                                TextSelectOp::line_start()
1171                            }
1172                            .call();
1173                        }
1174                    }
1175                    Key::End => {
1176                        let mut modifiers = args.modifiers;
1177                        let select = selectable && modifiers.take_shift();
1178                        let full_text = modifiers.take_ctrl();
1179                        if modifiers.is_empty() && (editable || select) {
1180                            args.propagation().stop();
1181
1182                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1183
1184                            if select {
1185                                if full_text {
1186                                    TextSelectOp::select_text_end()
1187                                } else {
1188                                    TextSelectOp::select_line_end()
1189                                }
1190                            } else if full_text {
1191                                TextSelectOp::text_end()
1192                            } else {
1193                                TextSelectOp::line_end()
1194                            }
1195                            .call();
1196                        }
1197                    }
1198                    Key::Escape => {
1199                        if args.modifiers.is_empty() && (editable || selectable) {
1200                            args.propagation().stop();
1201                            TEXT.resolve().selection_by = SelectionBy::Keyboard;
1202
1203                            TextSelectOp::clear_selection().call();
1204                        }
1205                    }
1206                    _ => {}
1207                }
1208            }
1209        } else if let Key::Alt | Key::AltGraph = &args.key {
1210            if TEXT.try_rich().is_some() {
1211                if TEXT.take_rich_selection_started_by_alt() {
1212                    args.propagation().stop();
1213                }
1214            } else if mem::take(&mut edit.selection_started_by_alt) {
1215                args.propagation().stop();
1216            }
1217        }
1218    } else if let Some(args) = MOUSE_INPUT_EVENT.on_unhandled(update) {
1219        if args.is_primary() && args.is_mouse_down() && args.target.widget_id() == widget.id() {
1220            let mut modifiers = args.modifiers;
1221            let alt = modifiers.take_alt();
1222            let select = selectable && modifiers.take_shift();
1223
1224            if modifiers.is_empty() && (!selectable_alt_only || alt) {
1225                args.propagation().stop();
1226                TEXT.resolve().selection_by = SelectionBy::Mouse;
1227                if alt {
1228                    if TEXT.try_rich().is_some() {
1229                        TEXT.flag_rich_selection_started_by_alt();
1230                    } else {
1231                        edit.selection_started_by_alt = true;
1232                    }
1233                }
1234
1235                edit.click_count = if let Some(info) = &mut edit.selection_mouse_down {
1236                    let cfg = MOUSE.multi_click_config().get();
1237
1238                    let double_allowed = args.timestamp.duration_since(info.timestamp) <= cfg.time && {
1239                        let dist = (info.position.to_vector() - args.position.to_vector()).abs();
1240                        let area = cfg.area;
1241                        dist.x <= area.width && dist.y <= area.height
1242                    };
1243
1244                    if double_allowed {
1245                        info.timestamp = args.timestamp;
1246                        info.count += 1;
1247                        info.count = info.count.min(4);
1248                    } else {
1249                        *info = SelectionMouseDown {
1250                            position: args.position,
1251                            timestamp: args.timestamp,
1252                            count: 1,
1253                        };
1254                    }
1255
1256                    info.count
1257                } else {
1258                    edit.selection_mouse_down = Some(SelectionMouseDown {
1259                        position: args.position,
1260                        timestamp: args.timestamp,
1261                        count: 1,
1262                    });
1263                    1
1264                };
1265
1266                match edit.click_count {
1267                    1 => {
1268                        if select {
1269                            TextSelectOp::select_nearest_to(args.position).call()
1270                        } else {
1271                            TextSelectOp::nearest_to(args.position).call();
1272
1273                            // select all on mouse-up if only acquire focus
1274                            edit.auto_select = selectable
1275                                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1276                                && !FOCUS.is_focused(widget.id()).get()
1277                                && TEXT.resolved().caret.selection_range().is_none();
1278                        }
1279                    }
1280                    2 => {
1281                        if selectable {
1282                            TextSelectOp::select_word_nearest_to(!select, args.position).call()
1283                        }
1284                    }
1285                    3 => {
1286                        if selectable {
1287                            TextSelectOp::select_line_nearest_to(!select, args.position).call()
1288                        }
1289                    }
1290                    4 => {
1291                        if selectable {
1292                            TextSelectOp::select_all().call()
1293                        }
1294                    }
1295                    _ => unreachable!(),
1296                };
1297                if selectable {
1298                    let id = widget.id();
1299                    edit.selection_move_handles.push(MOUSE_MOVE_EVENT.subscribe(id));
1300                    edit.selection_move_handles.push(POINTER_CAPTURE_EVENT.subscribe(id));
1301                    POINTER_CAPTURE.capture_widget(id);
1302                }
1303            }
1304        } else {
1305            if mem::take(&mut edit.auto_select)
1306                && selectable
1307                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1308                && args.is_primary()
1309                && args.is_mouse_up()
1310                && FOCUS.is_focused(widget.id()).get()
1311                && TEXT.resolved().caret.selection_range().is_none()
1312            {
1313                TextSelectOp::select_all().call()
1314            }
1315            edit.selection_move_handles.clear();
1316        }
1317    } else if let Some(args) = TOUCH_INPUT_EVENT.on_unhandled(update) {
1318        let mut modifiers = args.modifiers;
1319        let alt = modifiers.take_alt();
1320        if modifiers.is_empty() && (!selectable_alt_only || alt) && args.target.widget_id() == widget.id() {
1321            edit.auto_select = selectable
1322                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1323                && args.modifiers.is_empty()
1324                && args.is_touch_start()
1325                && !FOCUS.is_focused(widget.id()).get()
1326                && TEXT.resolved().caret.selection_range().is_none();
1327        }
1328    } else if let Some(args) = TOUCH_TAP_EVENT.on_unhandled(update) {
1329        let mut modifiers = args.modifiers;
1330        let alt = modifiers.take_alt();
1331        if modifiers.is_empty() && (!selectable_alt_only || alt) && args.target.widget_id() == widget.id() {
1332            args.propagation().stop();
1333
1334            TEXT.resolve().selection_by = SelectionBy::Touch;
1335            if alt {
1336                if TEXT.try_rich().is_some() {
1337                    TEXT.flag_rich_selection_started_by_alt();
1338                } else {
1339                    edit.selection_started_by_alt = true;
1340                }
1341            }
1342
1343            TextSelectOp::nearest_to(args.position).call();
1344
1345            if mem::take(&mut edit.auto_select)
1346                && selectable
1347                && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1348                && FOCUS.is_focused(WIDGET.id()).get()
1349                && TEXT.resolved().caret.selection_range().is_none()
1350            {
1351                TextSelectOp::select_all().call()
1352            }
1353        }
1354    } else if let Some(args) = TOUCH_LONG_PRESS_EVENT.on_unhandled(update) {
1355        let mut modifiers = args.modifiers;
1356        let alt = modifiers.take_alt();
1357        if modifiers.is_empty() && (!selectable_alt_only || alt) && selectable && args.target.widget_id() == widget.id() {
1358            args.propagation().stop();
1359
1360            TEXT.resolve().selection_by = SelectionBy::Touch;
1361            if alt {
1362                if TEXT.try_rich().is_some() {
1363                    TEXT.flag_rich_selection_started_by_alt();
1364                } else {
1365                    edit.selection_started_by_alt = true;
1366                }
1367            }
1368
1369            TextSelectOp::select_word_nearest_to(true, args.position).call();
1370        }
1371    } else if let Some(args) = MOUSE_MOVE_EVENT.on(update) {
1372        if !edit.selection_move_handles.is_dummy() && selectable {
1373            let handle = if let Some(rich_root_id) = TEXT.try_rich().map(|r| r.root_id) {
1374                args.target.contains(rich_root_id)
1375            } else {
1376                args.target.widget_id() == widget.id()
1377            };
1378
1379            if handle {
1380                args.propagation().stop();
1381
1382                match edit.click_count {
1383                    1 => TextSelectOp::select_nearest_to(args.position).call(),
1384                    2 => TextSelectOp::select_word_nearest_to(false, args.position).call(),
1385                    3 => TextSelectOp::select_line_nearest_to(false, args.position).call(),
1386                    4 => {}
1387                    _ => unreachable!(),
1388                }
1389            }
1390        }
1391    } else if let Some(args) = POINTER_CAPTURE_EVENT.on(update) {
1392        if args.is_lost(widget.id()) {
1393            edit.selection_move_handles.clear();
1394            edit.auto_select = false;
1395        }
1396    } else if selectable {
1397        if let Some(args) = SELECT_CMD.scoped(widget.id()).on_unhandled(update) {
1398            if let Some(op) = args.param::<TextSelectOp>() {
1399                args.propagation().stop();
1400                op.clone().call();
1401            }
1402        } else if let Some(args) = SELECT_ALL_CMD.scoped(widget.id()).on_unhandled(update) {
1403            args.propagation().stop();
1404            TextSelectOp::select_all().call();
1405        }
1406    }
1407
1408    let mut resolve = TEXT.resolve();
1409    let caret = &mut resolve.caret;
1410    if (caret.index, caret.index_version, caret.selection_index) != prev_caret_index {
1411        if !editable || caret.index.is_none() || !FOCUS.is_focused(widget.id()).get() {
1412            edit.caret_animation = VarHandle::dummy();
1413            caret.opacity = var(0.fct()).read_only();
1414        } else {
1415            caret.opacity = KEYBOARD.caret_animation();
1416            edit.caret_animation = caret.opacity.subscribe(UpdateOp::RenderUpdate, widget.id());
1417        }
1418        resolve.pending_layout |= PendingLayout::CARET;
1419        WIDGET.layout(); // update caret_origin
1420    }
1421}