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
48pub 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 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(); 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 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 *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 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 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 *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 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 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 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 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 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 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 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 }
839 self.txt_is_measured = is_measure;
840
841 metrics.constraints().inner().fill_size_or(ctx.shaped_text.size())
842 }
843
844 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 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(); 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 } 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#[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 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(); }
1421}