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