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