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