1use std::{mem, sync::Arc};
2
3use atomic::Atomic;
4use parking_lot::RwLock;
5use zng_app::{
6 DInstant,
7 event::{AnyEventArgs as _, CommandHandle, EventHandle, EventHandles},
8 widget::{
9 WIDGET,
10 node::{UiNode, UiNodeOp, match_node},
11 },
12};
13use zng_ext_font::{CaretIndex, FontFaceList, Hyphens, SegmentedText, ShapedText, TextShapingArgs, font_features::FontVariations};
14use zng_ext_input::{
15 focus::FOCUS,
16 keyboard::{KEY_INPUT_EVENT, KEYBOARD},
17 mouse::{MOUSE, MOUSE_INPUT_EVENT, MOUSE_MOVE_EVENT},
18 pointer_capture::{POINTER_CAPTURE, POINTER_CAPTURE_EVENT},
19 touch::{TOUCH_INPUT_EVENT, TOUCH_LONG_PRESS_EVENT, TOUCH_TAP_EVENT},
20};
21use zng_ext_l10n::LANG_VAR;
22use zng_ext_undo::UNDO;
23use zng_ext_window::WidgetInfoBuilderImeArea as _;
24use zng_layout::{
25 context::{InlineConstraints, InlineConstraintsMeasure, InlineSegment, LAYOUT, LayoutMetrics},
26 unit::{DipPoint, FactorUnits as _, Px, PxBox, PxConstraints2d, PxRect, PxSize, PxTransform, Rect, Size},
27};
28use zng_view_api::keyboard::{Key, KeyState};
29use zng_wgt::prelude::*;
30use zng_wgt_scroll::{SCROLL, cmd::ScrollToMode};
31
32use crate::{
33 ACCEPTS_ENTER_VAR, ACCEPTS_TAB_VAR, AUTO_SELECTION_VAR, AutoSelection, FONT_FAMILY_VAR, FONT_FEATURES_VAR, FONT_SIZE_VAR,
34 FONT_STRETCH_VAR, FONT_STYLE_VAR, FONT_VARIATIONS_VAR, FONT_WEIGHT_VAR, HYPHEN_CHAR_VAR, HYPHENS_VAR, IME_UNDERLINE_THICKNESS_VAR,
35 JUSTIFY_MODE_VAR, LETTER_SPACING_VAR, LINE_BREAK_VAR, LINE_HEIGHT_VAR, LINE_SPACING_VAR, OBSCURE_TXT_VAR, OBSCURING_CHAR_VAR,
36 OVERLINE_THICKNESS_VAR, STRIKETHROUGH_THICKNESS_VAR, TAB_LENGTH_VAR, TEXT_ALIGN_VAR, TEXT_EDITABLE_VAR, TEXT_OVERFLOW_ALIGN_VAR,
37 TEXT_OVERFLOW_VAR, TEXT_SELECTABLE_VAR, TEXT_WRAP_VAR, TextOverflow, UNDERLINE_POSITION_VAR, UNDERLINE_SKIP_VAR,
38 UNDERLINE_THICKNESS_VAR, UnderlinePosition, UnderlineSkip, WORD_BREAK_VAR, WORD_SPACING_VAR,
39 cmd::{SELECT_ALL_CMD, SELECT_CMD, TextSelectOp},
40 node::SelectionBy,
41};
42
43use super::{LAIDOUT_TEXT, LaidoutText, PendingLayout, RenderInfo, TEXT};
44
45pub fn layout_text(child: impl UiNode) -> impl UiNode {
56 let child = layout_text_edit(child);
57 let child = layout_text_layout(child);
58 layout_text_context(child)
59}
60fn layout_text_context(child: impl UiNode) -> impl UiNode {
61 let mut laidout = None;
62 match_node(child, move |child, op| match op {
63 UiNodeOp::Init => {
64 let fonts = FontFaceList::empty().sized(Px(10), vec![]);
65 laidout = Some(Arc::new(RwLock::new(LaidoutText {
66 shaped_text: ShapedText::new(fonts.best()),
67 fonts,
68 overflow: None,
69 overflow_suffix: None,
70 shaped_text_version: 0,
71 overlines: vec![],
72 overline_thickness: Px(0),
73 strikethroughs: vec![],
74 strikethrough_thickness: Px(0),
75 underlines: vec![],
76 underline_thickness: Px(0),
77 ime_underlines: vec![],
78 ime_underline_thickness: Px(0),
79 caret_origin: None,
80 caret_selection_origin: None,
81 caret_retained_x: Px(0),
82 render_info: RenderInfo {
83 transform: PxTransform::identity(),
84 scale_factor: 1.fct(),
85 },
86 viewport: PxSize::zero(),
87 })));
88
89 LAIDOUT_TEXT.with_context(&mut laidout, || child.init());
90 }
91 UiNodeOp::Deinit => {
92 LAIDOUT_TEXT.with_context(&mut laidout, || child.deinit());
93
94 laidout = None;
95 }
96 op => LAIDOUT_TEXT.with_context(&mut laidout, || child.op(op)),
97 })
98}
99fn layout_text_layout(child: impl UiNode) -> impl UiNode {
100 let mut txt = LayoutTextFinal {
101 shaping_args: TextShapingArgs::default(),
102 pending: PendingLayout::empty(),
103 txt_is_measured: false,
104 last_layout: (LayoutMetrics::new(1.fct(), PxSize::zero(), Px(0)), None),
105 baseline: Px(0),
106 };
107
108 match_node(child, move |child, op| match op {
109 UiNodeOp::Init => {
110 WIDGET
111 .sub_var(&FONT_SIZE_VAR)
112 .sub_var(&FONT_VARIATIONS_VAR)
113 .sub_var(&LETTER_SPACING_VAR)
114 .sub_var(&WORD_SPACING_VAR)
115 .sub_var(&LINE_SPACING_VAR)
116 .sub_var(&LINE_HEIGHT_VAR)
117 .sub_var(&TAB_LENGTH_VAR);
118 WIDGET
119 .sub_var(&UNDERLINE_POSITION_VAR)
120 .sub_var(&UNDERLINE_SKIP_VAR)
121 .sub_var_layout(&OVERLINE_THICKNESS_VAR)
122 .sub_var_layout(&STRIKETHROUGH_THICKNESS_VAR)
123 .sub_var_layout(&UNDERLINE_THICKNESS_VAR);
124 WIDGET
125 .sub_var(&LINE_BREAK_VAR)
126 .sub_var(&WORD_BREAK_VAR)
127 .sub_var(&HYPHENS_VAR)
128 .sub_var(&HYPHEN_CHAR_VAR)
129 .sub_var(&TEXT_WRAP_VAR)
130 .sub_var(&TEXT_OVERFLOW_VAR);
131 WIDGET
132 .sub_var_layout(&TEXT_ALIGN_VAR)
133 .sub_var_layout(&JUSTIFY_MODE_VAR)
134 .sub_var_layout(&TEXT_OVERFLOW_ALIGN_VAR);
135
136 WIDGET.sub_var(&FONT_FEATURES_VAR);
137
138 WIDGET.sub_var(&OBSCURE_TXT_VAR).sub_var(&OBSCURING_CHAR_VAR);
139
140 txt.shaping_args.lang = LANG_VAR.with(|l| l.best().clone());
143 txt.shaping_args.direction = txt.shaping_args.lang.direction();
144 txt.shaping_args.line_break = LINE_BREAK_VAR.get();
145 txt.shaping_args.word_break = WORD_BREAK_VAR.get();
146 txt.shaping_args.hyphens = HYPHENS_VAR.get();
147 txt.shaping_args.hyphen_char = HYPHEN_CHAR_VAR.get();
148 txt.shaping_args.font_features = FONT_FEATURES_VAR.with(|f| f.finalize());
149
150 if OBSCURE_TXT_VAR.get() {
151 txt.shaping_args.obscuring_char = Some(OBSCURING_CHAR_VAR.get());
152 }
153 }
154 UiNodeOp::Deinit => {
155 txt.shaping_args = TextShapingArgs::default();
156 }
157 UiNodeOp::Update { .. } => {
158 if FONT_SIZE_VAR.is_new() || FONT_VARIATIONS_VAR.is_new() {
159 txt.pending.insert(PendingLayout::RESHAPE);
160 TEXT.layout().overflow_suffix = None;
161 WIDGET.layout();
162 }
163
164 if LETTER_SPACING_VAR.is_new()
165 || WORD_SPACING_VAR.is_new()
166 || LINE_SPACING_VAR.is_new()
167 || LINE_HEIGHT_VAR.is_new()
168 || TAB_LENGTH_VAR.is_new()
169 || LANG_VAR.is_new()
170 {
171 txt.shaping_args.lang = LANG_VAR.with(|l| l.best().clone());
172 txt.shaping_args.direction = txt.shaping_args.lang.direction(); TEXT.layout().overflow_suffix = None;
174 txt.pending.insert(PendingLayout::RESHAPE);
175 WIDGET.layout();
176 }
177
178 if UNDERLINE_POSITION_VAR.is_new() || UNDERLINE_SKIP_VAR.is_new() {
179 txt.pending.insert(PendingLayout::UNDERLINE);
180 WIDGET.layout();
181 }
182
183 if let Some(lb) = LINE_BREAK_VAR.get_new() {
184 if txt.shaping_args.line_break != lb {
185 txt.shaping_args.line_break = lb;
186 txt.pending.insert(PendingLayout::RESHAPE);
187 WIDGET.layout();
188 }
189 }
190 if let Some(wb) = WORD_BREAK_VAR.get_new() {
191 if txt.shaping_args.word_break != wb {
192 txt.shaping_args.word_break = wb;
193 txt.pending.insert(PendingLayout::RESHAPE);
194 WIDGET.layout();
195 }
196 }
197 if let Some(h) = HYPHENS_VAR.get_new() {
198 if txt.shaping_args.hyphens != h {
199 txt.shaping_args.hyphens = h;
200 txt.pending.insert(PendingLayout::RESHAPE);
201 WIDGET.layout();
202 }
203 }
204 if let Some(c) = HYPHEN_CHAR_VAR.get_new() {
205 txt.shaping_args.hyphen_char = c;
206 if Hyphens::None != txt.shaping_args.hyphens {
207 txt.pending.insert(PendingLayout::RESHAPE);
208 WIDGET.layout();
209 }
210 }
211
212 if OBSCURE_TXT_VAR.is_new() || OBSCURING_CHAR_VAR.is_new() {
213 let c = if OBSCURE_TXT_VAR.get() {
214 Some(OBSCURING_CHAR_VAR.get())
215 } else {
216 None
217 };
218 if txt.shaping_args.obscuring_char != c {
219 txt.shaping_args.obscuring_char = c;
220 txt.pending.insert(PendingLayout::RESHAPE);
221 WIDGET.layout();
222 }
223 }
224
225 if TEXT_WRAP_VAR.is_new() {
226 txt.pending.insert(PendingLayout::RESHAPE);
227 WIDGET.layout();
228 }
229 if TEXT_OVERFLOW_VAR.is_new() {
230 TEXT.layout().overflow_suffix = None;
231 txt.pending.insert(PendingLayout::OVERFLOW);
232 WIDGET.layout();
233 }
234
235 FONT_FEATURES_VAR.with_new(|f| {
236 txt.shaping_args.font_features = f.finalize();
237 txt.pending.insert(PendingLayout::RESHAPE);
238 WIDGET.layout();
239 });
240
241 if FONT_FAMILY_VAR.is_new()
242 || FONT_STYLE_VAR.is_new()
243 || FONT_STRETCH_VAR.is_new()
244 || FONT_WEIGHT_VAR.is_new()
245 || LANG_VAR.is_new()
246 {
247 TEXT.layout().overflow_suffix = None;
249 }
250 }
251 UiNodeOp::Measure { wm, desired_size } => {
252 child.delegated();
253
254 let metrics = LAYOUT.metrics();
255
256 *desired_size = if let Some(size) = txt.measure(&metrics) {
257 size
258 } else {
259 let size = txt.layout(&metrics, true);
260
261 if let Some(inline) = wm.inline() {
262 let ctx = TEXT.laidout();
263
264 if let Some(first_line) = ctx.shaped_text.line(0) {
265 inline.first = first_line.original_size();
266 inline.with_first_segs(|i| {
267 for seg in first_line.segs() {
268 i.push(InlineSegment {
269 width: seg.advance(),
270 kind: seg.kind(),
271 });
272 }
273 });
274 } else {
275 inline.first = PxSize::zero();
276 inline.with_first_segs(|i| i.clear());
277 }
278
279 if ctx.shaped_text.lines_len() == 1 {
280 inline.last = inline.first;
281 inline.last_segs = inline.first_segs.clone();
282 } else if let Some(last_line) = ctx.shaped_text.line(ctx.shaped_text.lines_len().saturating_sub(1)) {
283 inline.last = last_line.original_size();
284 inline.with_last_segs(|i| {
285 for seg in last_line.segs() {
286 i.push(InlineSegment {
287 width: seg.advance(),
288 kind: seg.kind(),
289 })
290 }
291 })
292 } else {
293 inline.last = PxSize::zero();
294 inline.with_last_segs(|i| i.clear());
295 }
296
297 inline.first_wrapped = ctx.shaped_text.first_wrapped();
298 inline.last_wrapped = ctx.shaped_text.lines_len() > 1;
299 }
300 size
301 };
302
303 LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*desired_size), || {
304 *desired_size = child.measure(wm);
306 });
307 }
308 UiNodeOp::Layout { wl, final_size } => {
309 child.delegated();
310
311 let metrics = LAYOUT.metrics();
312
313 TEXT.layout().viewport = metrics.viewport();
314
315 *final_size = txt.layout(&metrics, false);
316
317 if txt.pending != PendingLayout::empty() {
318 WIDGET.render();
319 txt.pending = PendingLayout::empty();
320 }
321
322 if let Some(inline) = wl.inline() {
323 let ctx = TEXT.laidout();
324
325 let last_line = ctx.shaped_text.lines_len().saturating_sub(1);
326
327 inline.first_segs.clear();
328 inline.last_segs.clear();
329
330 for (i, line) in ctx.shaped_text.lines().enumerate() {
331 if i == 0 {
332 let info = ctx.shaped_text.line(0).unwrap().segs().map(|s| s.inline_info());
333 if LAYOUT.direction().is_rtl() {
334 inline.set_first_segs(info.rev());
336 } else {
337 inline.set_first_segs(info);
338 }
339 } else if i == last_line {
340 let info = ctx
341 .shaped_text
342 .line(ctx.shaped_text.lines_len().saturating_sub(1))
343 .unwrap()
344 .segs()
345 .map(|s| s.inline_info());
346 if LAYOUT.direction().is_rtl() {
347 inline.set_last_segs(info.rev());
349 } else {
350 inline.set_last_segs(info);
351 }
352 }
353
354 inline.rows.push(line.rect());
355 }
356 }
357
358 wl.set_baseline(txt.baseline);
359
360 LAYOUT.with_constraints(metrics.constraints().with_new_min_size(*final_size), || {
361 *final_size = child.layout(wl);
363 });
364 }
365 UiNodeOp::Render { .. } => {
366 txt.ensure_layout_for_render();
367 }
368 UiNodeOp::RenderUpdate { .. } => {
369 txt.ensure_layout_for_render();
370 }
371 _ => {}
372 })
373}
374struct LayoutTextFinal {
375 shaping_args: TextShapingArgs,
376 pending: PendingLayout,
377
378 txt_is_measured: bool,
379 last_layout: (LayoutMetrics, Option<InlineConstraintsMeasure>),
380 baseline: Px,
381}
382impl LayoutTextFinal {
383 fn measure(&mut self, metrics: &LayoutMetrics) -> Option<PxSize> {
384 if metrics.inline_constraints().is_some() {
385 return None;
386 }
387
388 metrics.constraints().fill_or_exact()
389 }
390
391 fn layout(&mut self, metrics: &LayoutMetrics, is_measure: bool) -> PxSize {
392 let mut resolved = TEXT.resolved();
393
394 self.pending |= resolved.pending_layout;
395
396 let font_size = metrics.font_size();
397
398 let mut ctx = TEXT.layout();
399
400 if font_size != ctx.fonts.requested_size() || !ctx.fonts.is_sized_from(&resolved.faces) {
401 ctx.fonts = resolved.faces.sized(font_size, FONT_VARIATIONS_VAR.with(FontVariations::finalize));
402 self.pending.insert(PendingLayout::RESHAPE);
403 }
404
405 if TEXT_WRAP_VAR.get() && !metrics.constraints().x.is_unbounded() {
406 let max_width = metrics.constraints().x.max().unwrap();
407 if self.shaping_args.max_width != max_width {
408 self.shaping_args.max_width = max_width;
409
410 if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(max_width) {
411 self.pending.insert(PendingLayout::RESHAPE);
412 }
413 }
414 } else if self.shaping_args.max_width != Px::MAX {
415 self.shaping_args.max_width = Px::MAX;
416 if !self.pending.contains(PendingLayout::RESHAPE) && ctx.shaped_text.can_rewrap(Px::MAX) {
417 self.pending.insert(PendingLayout::RESHAPE);
418 }
419 }
420
421 if ctx.caret_origin.is_none() {
422 self.pending.insert(PendingLayout::CARET);
423 }
424
425 if let Some(inline) = metrics.inline_constraints() {
426 match inline {
427 InlineConstraints::Measure(m) => {
428 if self.shaping_args.inline_constraints != Some(m) {
429 self.shaping_args.inline_constraints = Some(m);
430 self.pending.insert(PendingLayout::RESHAPE);
431 }
432 }
433 InlineConstraints::Layout(l) => {
434 if !self.pending.contains(PendingLayout::RESHAPE)
435 && (Some(l.first_segs.len()) != ctx.shaped_text.line(0).map(|l| l.segs_len())
436 || Some(l.last_segs.len())
437 != ctx
438 .shaped_text
439 .line(ctx.shaped_text.lines_len().saturating_sub(1))
440 .map(|l| l.segs_len()))
441 {
442 self.pending.insert(PendingLayout::RESHAPE);
443 }
444
445 if !self.pending.contains(PendingLayout::RESHAPE_LINES)
446 && (ctx.shaped_text.mid_clear() != l.mid_clear
447 || ctx.shaped_text.line(0).map(|l| l.rect()) != Some(l.first)
448 || ctx
449 .shaped_text
450 .line(ctx.shaped_text.lines_len().saturating_sub(1))
451 .map(|l| l.rect())
452 != Some(l.last))
453 {
454 self.pending.insert(PendingLayout::RESHAPE_LINES);
455 }
456 }
457 }
458 } else if self.shaping_args.inline_constraints.is_some() {
459 self.shaping_args.inline_constraints = None;
460 self.pending.insert(PendingLayout::RESHAPE);
461 }
462
463 if !self.pending.contains(PendingLayout::RESHAPE_LINES) {
464 let size = ctx.shaped_text.size();
465 if metrics.constraints().fill_size_or(size) != ctx.shaped_text.align_size() {
466 self.pending.insert(PendingLayout::RESHAPE_LINES);
467 }
468 }
469
470 let font = ctx.fonts.best();
471
472 let space_len = font.space_x_advance();
473 let dft_tab_len = space_len * 3;
474
475 let (letter_spacing, word_spacing, tab_length) = {
476 LAYOUT.with_constraints(PxConstraints2d::new_exact(space_len, space_len), || {
477 (
478 LETTER_SPACING_VAR.layout_x(),
479 WORD_SPACING_VAR.layout_x(),
480 TAB_LENGTH_VAR.layout_dft_x(dft_tab_len),
481 )
482 })
483 };
484
485 let dft_line_height = font.metrics().line_height();
486 let line_height = {
487 LAYOUT.with_constraints(PxConstraints2d::new_exact(dft_line_height, dft_line_height), || {
488 LINE_HEIGHT_VAR.layout_dft_y(dft_line_height)
489 })
490 };
491 let line_spacing =
492 { LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || LINE_SPACING_VAR.layout_y()) };
493
494 if !self.pending.contains(PendingLayout::RESHAPE)
495 && (letter_spacing != self.shaping_args.letter_spacing
496 || word_spacing != self.shaping_args.word_spacing
497 || tab_length != self.shaping_args.tab_x_advance)
498 {
499 self.pending.insert(PendingLayout::RESHAPE);
500 }
501 if !self.pending.contains(PendingLayout::RESHAPE_LINES)
502 && (line_spacing != self.shaping_args.line_spacing || line_height != self.shaping_args.line_height)
503 {
504 self.pending.insert(PendingLayout::RESHAPE_LINES);
505 }
506
507 self.shaping_args.letter_spacing = letter_spacing;
508 self.shaping_args.word_spacing = word_spacing;
509 self.shaping_args.tab_x_advance = tab_length;
510 self.shaping_args.line_height = line_height;
511 self.shaping_args.line_spacing = line_spacing;
512
513 let dft_thickness = font.metrics().underline_thickness;
514 let (overline, strikethrough, underline, ime_underline) = {
515 LAYOUT.with_constraints(PxConstraints2d::new_exact(line_height, line_height), || {
516 (
517 OVERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
518 STRIKETHROUGH_THICKNESS_VAR.layout_dft_y(dft_thickness),
519 UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
520 IME_UNDERLINE_THICKNESS_VAR.layout_dft_y(dft_thickness),
521 )
522 })
523 };
524
525 if !self.pending.contains(PendingLayout::OVERLINE) && (ctx.overline_thickness == 0) != (overline == 0) {
526 self.pending.insert(PendingLayout::OVERLINE);
527 }
528 if !self.pending.contains(PendingLayout::STRIKETHROUGH) && (ctx.strikethrough_thickness == 0) != (strikethrough == 0) {
529 self.pending.insert(PendingLayout::STRIKETHROUGH);
530 }
531 if !self.pending.contains(PendingLayout::UNDERLINE)
532 && ((ctx.underline_thickness == 0) != (underline == 0) || (ctx.ime_underline_thickness != 0) != (ime_underline != 0))
533 {
534 self.pending.insert(PendingLayout::UNDERLINE);
535 }
536 ctx.overline_thickness = overline;
537 ctx.strikethrough_thickness = strikethrough;
538 ctx.underline_thickness = underline;
539 ctx.ime_underline_thickness = ime_underline;
540
541 let align = TEXT_ALIGN_VAR.get();
542 let justify = JUSTIFY_MODE_VAR.get();
543 let overflow_align = TEXT_OVERFLOW_ALIGN_VAR.get();
544 if !self.pending.contains(PendingLayout::RESHAPE_LINES)
545 && (align != ctx.shaped_text.align()
546 || justify != ctx.shaped_text.justify_mode().unwrap_or_default()
547 || overflow_align != ctx.shaped_text.overflow_align())
548 {
549 self.pending.insert(PendingLayout::RESHAPE_LINES);
550 }
551
552 if self.pending.contains(PendingLayout::RESHAPE) {
557 ctx.shaped_text = ctx.fonts.shape_text(&resolved.segmented_text, &self.shaping_args);
558 self.pending = self.pending.intersection(PendingLayout::RESHAPE_LINES);
559 }
560
561 if !self.pending.contains(PendingLayout::RESHAPE_LINES)
562 && ctx.shaped_text.align_size() != metrics.constraints().fill_size_or(ctx.shaped_text.block_size())
563 {
564 self.pending.insert(PendingLayout::RESHAPE_LINES);
565 }
566
567 if self.pending.contains(PendingLayout::RESHAPE_LINES) && metrics.inline_constraints().is_none() {
568 ctx.shaped_text.reshape_lines(
576 metrics.constraints(),
577 metrics.inline_constraints().map(|c| c.layout()),
578 align,
579 overflow_align,
580 line_height,
581 line_spacing,
582 metrics.direction(),
583 );
584 }
585
586 if !is_measure {
587 self.last_layout = (metrics.clone(), self.shaping_args.inline_constraints);
588
589 if self.pending.contains(PendingLayout::RESHAPE_LINES) {
590 if metrics.inline_constraints().is_some() {
591 ctx.shaped_text.reshape_lines(
593 metrics.constraints(),
594 metrics.inline_constraints().map(|c| c.layout()),
595 align,
596 overflow_align,
597 line_height,
598 line_spacing,
599 metrics.direction(),
600 );
601 }
602 ctx.shaped_text.reshape_lines_justify(justify, &self.shaping_args.lang);
603
604 ctx.shaped_text_version = ctx.shaped_text_version.wrapping_add(1);
605 drop(resolved);
606 self.baseline = ctx.shaped_text.baseline();
607 resolved = TEXT.resolved();
608 ctx.caret_origin = None;
609 ctx.caret_selection_origin = None;
610 }
611 if self.pending.contains(PendingLayout::OVERFLOW) {
612 let txt_size = ctx.shaped_text.size();
613 let max_size = metrics.constraints().fill_size_or(txt_size);
614 if txt_size.width > max_size.width || txt_size.height > max_size.height {
615 let suf_width = ctx.overflow_suffix.as_ref().map(|s| s.size().width).unwrap_or(Px(0));
616 ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf_width);
617
618 if ctx.overflow.is_some() && ctx.overflow_suffix.is_none() && !TEXT_EDITABLE_VAR.get() {
619 match TEXT_OVERFLOW_VAR.get() {
620 TextOverflow::Truncate(suf) if !suf.is_empty() => {
621 let suf = SegmentedText::new(suf, self.shaping_args.direction);
622 let suf = ctx.fonts.shape_text(&suf, &self.shaping_args);
623
624 ctx.overflow = ctx.shaped_text.overflow_info(max_size, suf.size().width);
625 ctx.overflow_suffix = Some(suf);
626 }
627 _ => {}
628 }
629 }
630 } else {
631 ctx.overflow = None;
632 }
633 }
634 if self.pending.contains(PendingLayout::OVERLINE) {
635 if ctx.overline_thickness > Px(0) {
636 ctx.overlines = ctx.shaped_text.lines().map(|l| l.overline()).collect();
637 } else {
638 ctx.overlines = vec![];
639 }
640 }
641 if self.pending.contains(PendingLayout::STRIKETHROUGH) {
642 if ctx.strikethrough_thickness > Px(0) {
643 ctx.strikethroughs = ctx.shaped_text.lines().map(|l| l.strikethrough()).collect();
644 } else {
645 ctx.strikethroughs = vec![];
646 }
647 }
648
649 if self.pending.contains(PendingLayout::UNDERLINE) {
650 let ime_range = if let Some(ime) = &resolved.ime_preview {
651 let start = ime.prev_selection.unwrap_or(ime.prev_caret).index.min(ime.prev_caret.index);
652 start..start + ime.txt.len()
653 } else {
654 0..0
655 };
656 let caret_ime_range = if !ime_range.is_empty() && (ctx.underline_thickness > Px(0) || ctx.ime_underline_thickness > Px(0)) {
657 let start = ctx.shaped_text.snap_caret_line(CaretIndex {
658 index: ime_range.start,
659 line: 0,
660 });
661 let end = ctx.shaped_text.snap_caret_line(CaretIndex {
662 index: ime_range.end,
663 line: 0,
664 });
665
666 start..end
667 } else {
668 CaretIndex::ZERO..CaretIndex::ZERO
669 };
670
671 if ctx.underline_thickness > Px(0) {
672 let mut underlines = vec![];
673
674 let skip = UNDERLINE_SKIP_VAR.get();
675 match UNDERLINE_POSITION_VAR.get() {
676 UnderlinePosition::Font => {
677 if skip == UnderlineSkip::GLYPHS | UnderlineSkip::SPACES {
678 for line in ctx.shaped_text.lines() {
679 for und in line.underline_skip_glyphs_and_spaces(ctx.underline_thickness) {
680 underlines.push(und);
681 }
682 }
683 } else if skip.contains(UnderlineSkip::GLYPHS) {
684 for line in ctx.shaped_text.lines() {
685 for und in line.underline_skip_glyphs(ctx.underline_thickness) {
686 underlines.push(und);
687 }
688 }
689 } else if skip.contains(UnderlineSkip::SPACES) {
690 for line in ctx.shaped_text.lines() {
691 for und in line.underline_skip_spaces() {
692 underlines.push(und);
693 }
694 }
695 } else {
696 for line in ctx.shaped_text.lines() {
697 let und = line.underline();
698 underlines.push(und);
699 }
700 }
701 }
702 UnderlinePosition::Descent => {
703 if skip.contains(UnderlineSkip::SPACES) {
705 for line in ctx.shaped_text.lines() {
706 for und in line.underline_descent_skip_spaces() {
707 underlines.push(und);
708 }
709 }
710 } else {
711 for line in ctx.shaped_text.lines() {
712 let und = line.underline_descent();
713 underlines.push(und);
714 }
715 }
716 }
717 }
718
719 if !ime_range.is_empty() {
720 underlines = ctx.shaped_text.clip_lines(
721 caret_ime_range.clone(),
722 true,
723 resolved.segmented_text.text(),
724 underlines.into_iter(),
725 );
726 }
727
728 ctx.underlines = underlines;
729 } else {
730 ctx.underlines = vec![];
731 }
732
733 if ctx.ime_underline_thickness > Px(0) && !ime_range.is_empty() {
734 let mut ime_underlines = vec![];
735
736 for line in ctx.shaped_text.lines() {
738 let line_range = line.text_range();
739 if line_range.start < ime_range.end && line_range.end > ime_range.start {
740 for seg in line.segs() {
741 let seg_range = seg.text_range();
742 if seg_range.start < ime_range.end && seg_range.end > ime_range.start {
743 for und in seg.underline_skip_glyphs(ctx.ime_underline_thickness) {
744 ime_underlines.push(und);
745 }
746 }
747 }
748 }
749 }
750
751 ctx.ime_underlines =
752 ctx.shaped_text
753 .clip_lines(caret_ime_range, false, resolved.segmented_text.text(), ime_underlines.into_iter());
754 } else {
755 ctx.ime_underlines = vec![];
756 }
757 }
758
759 if self.pending.contains(PendingLayout::CARET) {
760 drop(resolved);
761 let mut resolved_mut = TEXT.resolve();
762 let resolved_mut = &mut *resolved_mut;
763 let caret = &mut resolved_mut.caret;
764 if let Some(index) = &mut caret.index {
765 *index = ctx.shaped_text.snap_caret_line(*index);
766
767 let p = ctx.shaped_text.caret_origin(*index, resolved_mut.segmented_text.text());
768 if !caret.used_retained_x {
769 ctx.caret_retained_x = p.x;
770 }
771 ctx.caret_origin = Some(p);
772
773 if let Some(sel) = &mut caret.selection_index {
774 *sel = ctx.shaped_text.snap_caret_line(*sel);
775 ctx.caret_selection_origin = Some(ctx.shaped_text.caret_origin(*sel, resolved_mut.segmented_text.text()));
776 }
777
778 if !mem::take(&mut caret.skip_next_scroll) && SCROLL.try_id().is_some() {
779 let line_height = ctx
780 .shaped_text
781 .line(index.line)
782 .map(|l| l.rect().height())
783 .unwrap_or_else(|| ctx.shaped_text.line_height());
784
785 if let Some(p) = ctx.render_info.transform.transform_point(p) {
786 let p = p - WIDGET.info().inner_bounds().origin;
787 let min_rect = Rect::new(p.to_point(), Size::new(Px(1), line_height * 2 + ctx.shaped_text.line_spacing()));
788 SCROLL.scroll_to(ScrollToMode::minimal_rect(min_rect));
789 }
790 }
791 }
792 }
793
794 }
796 self.txt_is_measured = is_measure;
797
798 metrics.constraints().fill_size_or(ctx.shaped_text.size())
799 }
800
801 fn ensure_layout_for_render(&mut self) {
802 if self.txt_is_measured {
803 let metrics = self.last_layout.0.clone();
804 self.shaping_args.inline_constraints = self.last_layout.1;
805 LAYOUT.with_context(metrics.clone(), || {
806 self.layout(&metrics, false);
807 });
808
809 debug_assert!(!self.txt_is_measured);
810 }
811 }
812}
813
814fn layout_text_edit(child: impl UiNode) -> impl UiNode {
815 let mut edit = None::<Box<LayoutTextEdit>>;
817
818 match_node(child, move |child, op| {
819 let mut enable = false;
820 match op {
821 UiNodeOp::Init => {
822 enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
823 }
824 UiNodeOp::Deinit => {
825 edit = None;
826 }
827 UiNodeOp::Info { info } => {
828 if let Some(e) = &edit {
829 info.set_ime_area(e.ime_area.clone());
830 }
831 }
832 UiNodeOp::Event { update } => {
833 child.event(update);
834
835 if let Some(e) = &mut edit {
836 layout_text_edit_events(update, e);
837 }
838 }
839 UiNodeOp::Update { .. } => {
840 if TEXT_EDITABLE_VAR.is_new() || TEXT_SELECTABLE_VAR.is_new() {
841 enable = TEXT_EDITABLE_VAR.get() || TEXT_SELECTABLE_VAR.get();
842 if !enable && edit.is_some() {
843 edit = None;
844 }
845 } else if let Some(edit) = &edit {
846 TEXT.resolved().txt.with_new(|t| {
847 edit.select_all.set_enabled(!t.is_empty());
848 });
849 }
850
851 if OBSCURE_TXT_VAR.is_new() || OBSCURING_CHAR_VAR.is_new() {
852 if let Some(obscure) = OBSCURE_TXT_VAR.get_new() {
853 if edit.is_none() && WINDOW.info().access_enabled().is_enabled() {
854 WIDGET.info();
855 }
856
857 if obscure {
858 UNDO.clear();
859 }
860 }
861 }
862 }
863 UiNodeOp::Render { frame } => {
864 child.render(frame);
865 if let Some(e) = &edit {
866 e.update_ime(&mut TEXT.layout());
867 }
868 }
869 UiNodeOp::RenderUpdate { update } => {
870 child.render_update(update);
871 if let Some(e) = &edit {
872 e.update_ime(&mut TEXT.layout());
873 }
874 }
875 _ => {}
876 }
877
878 if enable {
879 let edit = LayoutTextEdit::get(&mut edit);
880
881 let editable = TEXT_EDITABLE_VAR.get();
882 let selectable = TEXT_SELECTABLE_VAR.get();
883
884 if selectable || editable {
885 let id = WIDGET.id();
886
887 edit.events[0] = MOUSE_INPUT_EVENT.subscribe(id);
888 edit.events[1] = TOUCH_TAP_EVENT.subscribe(id);
889 edit.events[2] = TOUCH_LONG_PRESS_EVENT.subscribe(id);
890 edit.events[3] = TOUCH_INPUT_EVENT.subscribe(id);
891 }
893
894 if selectable {
895 let id = WIDGET.id();
896
897 edit.select = SELECT_CMD.scoped(id).subscribe(true);
898 let is_empty = TEXT.resolved().txt.with(|t| t.is_empty());
899 edit.select_all = SELECT_ALL_CMD.scoped(id).subscribe(!is_empty);
900 }
901 }
902 })
903}
904#[derive(Default)]
906struct LayoutTextEdit {
907 events: [EventHandle; 4],
908 caret_animation: VarHandle,
909 select: CommandHandle,
910 select_all: CommandHandle,
911 ime_area: Arc<Atomic<PxRect>>,
912 click_count: u8,
913 selection_mouse_down: Option<SelectionMouseDown>,
914 auto_select: bool,
915 selection_move_handles: EventHandles,
916}
917struct SelectionMouseDown {
918 position: DipPoint,
919 timestamp: DInstant,
920 count: u8,
921}
922impl LayoutTextEdit {
923 fn get(edit_data: &mut Option<Box<Self>>) -> &mut Self {
924 &mut *edit_data.get_or_insert_with(Default::default)
925 }
926
927 fn update_ime(&self, txt: &mut LaidoutText) {
928 let transform = txt.render_info.transform;
929 let area;
930
931 if let Some(a) = txt.caret_origin {
932 let (ac, bc) = {
933 let ctx = TEXT.resolved();
934 let c = &ctx.caret;
935 (c.index, c.selection_index)
936 };
937 let ac = ac.unwrap_or(CaretIndex::ZERO);
938 let mut a_line = PxRect::new(a, PxSize::new(Px(1), txt.shaped_text.line(ac.line).unwrap().height())).to_box2d();
939
940 if let Some(b) = txt.caret_selection_origin {
941 let bc = bc.unwrap_or(CaretIndex::ZERO);
942 let b_line = PxRect::new(b, PxSize::new(Px(1), txt.shaped_text.line(bc.line).unwrap().height())).to_box2d();
943
944 a_line.min = a_line.min.min(b_line.min);
945 a_line.max = a_line.max.min(b_line.max);
946 }
947 area = a_line;
948 } else {
949 area = PxBox::from_size(txt.shaped_text.size());
950 }
951
952 if let Some(area) = transform.outer_transformed(area) {
953 self.ime_area.store(area.to_rect(), atomic::Ordering::Relaxed);
954 }
955 }
956}
957fn layout_text_edit_events(update: &EventUpdate, edit: &mut LayoutTextEdit) {
958 let resolved = TEXT.resolved();
959 let editable = TEXT_EDITABLE_VAR.get() && resolved.txt.capabilities().can_modify();
960 let selectable = TEXT_SELECTABLE_VAR.get();
961
962 if !editable && !selectable {
963 return;
964 }
965 let widget = WIDGET.info();
966 if !widget.interactivity().is_enabled() {
967 return;
968 }
969
970 let prev_caret_index = {
971 let caret = &resolved.caret;
972 (caret.index, caret.index_version, caret.selection_index)
973 };
974 drop(resolved);
975
976 if let Some(args) = KEY_INPUT_EVENT.on_unhandled(update) {
977 if args.state == KeyState::Pressed && args.target.widget_id() == widget.id() {
978 match &args.key {
979 Key::Tab => {
980 if editable && args.modifiers.is_empty() && ACCEPTS_TAB_VAR.get() {
981 args.propagation().stop();
982 TEXT.resolve().selection_by = SelectionBy::Keyboard;
983 }
984 }
985 Key::Enter => {
986 if editable && args.modifiers.is_empty() && ACCEPTS_ENTER_VAR.get() {
987 args.propagation().stop();
988 TEXT.resolve().selection_by = SelectionBy::Keyboard;
989 }
990 }
991 Key::ArrowRight => {
992 let mut modifiers = args.modifiers;
993 let select = selectable && modifiers.take_shift();
994 let word = modifiers.take_ctrl();
995 if modifiers.is_empty() && (editable || select) {
996 args.propagation().stop();
997
998 TEXT.resolve().selection_by = SelectionBy::Keyboard;
999
1000 if select {
1001 if word {
1002 TextSelectOp::select_next_word()
1003 } else {
1004 TextSelectOp::select_next()
1005 }
1006 } else if word {
1007 TextSelectOp::next_word()
1008 } else {
1009 TextSelectOp::next()
1010 }
1011 .call();
1012 }
1013 }
1014 Key::ArrowLeft => {
1015 let mut modifiers = args.modifiers;
1016 let select = selectable && modifiers.take_shift();
1017 let word = modifiers.take_ctrl();
1018 if modifiers.is_empty() && (editable || select) {
1019 args.propagation().stop();
1020
1021 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1022
1023 if select {
1024 if word {
1025 TextSelectOp::select_prev_word()
1026 } else {
1027 TextSelectOp::select_prev()
1028 }
1029 } else if word {
1030 TextSelectOp::prev_word()
1031 } else {
1032 TextSelectOp::prev()
1033 }
1034 .call();
1035 }
1036 }
1037 Key::ArrowUp => {
1038 if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1039 let mut modifiers = args.modifiers;
1040 let select = selectable && modifiers.take_shift();
1041 if modifiers.is_empty() && (editable || select) {
1042 args.propagation().stop();
1043
1044 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1045
1046 if select {
1047 TextSelectOp::select_line_up()
1048 } else {
1049 TextSelectOp::line_up()
1050 }
1051 .call();
1052 }
1053 }
1054 }
1055 Key::ArrowDown => {
1056 if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1057 let mut modifiers = args.modifiers;
1058 let select = selectable && modifiers.take_shift();
1059 if modifiers.is_empty() && (editable || select) {
1060 args.propagation().stop();
1061
1062 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1063
1064 if select {
1065 TextSelectOp::select_line_down()
1066 } else {
1067 TextSelectOp::line_down()
1068 }
1069 .call();
1070 }
1071 }
1072 }
1073 Key::PageUp => {
1074 if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1075 let mut modifiers = args.modifiers;
1076 let select = selectable && modifiers.take_shift();
1077 if modifiers.is_empty() && (editable || select) {
1078 args.propagation().stop();
1079
1080 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1081
1082 if select {
1083 TextSelectOp::select_page_up()
1084 } else {
1085 TextSelectOp::page_up()
1086 }
1087 .call();
1088 }
1089 }
1090 }
1091 Key::PageDown => {
1092 if ACCEPTS_ENTER_VAR.get() || TEXT.laidout().shaped_text.lines_len() > 1 {
1093 let mut modifiers = args.modifiers;
1094 let select = selectable && modifiers.take_shift();
1095 if modifiers.is_empty() && (editable || select) {
1096 args.propagation().stop();
1097
1098 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1099
1100 if select {
1101 TextSelectOp::select_page_down()
1102 } else {
1103 TextSelectOp::page_down()
1104 }
1105 .call();
1106 }
1107 }
1108 }
1109 Key::Home => {
1110 let mut modifiers = args.modifiers;
1111 let select = selectable && modifiers.take_shift();
1112 let full_text = modifiers.take_ctrl();
1113 if modifiers.is_empty() && (editable || select) {
1114 args.propagation().stop();
1115
1116 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1117
1118 if select {
1119 if full_text {
1120 TextSelectOp::select_text_start()
1121 } else {
1122 TextSelectOp::select_line_start()
1123 }
1124 } else if full_text {
1125 TextSelectOp::text_start()
1126 } else {
1127 TextSelectOp::line_start()
1128 }
1129 .call();
1130 }
1131 }
1132 Key::End => {
1133 let mut modifiers = args.modifiers;
1134 let select = selectable && modifiers.take_shift();
1135 let full_text = modifiers.take_ctrl();
1136 if modifiers.is_empty() && (editable || select) {
1137 args.propagation().stop();
1138
1139 TEXT.resolve().selection_by = SelectionBy::Keyboard;
1140
1141 if select {
1142 if full_text {
1143 TextSelectOp::select_text_end()
1144 } else {
1145 TextSelectOp::select_line_end()
1146 }
1147 } else if full_text {
1148 TextSelectOp::text_end()
1149 } else {
1150 TextSelectOp::line_end()
1151 }
1152 .call();
1153 }
1154 }
1155 _ => {}
1156 }
1157 }
1158 } else if let Some(args) = MOUSE_INPUT_EVENT.on_unhandled(update) {
1159 if args.is_primary() && args.is_mouse_down() && args.target.widget_id() == widget.id() {
1160 let mut modifiers = args.modifiers;
1161 let select = selectable && modifiers.take_shift();
1162
1163 if modifiers.is_empty() {
1164 args.propagation().stop();
1165
1166 TEXT.resolve().selection_by = SelectionBy::Mouse;
1167
1168 edit.click_count = if let Some(info) = &mut edit.selection_mouse_down {
1169 let cfg = MOUSE.multi_click_config().get();
1170
1171 let double_allowed = args.timestamp.duration_since(info.timestamp) <= cfg.time && {
1172 let dist = (info.position.to_vector() - args.position.to_vector()).abs();
1173 let area = cfg.area;
1174 dist.x <= area.width && dist.y <= area.height
1175 };
1176
1177 if double_allowed {
1178 info.timestamp = args.timestamp;
1179 info.count += 1;
1180 info.count = info.count.min(4);
1181 } else {
1182 *info = SelectionMouseDown {
1183 position: args.position,
1184 timestamp: args.timestamp,
1185 count: 1,
1186 };
1187 }
1188
1189 info.count
1190 } else {
1191 edit.selection_mouse_down = Some(SelectionMouseDown {
1192 position: args.position,
1193 timestamp: args.timestamp,
1194 count: 1,
1195 });
1196 1
1197 };
1198
1199 match edit.click_count {
1200 1 => {
1201 if select {
1202 TextSelectOp::select_nearest_to(args.position).call()
1203 } else {
1204 TextSelectOp::nearest_to(args.position).call();
1205
1206 edit.auto_select = selectable
1208 && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1209 && !FOCUS.is_focused(widget.id()).get()
1210 && TEXT.resolved().caret.selection_range().is_none();
1211 }
1212 }
1213 2 => {
1214 if selectable {
1215 TextSelectOp::select_word_nearest_to(!select, args.position).call()
1216 }
1217 }
1218 3 => {
1219 if selectable {
1220 TextSelectOp::select_line_nearest_to(!select, args.position).call()
1221 }
1222 }
1223 4 => {
1224 if selectable {
1225 TextSelectOp::select_all().call()
1226 }
1227 }
1228 _ => unreachable!(),
1229 };
1230 if selectable {
1231 let id = widget.id();
1232 edit.selection_move_handles.push(MOUSE_MOVE_EVENT.subscribe(id));
1233 edit.selection_move_handles.push(POINTER_CAPTURE_EVENT.subscribe(id));
1234 POINTER_CAPTURE.capture_widget(id);
1235 }
1236 }
1237 } else {
1238 if mem::take(&mut edit.auto_select)
1239 && selectable
1240 && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1241 && args.is_primary()
1242 && args.is_mouse_up()
1243 && FOCUS.is_focused(widget.id()).get()
1244 && TEXT.resolved().caret.selection_range().is_none()
1245 {
1246 TextSelectOp::select_all().call()
1247 }
1248 edit.selection_move_handles.clear();
1249 }
1250 } else if let Some(args) = TOUCH_INPUT_EVENT.on_unhandled(update) {
1251 if args.modifiers.is_empty() && args.target.widget_id() == widget.id() {
1252 edit.auto_select = selectable
1253 && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1254 && args.modifiers.is_empty()
1255 && args.is_touch_start()
1256 && !FOCUS.is_focused(widget.id()).get()
1257 && TEXT.resolved().caret.selection_range().is_none();
1258 }
1259 } else if let Some(args) = TOUCH_TAP_EVENT.on_unhandled(update) {
1260 if args.modifiers.is_empty() && args.target.widget_id() == widget.id() {
1261 args.propagation().stop();
1262
1263 TEXT.resolve().selection_by = SelectionBy::Touch;
1264
1265 TextSelectOp::nearest_to(args.position).call();
1266
1267 if mem::take(&mut edit.auto_select)
1268 && selectable
1269 && AUTO_SELECTION_VAR.get().contains(AutoSelection::ALL_ON_FOCUS_POINTER)
1270 && FOCUS.is_focused(WIDGET.id()).get()
1271 && TEXT.resolved().caret.selection_range().is_none()
1272 {
1273 TextSelectOp::select_all().call()
1274 }
1275 }
1276 } else if let Some(args) = TOUCH_LONG_PRESS_EVENT.on_unhandled(update) {
1277 if args.modifiers.is_empty() && selectable && args.target.widget_id() == widget.id() {
1278 args.propagation().stop();
1279
1280 TEXT.resolve().selection_by = SelectionBy::Touch;
1281
1282 TextSelectOp::select_word_nearest_to(true, args.position).call();
1283 }
1284 } else if let Some(args) = MOUSE_MOVE_EVENT.on(update) {
1285 if !edit.selection_move_handles.is_dummy() && selectable && args.target.widget_id() == widget.id() {
1286 args.propagation().stop();
1287
1288 match edit.click_count {
1289 1 => TextSelectOp::select_nearest_to(args.position).call(),
1290 2 => TextSelectOp::select_word_nearest_to(false, args.position).call(),
1291 3 => TextSelectOp::select_line_nearest_to(false, args.position).call(),
1292 4 => {}
1293 _ => unreachable!(),
1294 }
1295 }
1296 } else if let Some(args) = POINTER_CAPTURE_EVENT.on(update) {
1297 if args.is_lost(widget.id()) {
1298 edit.selection_move_handles.clear();
1299 edit.auto_select = false;
1300 }
1301 } else if selectable {
1302 if let Some(args) = SELECT_CMD.scoped(widget.id()).on_unhandled(update) {
1303 if let Some(op) = args.param::<TextSelectOp>() {
1304 args.propagation().stop();
1305
1306 op.clone().call();
1307 }
1308 } else if let Some(args) = SELECT_ALL_CMD.scoped(widget.id()).on_unhandled(update) {
1309 args.propagation().stop();
1310 TextSelectOp::select_all().call();
1311 }
1312 }
1313
1314 let mut resolve = TEXT.resolve();
1315 let caret = &mut resolve.caret;
1316 if (caret.index, caret.index_version, caret.selection_index) != prev_caret_index {
1317 if !editable || caret.index.is_none() || !FOCUS.is_focused(widget.id()).get() {
1318 edit.caret_animation = VarHandle::dummy();
1319 caret.opacity = var(0.fct()).read_only();
1320 } else {
1321 caret.opacity = KEYBOARD.caret_animation();
1322 edit.caret_animation = caret.opacity.subscribe(UpdateOp::RenderUpdate, widget.id());
1323 }
1324 resolve.pending_layout |= PendingLayout::CARET;
1325 WIDGET.layout(); }
1327}