1use {
2 crate::{
3 makepad_derive_widget::*,
4 makepad_draw::{
5 text::{
6 geom::Point,
7 selection::{
8 Cursor,
9 CursorPosition,
10 Selection
11 },
12 layouter::LaidoutText,
13 },
14 *
15 },
16 widget::*,
17 },
18 std::rc::Rc,
19 unicode_segmentation::{GraphemeCursor, UnicodeSegmentation},
20};
21
22
23live_design! {
24 link widgets;
25
26 use link::theme::*;
27 use makepad_draw::shader::std::*;
28
29 pub TextInputBase = {{TextInput}} {}
30
31 pub TextInput = <TextInputBase> {
32 width: Fill, height: Fit,
33 padding: <THEME_MSPACE_1> { left: (THEME_SPACE_2), right: (THEME_SPACE_2) }
34 margin: <THEME_MSPACE_V_1> {}
35 flow: RightWrap,
36 is_password: false,
37 is_read_only: false,
38 is_numeric_only: false
39 empty_text: "Your text here",
40
41 draw_bg: {
42 instance hover: 0.0
43 instance focus: 0.0
44 instance down: 0.0
45 instance empty: 0.0
46 instance disabled: 0.0
47
48 uniform border_radius: (THEME_CORNER_RADIUS)
49 uniform border_size: (THEME_BEVELING)
50
51 uniform color_dither: 1.0
52
53 color: (THEME_COLOR_INSET)
54 uniform color_hover: (THEME_COLOR_INSET_HOVER)
55 uniform color_focus: (THEME_COLOR_INSET_FOCUS)
56 uniform color_down: (THEME_COLOR_INSET_DOWN)
57 uniform color_empty: (THEME_COLOR_INSET_EMPTY)
58 uniform color_disabled: (THEME_COLOR_INSET_DISABLED)
59
60 uniform border_color_1: (THEME_COLOR_BEVEL_INSET_2)
61 uniform border_color_1_hover: (THEME_COLOR_BEVEL_INSET_2_HOVER)
62 uniform border_color_1_focus: (THEME_COLOR_BEVEL_INSET_2_FOCUS)
63 uniform border_color_1_down: (THEME_COLOR_BEVEL_INSET_2_DOWN)
64 uniform border_color_1_empty: (THEME_COLOR_BEVEL_INSET_2_EMPTY)
65 uniform border_color_1_disabled: (THEME_COLOR_BEVEL_INSET_2_DISABLED)
66
67 uniform border_color_2: (THEME_COLOR_BEVEL_INSET_1)
68 uniform border_color_2_hover: (THEME_COLOR_BEVEL_INSET_1_HOVER)
69 uniform border_color_2_focus: (THEME_COLOR_BEVEL_INSET_1_FOCUS)
70 uniform border_color_2_down: (THEME_COLOR_BEVEL_INSET_1_DOWN)
71 uniform border_color_2_empty: (THEME_COLOR_BEVEL_INSET_1_EMPTY)
72 uniform border_color_2_disabled: (THEME_COLOR_BEVEL_INSET_1_DISABLED)
73
74 fn pixel(self) -> vec4 {
75 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
76 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
77
78 let border_sz_uv = vec2(
79 self.border_size / self.rect_size.x,
80 self.border_size / self.rect_size.y
81 )
82
83 let scale_factor_border = vec2(
84 self.rect_size.x / self.rect_size.x,
85 self.rect_size.y / self.rect_size.y
86 );
87
88 let gradient_border = vec2(
89 self.pos.x * scale_factor_border.x + dither,
90 self.pos.y * scale_factor_border.y + dither
91 )
92
93 let sz_inner_px = vec2(
94 self.rect_size.x - self.border_size * 2.,
95 self.rect_size.y - self.border_size * 2.
96 );
97
98 let scale_factor_fill = vec2(
99 self.rect_size.x / sz_inner_px.x,
100 self.rect_size.y / sz_inner_px.y
101 );
102
103 let gradient_fill = vec2(
104 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
105 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
106 )
107
108 sdf.box(
109 self.border_size,
110 self.border_size,
111 self.rect_size.x - self.border_size * 2.,
112 self.rect_size.y - self.border_size * 2.,
113 self.border_radius
114 )
115
116 sdf.fill_keep(
117 mix(
118 mix(
119 mix(
120 mix(
121 self.color,
122 self.color_empty,
123 self.empty
124 ),
125 self.color_focus,
126 self.focus
127 ),
128 mix(
129 self.color_hover,
130 self.color_down,
131 self.down
132 ),
133 self.hover
134 ),
135 self.color_disabled,
136 self.disabled
137 )
138 );
139
140 sdf.stroke(
141 mix(
142 mix(
143 mix(
144 mix(
145 mix(self.border_color_1, self.border_color_2, gradient_border.y),
146 mix(self.border_color_1_empty, self.border_color_2_empty, gradient_border.y),
147 self.empty
148 ),
149 mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
150 self.focus
151 ),
152 mix(
153 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
154 mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
155 self.down
156 ),
157 self.hover
158 ),
159 mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
160 self.disabled
161 ),
162 self.border_size
163 );
164
165
166 return sdf.result;
167 }
168 }
169
170 draw_text: {
171 instance hover: 0.0
172 instance focus: 0.0
173 instance down: 0.0
174 instance empty: 0.0
175 instance disabled: 0.0
176
177 color: (THEME_COLOR_TEXT)
178 uniform color_hover: (THEME_COLOR_TEXT_HOVER)
179 uniform color_focus: (THEME_COLOR_TEXT_FOCUS)
180 uniform color_down: (THEME_COLOR_TEXT_DOWN)
181 uniform color_disabled: (THEME_COLOR_TEXT_DISABLED)
182 uniform color_empty: (THEME_COLOR_TEXT_PLACEHOLDER)
183 uniform color_empty_hover: (THEME_COLOR_TEXT_PLACEHOLDER_HOVER)
184 uniform color_empty_focus: (THEME_COLOR_TEXT_FOCUS)
185
186 text_style: <THEME_FONT_REGULAR> {
187 line_spacing: (THEME_FONT_WDGT_LINE_SPACING),
188 font_size: (THEME_FONT_SIZE_P)
189 }
190
191 fn get_color(self) -> vec4 {
192 return
193 mix(
194 mix(
195 mix(
196 mix(
197 self.color,
198 mix(
199 self.color_hover,
200 self.color_down,
201 self.down
202 ),
203 self.hover
204 ),
205 self.color_empty,
206 self.empty
207 ),
208 self.color_focus,
209 self.focus
210 ),
211 self.color_disabled,
212 self.disabled
213 )
214 }
215 }
216
217 draw_selection: {
218 instance hover: 0.0
219 instance focus: 0.0
220 instance down: 0.0
221 instance empty: 0.0
222 instance disabled: 0.0
223
224 uniform border_radius: (THEME_TEXTSELECTION_CORNER_RADIUS)
225
226 uniform color: (THEME_COLOR_SELECTION)
227 uniform color_hover: (THEME_COLOR_SELECTION_HOVER)
228 uniform color_focus: (THEME_COLOR_SELECTION_FOCUS)
229 uniform color_down: (THEME_COLOR_SELECTION_DOWN)
230 uniform color_empty: (THEME_COLOR_SELECTION_EMPTY)
231 uniform color_disabled: (THEME_COLOR_SELECTION_DISABLED)
232
233 fn pixel(self) -> vec4 {
234 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
235 sdf.box(
236 0.0,
237 0.0,
238 self.rect_size.x,
239 self.rect_size.y,
240 self.border_radius
241 );
242 sdf.fill(
243 mix(
244 mix(
245 mix(
246 mix(
247 self.color,
248 self.color_empty,
249 self.empty
250 ),
251 self.color_focus,
252 self.focus
253 ),
254 mix(
255 self.color_hover,
256 self.color_down,
257 self.down
258 ),
259 self.hover
260 ),
261 self.color_disabled,
262 self.disabled
263 )
264 );
265 return sdf.result;
266 }
267 }
268
269 draw_cursor: {
270 instance focus: 0.0
271 instance down: 0.0
272 instance empty: 0.0
273 instance disabled: 0.0
274 instance blink: 0.0
275
276 uniform border_radius: 0.5
277
278 uniform color: (THEME_COLOR_TEXT_CURSOR)
279
280 fn pixel(self) -> vec4 {
281 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
282 sdf.box(
283 0.0,
284 0.0,
285 self.rect_size.x,
286 self.rect_size.y,
287 self.border_radius
288 );
289 sdf.fill(
290 mix(THEME_COLOR_U_HIDDEN, self.color, (1.0-self.blink) * self.focus)
291 );
292 return sdf.result;
293 }
294 }
295
296 animator: {
297 empty = {
298 default: off,
299 off = {
300 from: {all: Forward {duration: 0.}}
301 apply: {
302 draw_bg: {empty: 0.0}
303 draw_text: {empty: 0.0}
304 draw_selection: {empty: 0.0}
305 draw_cursor: {empty: 0.0}
306 }
307 }
308 on = {
309 from: {all: Forward {duration: 0.2}}
310 apply: {
311 draw_bg: {empty: 1.0}
312 draw_text: {empty: 1.0}
313 draw_selection: {empty: 1.0}
314 draw_cursor: {empty: 1.0}
315 }
316 }
317 }
318 blink = {
319 default: off
320 off = {
321 from: {all: Forward {duration:0.05}}
322 apply: {
323 draw_cursor: {blink:0.0}
324 }
325 }
326 on = {
327 from: {all: Forward {duration: 0.05}}
328 apply: {
329 draw_cursor: {blink:1.0}
330 }
331 }
332 }
333 hover = {
334 default: off,
335 off = {
336 from: {all: Forward {duration: 0.1}}
337 apply: {
338 draw_bg: {down: 0.0, hover: 0.0}
339 draw_text: {down: 0.0, hover: 0.0}
340 }
341 }
342
343 on = {
344 from: {
345 all: Forward {duration: 0.1}
346 down: Forward {duration: 0.01}
347 }
348 apply: {
349 draw_bg: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
350 draw_text: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
351 }
352 }
353
354 down = {
355 from: {all: Forward {duration: 0.2}}
356 apply: {
357 draw_bg: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
358 draw_text: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
359 }
360 }
361 }
362 disabled = {
363 default: off,
364 off = {
365 from: {all: Forward {duration: 0.}}
366 apply: {
367 draw_bg: {disabled: 0.0}
368 draw_text: {disabled: 0.0}
369 draw_selection: {disabled: 0.0}
370 draw_cursor: {disabled: 0.0}
371 }
372 }
373 on = {
374 from: {all: Forward {duration: 0.2}}
375 apply: {
376 draw_bg: {disabled: 1.0}
377 draw_text: {disabled: 1.0}
378 draw_selection: {disabled: 1.0}
379 draw_cursor: {disabled: 1.0}
380 }
381 }
382 }
383 hover = {
384 default: off,
385 off = {
386 from: {all: Forward {duration: 0.1}}
387 apply: {
388 draw_bg: {down: 0.0, hover: 0.0}
389 draw_text: {down: 0.0, hover: 0.0}
390 }
391 }
392
393 on = {
394 from: {
395 all: Forward {duration: 0.1}
396 down: Forward {duration: 0.01}
397 }
398 apply: {
399 draw_bg: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
400 draw_text: {down: 0.0, hover: [{time: 0.0, value: 1.0}],}
401 }
402 }
403
404 down = {
405 from: {all: Forward {duration: 0.2}}
406 apply: {
407 draw_bg: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
408 draw_text: {down: [{time: 0.0, value: 1.0}], hover: 1.0,}
409 }
410 }
411 }
412 focus = {
413 default: off
414 off = {
415 from: {
416 all: Forward { duration: 0.25 }
417 }
418 apply: {
419 draw_bg: { focus: 0.0 }
420 draw_text: { focus: 0.0 },
421 draw_cursor: { focus: 0.0 },
422 draw_selection: { focus: 0.0 }
423 }
424 }
425 on = {
426 from: { all: Snap }
427 apply: {
428 draw_bg: { focus: 1.0 }
429 draw_text: { focus: 1.0 }
430 draw_cursor: { focus: 1.0 },
431 draw_selection: { focus: 1.0 }
432 }
433 }
434 }
435 }
436 }
437
438 pub TextInputFlat = <TextInput> {
439 draw_bg: {
440 border_color_1: (THEME_COLOR_BEVEL)
441 border_color_1_hover: (THEME_COLOR_BEVEL_HOVER)
442 border_color_1_focus: (THEME_COLOR_BEVEL_FOCUS)
443 border_color_1_down: (THEME_COLOR_BEVEL_DOWN)
444 border_color_1_empty: (THEME_COLOR_BEVEL_EMPTY)
445 border_color_1_disabled: (THEME_COLOR_BEVEL_DISABLED)
446
447 border_color_2: (THEME_COLOR_BEVEL)
448 border_color_2_hover: (THEME_COLOR_BEVEL_HOVER)
449 border_color_2_focus: (THEME_COLOR_BEVEL_FOCUS)
450 border_color_2_down: (THEME_COLOR_BEVEL_DOWN)
451 border_color_2_empty: (THEME_COLOR_BEVEL_EMPTY)
452 border_color_2_disabled: (THEME_COLOR_BEVEL_DISABLED)
453 }
454 }
455
456 pub TextInputFlatter = <TextInputFlat> { draw_bg: { border_size: 0. } }
457
458 pub TextInputGradientX = <TextInput> {
459 draw_bg: {
460 instance hover: 0.0
461 instance focus: 0.0
462 instance down: 0.0
463 instance disabled: 0.0
464 instance empty: 0.0
465
466 uniform border_radius: (THEME_CORNER_RADIUS)
467 uniform border_size: (THEME_BEVELING)
468
469 uniform color_dither: 1.0
470
471 uniform color_1: (THEME_COLOR_INSET_1)
472 uniform color_1_hover: (THEME_COLOR_INSET_1_HOVER)
473 uniform color_1_focus: (THEME_COLOR_INSET_1_FOCUS)
474 uniform color_1_down: (THEME_COLOR_INSET_1_DOWN)
475 uniform color_1_empty: (THEME_COLOR_INSET_1_EMPTY)
476 uniform color_1_disabled: (THEME_COLOR_INSET_1_DISABLED)
477
478 uniform color_2: (THEME_COLOR_INSET_2)
479 uniform color_2_hover: (THEME_COLOR_INSET_2_HOVER)
480 uniform color_2_focus: (THEME_COLOR_INSET_2_FOCUS)
481 uniform color_2_down: (THEME_COLOR_INSET_2_DOWN)
482 uniform color_2_empty: (THEME_COLOR_INSET_2_EMPTY)
483 uniform color_2_disabled: (THEME_COLOR_INSET_2_DISABLED)
484
485 uniform border_color_1: (THEME_COLOR_BEVEL_INSET_2)
486 uniform border_color_1_hover: (THEME_COLOR_BEVEL_INSET_2_HOVER)
487 uniform border_color_1_focus: (THEME_COLOR_BEVEL_INSET_2_FOCUS)
488 uniform border_color_1_down: (THEME_COLOR_BEVEL_INSET_2_DOWN)
489 uniform border_color_1_empty: (THEME_COLOR_BEVEL_INSET_2_EMPTY)
490 uniform border_color_1_disabled: (THEME_COLOR_BEVEL_INSET_2_DISABLED)
491
492 uniform border_color_2: (THEME_COLOR_BEVEL_INSET_1)
493 uniform border_color_2_hover: (THEME_COLOR_BEVEL_INSET_1_HOVER)
494 uniform border_color_2_focus: (THEME_COLOR_BEVEL_INSET_1_FOCUS)
495 uniform border_color_2_down: (THEME_COLOR_BEVEL_INSET_1_DOWN)
496 uniform border_color_2_empty: (THEME_COLOR_BEVEL_INSET_1_EMPTY)
497 uniform border_color_2_disabled: (THEME_COLOR_BEVEL_INSET_1_DISABLED)
498
499 fn pixel(self) -> vec4 {
500 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
501 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
502
503 let border_sz_uv = vec2(
504 self.border_size / self.rect_size.x,
505 self.border_size / self.rect_size.y
506 )
507
508 let scale_factor_border = vec2(
509 self.rect_size.x / self.rect_size.x,
510 self.rect_size.y / self.rect_size.y
511 );
512
513 let gradient_border = vec2(
514 self.pos.x * scale_factor_border.x + dither,
515 self.pos.y * scale_factor_border.y + dither
516 )
517
518 let sz_inner_px = vec2(
519 self.rect_size.x - self.border_size * 2.,
520 self.rect_size.y - self.border_size * 2.
521 );
522
523 let scale_factor_fill = vec2(
524 self.rect_size.x / sz_inner_px.x,
525 self.rect_size.y / sz_inner_px.y
526 );
527
528 let gradient_fill = vec2(
529 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
530 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
531 )
532
533 sdf.box(
534 self.border_size,
535 self.border_size,
536 self.rect_size.x - self.border_size * 2.,
537 self.rect_size.y - self.border_size * 2.,
538 self.border_radius
539 )
540
541 sdf.fill_keep(
542 mix(
543 mix(
544 mix(
545 mix(
546 mix(self.color_1, self.color_2, gradient_fill.x),
547 mix(self.color_1_empty, self.color_2_empty, gradient_fill.x),
548 self.empty
549 ),
550 mix(self.color_1_focus, self.color_2_focus, gradient_fill.x),
551 self.focus
552 ),
553 mix(
554 mix(self.color_1_hover, self.color_2_hover, gradient_fill.x),
555 mix(self.color_1_down, self.color_2_down, gradient_fill.x),
556 self.down
557 ),
558 self.hover
559 ),
560 mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.x),
561 self.disabled
562 )
563 );
564
565 sdf.stroke(
566 mix(
567 mix(
568 mix(
569 mix(
570 mix(self.border_color_1, self.border_color_2, gradient_border.y),
571 mix(self.border_color_1_empty, self.border_color_2_empty, gradient_border.y),
572 self.empty
573 ),
574 mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
575 self.focus
576 ),
577 mix(
578 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
579 mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
580 self.down
581 ),
582 self.hover
583 ),
584 mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
585 self.disabled
586 ),
587 self.border_size
588 );
589
590 return sdf.result
591 }
592 }
593
594 draw_selection: {
595 instance hover: 0.0
596 instance focus: 0.0
597 instance down: 0.0
598 instance disabled: 0.0
599 instance empty: 0.0
600
601 uniform border_radius: (THEME_TEXTSELECTION_CORNER_RADIUS)
602
603 uniform color_1: (THEME_COLOR_SELECTION)
604 uniform color_1_hover: (THEME_COLOR_SELECTION_HOVER)
605 uniform color_1_focus: (THEME_COLOR_SELECTION_FOCUS)
606 uniform color_1_down: (THEME_COLOR_SELECTION_DOWN)
607 uniform color_1_empty: (THEME_COLOR_SELECTION_EMPTY)
608 uniform color_1_disabled: (THEME_COLOR_SELECTION_DISABLED)
609
610 uniform color_2: (THEME_COLOR_SELECTION)
611 uniform color_2_hover: (THEME_COLOR_SELECTION_HOVER)
612 uniform color_2_focus: (THEME_COLOR_SELECTION_FOCUS)
613 uniform color_2_down: (THEME_COLOR_SELECTION_DOWN)
614 uniform color_2_empty: (THEME_COLOR_SELECTION_EMPTY)
615 uniform color_2_disabled: (THEME_COLOR_SELECTION_DISABLED)
616
617 fn pixel(self) -> vec4 {
618 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
619
620 sdf.box(
621 0.0,
622 0.0,
623 self.rect_size.x,
624 self.rect_size.y,
625 self.border_radius
626 )
627
628 sdf.fill(
629 mix(
630 mix(
631 mix(
632 mix(
633 mix(self.color_1, self.color_2, self.pos.x),
634 mix(self.color_1_empty, self.color_2_empty, self.pos.x),
635 self.empty
636 ),
637 mix(self.color_1_focus, self.color_2_focus, self.pos.x),
638 self.focus
639 ),
640 mix(
641 mix(self.color_1_hover, self.color_2_hover, self.pos.x),
642 mix(self.color_1_down, self.color_2_down, self.pos.x),
643 self.down
644 ),
645 self.hover
646 ),
647 mix(self.color_1_disabled, self.color_2_disabled, self.pos.x),
648 self.disabled
649 )
650 );
651
652 return sdf.result
653 }
654 }
655 }
656
657
658 pub TextInputGradientY = <TextInputGradientX> {
659 draw_bg: {
660 fn pixel(self) -> vec4 {
661 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
662 let dither = Math::random_2d(self.pos.xy) * 0.04 * self.color_dither;
663
664 let border_sz_uv = vec2(
665 self.border_size / self.rect_size.x,
666 self.border_size / self.rect_size.y
667 )
668
669 let scale_factor_border = vec2(
670 self.rect_size.x / self.rect_size.x,
671 self.rect_size.y / self.rect_size.y
672 );
673
674 let gradient_border = vec2(
675 self.pos.x * scale_factor_border.x + dither,
676 self.pos.y * scale_factor_border.y + dither
677 )
678
679 let sz_inner_px = vec2(
680 self.rect_size.x - self.border_size * 2.,
681 self.rect_size.y - self.border_size * 2.
682 );
683
684 let scale_factor_fill = vec2(
685 self.rect_size.x / sz_inner_px.x,
686 self.rect_size.y / sz_inner_px.y
687 );
688
689 let gradient_fill = vec2(
690 self.pos.x * scale_factor_fill.x - border_sz_uv.x * 2. + dither,
691 self.pos.y * scale_factor_fill.y - border_sz_uv.y * 2. + dither
692 )
693
694 sdf.box(
695 self.border_size,
696 self.border_size,
697 self.rect_size.x - self.border_size * 2.,
698 self.rect_size.y - self.border_size * 2.,
699 self.border_radius
700 )
701
702 sdf.fill_keep(
703 mix(
704 mix(
705 mix(
706 mix(
707 mix(self.color_1, self.color_2, gradient_fill.y),
708 mix(self.color_1_empty, self.color_2_empty, gradient_fill.y),
709 self.empty
710 ),
711 mix(self.color_1_focus, self.color_2_focus, gradient_fill.y),
712 self.focus
713 ),
714 mix(
715 mix(self.color_1_hover, self.color_2_hover, gradient_fill.y),
716 mix(self.color_1_down, self.color_2_down, gradient_fill.y),
717 self.down
718 ),
719 self.hover
720 ),
721 mix(self.color_1_disabled, self.color_2_disabled, gradient_fill.y),
722 self.disabled
723 )
724 );
725
726 sdf.stroke(
727 mix(
728 mix(
729 mix(
730 mix(
731 mix(self.border_color_1, self.border_color_2, gradient_border.y),
732 mix(self.border_color_1_empty, self.border_color_2_empty, gradient_border.y),
733 self.empty
734 ),
735 mix(self.border_color_1_focus, self.border_color_2_focus, gradient_border.y),
736 self.focus
737 ),
738 mix(
739 mix(self.border_color_1_hover, self.border_color_2_hover, gradient_border.y),
740 mix(self.border_color_1_down, self.border_color_2_down, gradient_border.y),
741 self.down
742 ),
743 self.hover
744 ),
745 mix(self.border_color_1_disabled, self.border_color_2_disabled, gradient_border.y),
746 self.disabled
747 ),
748 self.border_size
749 );
750
751 return sdf.result
752 }
753 }
754
755 draw_selection: {
756 fn pixel(self) -> vec4 {
757 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
758
759 sdf.box(
760 0.0,
761 0.0,
762 self.rect_size.x,
763 self.rect_size.y,
764 self.border_radius
765 )
766
767 sdf.fill(
768 mix(
769 mix(
770 mix(
771 mix(
772 mix(self.color_1, self.color_2, self.pos.y),
773 mix(self.color_1_empty, self.color_2_empty, self.pos.y),
774 self.empty
775 ),
776 mix(self.color_1_focus, self.color_2_focus, self.pos.y),
777 self.focus
778 ),
779 mix(
780 mix(self.color_1_hover, self.color_2_hover, self.pos.y),
781 mix(self.color_1_down, self.color_2_down, self.pos.y),
782 self.down
783 ),
784 self.hover
785 ),
786 mix(self.color_1_disabled, self.color_2_disabled, self.pos.y),
787 self.disabled
788 )
789 );
790
791 return sdf.result
792 }
793 }
794 }
795}
796
797#[derive(Live, Widget)]
798pub struct TextInput {
799 #[animator] animator: Animator,
800
801 #[redraw] #[live] draw_bg: DrawColor,
802 #[live] draw_text: DrawText,
803 #[live] draw_selection: DrawQuad,
804 #[live] draw_cursor: DrawQuad,
805
806 #[layout] layout: Layout,
807 #[walk] walk: Walk,
808 #[live] label_align: Align,
809
810 #[live] is_password: bool,
811 #[live] is_read_only: bool,
812 #[live] is_numeric_only: bool,
813 #[live] empty_text: String,
814 #[rust] text: String,
815 #[live(0.5)] blink_speed: f64,
816
817 #[rust] password_text: String,
818 #[rust] laidout_text: Option<Rc<LaidoutText>>,
819 #[rust] text_area: Area,
820 #[rust] selection: Selection,
821 #[rust] history: History,
822 #[rust] blink_timer: Timer,
823}
824
825 impl LiveHook for TextInput{
826 fn apply_value_unknown(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) -> usize {
827 if nodes[index].id == live_id!(text){
828 if !apply.from.is_update_from_doc(){
829 return self.text.apply(cx, apply, index, nodes)
830 }
831 }
832 else{
833 cx.apply_error_no_matching_field(live_error_origin!(), index, nodes);
834 }
835 nodes.skip_node(index)
836 }
837 fn after_new_from_doc(&mut self, cx:&mut Cx){
838 self.check_text_is_empty(cx);
839 }
840 }
841
842impl TextInput {
843 pub fn is_password(&self) -> bool {
844 self.is_password
845 }
846
847 pub fn set_is_password(&mut self, cx: &mut Cx, is_password: bool) {
848 self.is_password = is_password;
849 self.laidout_text = None;
850 self.draw_bg.redraw(cx);
851 }
852
853 pub fn toggle_is_password(&mut self, cx: &mut Cx) {
854 self.set_is_password(cx, !self.is_password);
855 }
856
857 pub fn is_read_only(&self) -> bool {
858 self.is_read_only
859 }
860
861 pub fn set_is_read_only(&mut self, cx: &mut Cx, is_read_only: bool) {
862 self.is_read_only = is_read_only;
863 self.laidout_text = None;
864 self.draw_bg.redraw(cx);
865 }
866
867 pub fn toggle_is_read_only(&mut self, cx: &mut Cx) {
868 self.set_is_read_only(cx, !self.is_read_only);
869 }
870
871 pub fn is_numeric_only(&self) -> bool {
872 self.is_numeric_only
873 }
874
875 pub fn set_is_numeric_only(&mut self, cx: &mut Cx, is_numeric_only: bool) {
876 self.is_numeric_only = is_numeric_only;
877 self.laidout_text = None;
878 self.draw_bg.redraw(cx);
879 }
880
881 pub fn toggle_is_numeric_only(&mut self, cx: &mut Cx) {
882 self.set_is_numeric_only(cx, !self.is_numeric_only);
883 }
884
885 pub fn empty_text(&self) -> &str {
886 &self.empty_text
887 }
888
889 pub fn set_empty_text(&mut self, cx: &mut Cx, empty_text: String) {
890 self.empty_text = empty_text;
891 if self.text.is_empty() {
892 self.draw_bg.redraw(cx);
893 }
894 }
895
896
897 pub fn selection(&self) -> Selection {
898 self.selection
899 }
900
901 pub fn set_selection(&mut self, cx: &mut Cx, selection: Selection) {
902 self.selection = selection;
903 self.history.force_new_edit_group();
904 self.draw_bg.redraw(cx);
905 }
906
907 pub fn cursor(&self) -> Cursor {
908 self.selection.cursor
909 }
910
911 pub fn set_cursor(&mut self, cx: &mut Cx, cursor: Cursor, keep_selection: bool) {
912 self.set_selection(
913 cx,
914 Selection {
915 anchor: if keep_selection {
916 self.selection.anchor
917 } else {
918 cursor
919 },
920 cursor
921 }
922 );
923 }
924
925 pub fn selected_text(&self) -> &str {
926 &self.text[self.selection.start().index..self.selection.end().index]
927 }
928
929 pub fn reset_blink_timer(&mut self, cx: &mut Cx) {
930 self.animator_cut(cx, id!(blink.off));
931 if !self.is_read_only {
932 cx.stop_timer(self.blink_timer);
933 self.blink_timer = cx.start_timeout(self.blink_speed)
934 }
935 }
936
937 fn cursor_to_position(&self, cursor: Cursor) -> Result<CursorPosition, ()> {
938 let Some(laidout_text) = self.laidout_text.as_ref() else {
939 return Err(());
940 };
941 let position = laidout_text.cursor_to_position(self.cursor_to_password_cursor(cursor));
942 Ok(CursorPosition {
943 row_index: position.row_index,
944 x_in_lpxs: position.x_in_lpxs * self.draw_text.font_scale,
945 })
946 }
947
948 fn point_in_lpxs_to_cursor(&self, point_in_lpxs: Point<f32>) -> Result<Cursor, ()> {
949 let Some(laidout_text) = self.laidout_text.as_ref() else {
950 return Err(());
951 };
952 let cursor = laidout_text.point_in_lpxs_to_cursor(point_in_lpxs / self.draw_text.font_scale);
953 Ok(self.password_cursor_to_cursor(cursor))
954 }
955
956 fn position_to_cursor(&self, position: CursorPosition) -> Result<Cursor, ()> {
957 let Some(laidout_text) = self.laidout_text.as_ref() else {
958 return Err(());
959 };
960 let cursor = laidout_text.position_to_cursor(CursorPosition {
961 row_index: position.row_index,
962 x_in_lpxs: position.x_in_lpxs / self.draw_text.font_scale,
963 });
964 Ok(self.password_cursor_to_cursor(cursor))
965 }
966
967 fn selection_to_password_selection(&self, selection: Selection) -> Selection {
968 Selection {
969 cursor: self.cursor_to_password_cursor(selection.cursor),
970 anchor: self.cursor_to_password_cursor(selection.anchor),
971 }
972 }
973
974 fn cursor_to_password_cursor(&self, cursor: Cursor) -> Cursor {
975 Cursor {
976 index: self.index_to_password_index(cursor.index),
977 prefer_next_row: cursor.prefer_next_row,
978 }
979 }
980
981 fn password_cursor_to_cursor(&self, password_cursor: Cursor) -> Cursor {
982 Cursor {
983 index: self.password_index_to_index(password_cursor.index),
984 prefer_next_row: password_cursor.prefer_next_row,
985 }
986 }
987
988 fn index_to_password_index(&self, index: usize) -> usize {
989 if !self.is_password {
990 return index;
991 }
992 let grapheme_index = self.text[..index].graphemes(true).count();
993 self.password_text
994 .grapheme_indices(true)
995 .nth(grapheme_index).map_or(self.password_text.len(), |(index, _)| index)
996 }
997
998 fn password_index_to_index(&self, password_index: usize) -> usize {
999 if !self.is_password {
1000 return password_index;
1001 }
1002 let grapheme_index = self.password_text[..password_index].graphemes(true).count();
1003 self.text
1004 .grapheme_indices(true)
1005 .nth(grapheme_index).map_or(self.text.len(), |(index, _)| index)
1006 }
1007
1008 fn inner_walk(&self) -> Walk {
1009 if self.walk.width.is_fit() {
1010 Walk::fit()
1011 } else {
1012 Walk::fill_fit()
1013 }
1014 }
1015
1016 fn layout_text(&mut self, cx: &mut Cx2d) {
1017 if self.laidout_text.is_some() {
1018 return;
1019 }
1020 let text = if self.is_password {
1021 self.password_text.clear();
1022 for grapheme in self.text.graphemes(true) {
1023 self.password_text.push(if grapheme == "\n" {
1024 '\n'
1025 } else {
1026 '•'
1027 });
1028 }
1029 &self.password_text
1030 } else {
1031 &self.text
1032 };
1033 let turtle_rect = cx.turtle().padded_rect();
1034 let max_width_in_lpxs = if !turtle_rect.size.x.is_nan() {
1035 Some(turtle_rect.size.x as f32)
1036 } else {
1037 None
1038 };
1039 let wrap = cx.turtle().layout().flow == Flow::RightWrap;
1040 self.laidout_text = Some(self.draw_text.layout(
1041 cx,
1042 0.0,
1043 0.0,
1044 max_width_in_lpxs,
1045 wrap,
1046 self.label_align,
1047 text
1048 ));
1049 }
1050
1051 fn draw_text(&mut self, cx: &mut Cx2d) -> Rect {
1052 let inner_walk = self.inner_walk();
1053 let text_rect = if self.text.is_empty() {
1054 self.draw_text.draw_walk(
1055 cx,
1056 inner_walk,
1057 self.label_align,
1058 &self.empty_text
1059 )
1060 } else {
1061 let laidout_text = self.laidout_text.as_ref().unwrap();
1062 self.draw_text.draw_walk_laidout(
1063 cx,
1064 inner_walk,
1065 laidout_text,
1066 )
1067 };
1068 cx.add_aligned_rect_area(&mut self.text_area, text_rect);
1069 text_rect
1070 }
1071
1072 fn draw_cursor(&mut self, cx: &mut Cx2d, text_rect: Rect) -> DVec2 {
1073 let CursorPosition {
1074 row_index,
1075 x_in_lpxs,
1076 } = self
1077 .cursor_to_position(self.selection.cursor)
1078 .ok()
1079 .expect("layout should not be `None` because we called `layout_text` in `draw_walk`");
1080 let laidout_text = self
1081 .laidout_text
1082 .as_ref()
1083 .expect("layout should not be `None` because we called `layout_text` in `draw_walk`");
1084 let row = &laidout_text.rows[row_index];
1085 let cursor_pos = dvec2(
1086 (x_in_lpxs - 1.0 * self.draw_text.font_scale) as f64,
1087 ((row.origin_in_lpxs.y - row.ascender_in_lpxs) * self.draw_text.font_scale) as f64,
1088 );
1089 self.draw_cursor.draw_abs(
1090 cx,
1091 rect(
1092 text_rect.pos.x + cursor_pos.x,
1093 text_rect.pos.y + cursor_pos.y,
1094 (2.0 * self.draw_text.font_scale) as f64,
1095 ((row.ascender_in_lpxs - row.descender_in_lpxs) * self.draw_text.font_scale) as f64,
1096 )
1097 );
1098 cursor_pos
1099 }
1100
1101 fn draw_selection(&mut self, cx: &mut Cx2d, text_rect: Rect) {
1102 let laidout_text = self
1103 .laidout_text
1104 .as_ref()
1105 .expect("layout should not be `None` because we called `layout_text` in `draw_walk`");
1106
1107 self.draw_selection.begin_many_instances(cx);
1108 for rect_in_lpxs in laidout_text.selection_rects_in_lpxs(
1109 self.selection_to_password_selection(self.selection)
1110 ) {
1111 self.draw_selection.draw_abs(
1112 cx,
1113 rect(
1114 text_rect.pos.x + (rect_in_lpxs.origin.x * self.draw_text.font_scale) as f64,
1115 text_rect.pos.y + (rect_in_lpxs.origin.y * self.draw_text.font_scale) as f64,
1116 (rect_in_lpxs.size.width * self.draw_text.font_scale) as f64,
1117 (rect_in_lpxs.size.height * self.draw_text.font_scale) as f64,
1118 )
1119 );
1120 }
1121 self.draw_selection.end_many_instances(cx);
1122 }
1123
1124 pub fn move_cursor_left(&mut self, cx: &mut Cx, keep_selection: bool) {
1125 self.set_cursor(
1126 cx,
1127 Cursor {
1128 index: prev_grapheme_boundary(&self.text, self.selection.cursor.index),
1129 prefer_next_row: true,
1130 },
1131 keep_selection
1132 );
1133 }
1134
1135 pub fn move_cursor_right(&mut self, cx: &mut Cx, keep_selection: bool) {
1136 self.set_cursor(
1137 cx,
1138 Cursor {
1139 index: next_grapheme_boundary(&self.text, self.selection.cursor.index),
1140 prefer_next_row: false,
1141 },
1142 keep_selection,
1143 );
1144 }
1145
1146 pub fn move_cursor_up(&mut self, cx: &mut Cx, keep_selection: bool) -> Result<(), ()> {
1147 let position = self.cursor_to_position(self.selection.cursor)?;
1148 self.set_cursor(
1149 cx,
1150 self.position_to_cursor(CursorPosition {
1151 row_index: if position.row_index == 0 {
1152 0
1153 } else {
1154 position.row_index - 1
1155 },
1156 x_in_lpxs: position.x_in_lpxs,
1157 })?,
1158 keep_selection
1159 );
1160 Ok(())
1161 }
1162
1163 pub fn move_cursor_down(&mut self, cx: &mut Cx, keep_selection: bool) -> Result<(), ()> {
1164 let laidout_text = self.laidout_text.as_ref().unwrap();
1165 let position = self.cursor_to_position(self.selection.cursor)?;
1166 self.set_cursor(
1167 cx,
1168 self.position_to_cursor(CursorPosition {
1169 row_index: if position.row_index == laidout_text.rows.len() - 1 {
1170 laidout_text.rows.len() - 1
1171 } else {
1172 position.row_index + 1
1173 },
1174 x_in_lpxs: position.x_in_lpxs,
1175 })?,
1176 keep_selection
1177 );
1178 Ok(())
1179 }
1180
1181 pub fn select_all(&mut self, cx: &mut Cx) {
1182 self.set_selection(
1183 cx,
1184 Selection {
1185 anchor: Cursor { index: 0, prefer_next_row: false },
1186 cursor: Cursor { index: self.text.len(), prefer_next_row: false },
1187 }
1188 );
1189 }
1190
1191 pub fn select_word(&mut self, cx: &mut Cx) {
1192 if self.selection.cursor.index < self.selection.anchor.index {
1193 self.set_cursor(
1194 cx,
1195 Cursor {
1196 index: self.ceil_word_boundary(self.selection.cursor.index),
1197 prefer_next_row: true,
1198 },
1199 true,
1200 );
1201 } else if self.selection.cursor.index > self.selection.anchor.index {
1202 self.set_cursor(
1203 cx,
1204 Cursor {
1205 index: self.floor_word_boundary(self.selection.cursor.index),
1206 prefer_next_row: false,
1207 },
1208 true,
1209 );
1210 } else {
1211 self.set_selection(
1212 cx,
1213 Selection {
1214 anchor: Cursor {
1215 index: self.ceil_word_boundary(self.selection.cursor.index),
1216 prefer_next_row: true,
1217 },
1218 cursor: Cursor {
1219 index: self.floor_word_boundary(self.selection.cursor.index),
1220 prefer_next_row: false,
1221 }
1222 },
1223 );
1224 }
1225 }
1226
1227 pub fn force_new_edit_group(&mut self) {
1228 self.history.force_new_edit_group();
1229 }
1230
1231 fn ceil_word_boundary(&self, index: usize) -> usize {
1232 let mut prev_word_boundary_index = 0;
1233 for (word_boundary_index, _) in self.text.split_word_bound_indices() {
1234 if word_boundary_index > index {
1235 return prev_word_boundary_index;
1236 }
1237 prev_word_boundary_index = word_boundary_index;
1238 }
1239 prev_word_boundary_index
1240 }
1241
1242 fn floor_word_boundary(&self, index: usize) -> usize {
1243 let mut prev_word_boundary_index = self.text.len();
1244 for (word_boundary_index, _) in self.text.split_word_bound_indices().rev() {
1245 if word_boundary_index < index {
1246 return prev_word_boundary_index;
1247 }
1248 prev_word_boundary_index = word_boundary_index;
1249 }
1250 prev_word_boundary_index
1251 }
1252
1253 fn filter_input(&self, input: &str, is_set_text: bool) -> String {
1254 if self.is_numeric_only {
1255 let mut contains_dot = if is_set_text {
1256 false
1257 } else {
1258 let before_selection = self.text[..self.selection.start().index].to_string();
1259 let after_selection = self.text[self.selection.end().index..].to_string();
1260 before_selection.contains('.') || after_selection.contains('.')
1261 };
1262 input.chars().filter(|char| {
1263 match char {
1264 '.' | ',' if !contains_dot => {
1265 contains_dot = true;
1266 true
1267 },
1268 char => char.is_ascii_digit(),
1269 }
1270 }).collect()
1271 } else {
1272 input.to_string()
1273 }
1274 }
1275
1276 fn create_or_extend_edit_group(&mut self, edit_kind: EditKind) {
1277 self.history.create_or_extend_edit_group(edit_kind, self.selection);
1278 }
1279
1280 fn apply_edit(&mut self, cx: &mut Cx, edit: Edit) {
1281 self.selection.cursor.index = edit.start + edit.replace_with.len();
1282 self.selection.anchor.index = self.selection.cursor.index;
1283 self.history.apply_edit(edit, &mut self.text);
1284 self.laidout_text = None;
1285 self.check_text_is_empty(cx);
1286 }
1287
1288 fn undo(&mut self, cx: &mut Cx) -> bool {
1289 if let Some(new_selection) = self.history.undo(self.selection, &mut self.text) {
1290 self.laidout_text = None;
1291 self.selection = new_selection;
1292 self.check_text_is_empty(cx);
1293 true
1294 } else {
1295 false
1296 }
1297 }
1298
1299 fn redo(&mut self, cx: &mut Cx) -> bool {
1300 if let Some(new_selection) = self.history.redo(self.selection, &mut self.text) {
1301 self.laidout_text = None;
1302 self.selection = new_selection;
1303 self.check_text_is_empty(cx);
1304 true
1305 } else {
1306 false
1307 }
1308 }
1309
1310 fn check_text_is_empty(&mut self, cx: &mut Cx) {
1311 if self.text.is_empty() {
1312 self.animator_play(cx, id!(empty.on));
1313 } else {
1314 self.animator_play(cx, id!(empty.off));
1315 }
1316 }
1317
1318}
1319
1320impl Widget for TextInput {
1321
1322 fn text(&self) -> String {
1323 self.text.clone()
1324 }
1325
1326 fn set_text(&mut self, cx: &mut Cx, text: &str) {
1327 self.text = self.filter_input(text, true);
1328 self.set_selection(
1329 cx,
1330 Selection {
1331 anchor: Cursor {
1332 index: self.selection.anchor.index.min(self.text.len()),
1333 prefer_next_row: self.selection.anchor.prefer_next_row,
1334 },
1335 cursor: Cursor {
1336 index: self.selection.cursor.index.min(self.text.len()),
1337 prefer_next_row: self.selection.cursor.prefer_next_row,
1338 }
1339 }
1340 );
1341 self.history.clear();
1342 self.laidout_text = None;
1343 self.draw_bg.redraw(cx);
1344 self.check_text_is_empty(cx);
1345 }
1346
1347 fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
1348 self.draw_bg.begin(cx, walk, self.layout);
1349 self.draw_selection.append_to_draw_call(cx);
1350 self.layout_text(cx);
1351 let text_rect = self.draw_text(cx);
1352 let cursor_pos = self.draw_cursor(cx, text_rect);
1353 self.draw_selection(cx, text_rect);
1354 self.draw_bg.end(cx);
1355 if cx.has_key_focus(self.draw_bg.area()) {
1356 cx.show_text_ime(
1357 self.draw_bg.area(),
1358 cursor_pos,
1359 );
1360 }
1361 cx.add_nav_stop(self.draw_bg.area(), NavRole::TextInput, Margin::default());
1362 DrawStep::done()
1363 }
1364
1365 fn set_disabled(&mut self, cx:&mut Cx, disabled:bool){
1366 self.animator_toggle(cx, disabled, Animate::Yes, id!(disabled.on), id!(disabled.off));
1367 }
1368
1369 fn disabled(&self, cx:&Cx) -> bool {
1370 self.animator_in_state(cx, id!(disabled.on))
1371 }
1372
1373 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
1374 if self.animator_handle_event(cx, event).must_redraw() {
1375 self.draw_bg.redraw(cx);
1376 }
1377
1378 if self.blink_timer.is_event(event).is_some() {
1379 if self.animator_in_state(cx, id!(blink.off)) {
1380 self.animator_play(cx, id!(blink.on));
1381 } else {
1382 self.animator_play(cx, id!(blink.off));
1383 }
1384 self.blink_timer = cx.start_timeout(self.blink_speed)
1385 }
1386
1387 let uid = self.widget_uid();
1388 match event.hits(cx, self.draw_bg.area()) {
1389 Hit::FingerHoverIn(_) => {
1390 self.animator_play(cx, id!(hover.on));
1391 }
1392 Hit::FingerHoverOut(_) => {
1393 self.animator_play(cx, id!(hover.off));
1394 }
1395 Hit::KeyFocus(_) => {
1396 self.animator_play(cx, id!(focus.on));
1397 self.reset_blink_timer(cx);
1398 cx.widget_action(uid, &scope.path, TextInputAction::KeyFocus);
1399 },
1400 Hit::KeyFocusLost(_) => {
1401 self.animator_play(cx, id!(focus.off));
1402 self.animator_play(cx, id!(blink.on));
1403 cx.stop_timer(self.blink_timer);
1404 cx.hide_text_ime();
1405 cx.widget_action(uid, &scope.path, TextInputAction::KeyFocusLost);
1406 }
1407 Hit::KeyDown(KeyEvent {
1408 key_code: KeyCode::ArrowLeft,
1409 modifiers: KeyModifiers {
1410 shift: keep_selection,
1411 logo: false,
1412 alt: false,
1413 control: false
1414 },
1415 ..
1416 }) => {
1417 self.reset_blink_timer(cx);
1418 self.move_cursor_left(cx, keep_selection);
1419 }
1420 Hit::KeyDown(KeyEvent {
1421 key_code: KeyCode::ArrowRight,
1422 modifiers: KeyModifiers {
1423 shift: keep_selection,
1424 logo: false,
1425 alt: false,
1426 control: false
1427 },
1428 ..
1429 }) => {
1430 self.reset_blink_timer(cx);
1431 self.move_cursor_right(cx, keep_selection);
1432 }
1433 Hit::KeyDown(KeyEvent {
1434 key_code: KeyCode::ArrowUp,
1435 modifiers: KeyModifiers {
1436 shift: keep_selection,
1437 logo: false,
1438 alt: false,
1439 control: false
1440 },
1441 ..
1442 }) => {
1443 self.reset_blink_timer(cx);
1444 if self.move_cursor_up(cx, keep_selection).is_err() {
1445 warning!("can't move cursor because layout was invalidated by earlier event");
1446 }
1447 },
1448 Hit::KeyDown(KeyEvent {
1449 key_code: KeyCode::ArrowDown,
1450 modifiers: KeyModifiers {
1451 shift: keep_selection,
1452 logo: false,
1453 alt: false,
1454 control: false
1455 },
1456 ..
1457 }) => {
1458 self.reset_blink_timer(cx);
1459 if self.move_cursor_down(cx, keep_selection).is_err() {
1460 warning!("can't move cursor because layout was invalidated by earlier event");
1461 }
1462 }
1463 Hit::KeyDown(KeyEvent {
1464 key_code: KeyCode::KeyA,
1465 modifiers,
1466 ..
1467 }) if modifiers.is_primary() => self.select_all(cx),
1468 Hit::FingerDown(FingerDownEvent {
1469 abs,
1470 tap_count,
1471 device,
1472 ..
1473 }) if device.is_primary_hit() => {
1474 self.reset_blink_timer(cx);
1475 self.set_key_focus(cx);
1476 let rel = abs - self.text_area.rect(cx).pos;
1477 let Ok(cursor) = self.point_in_lpxs_to_cursor(
1478 Point::new(rel.x as f32, rel.y as f32)
1479 ) else {
1480 warning!("can't move cursor because layout was invalidated by earlier event");
1481 return;
1482 };
1483 self.set_cursor(
1484 cx,
1485 cursor,
1486 false
1487 );
1488 match tap_count {
1489 2 => self.select_word(cx),
1490 3 => self.select_all(cx),
1491 _ => {}
1492 }
1493
1494 self.animator_play(cx, id!(hover.down));
1495 }
1496 Hit::FingerUp(fe) => {
1497 if fe.is_over && fe.was_tap() {
1498 if fe.has_hovers() {
1499 self.animator_play(cx, id!(hover.on));
1500 } else {
1501 self.animator_play(cx, id!(hover.off));
1502 }
1503 } else {
1504 self.animator_play(cx, id!(hover.off));
1505 }
1506 }
1507 Hit::FingerMove(FingerMoveEvent {
1508 abs,
1509 tap_count,
1510 device,
1511 ..
1512 }) if device.is_primary_hit() => {
1513 self.reset_blink_timer(cx);
1514 self.set_key_focus(cx);
1515 let rel = abs - self.text_area.rect(cx).pos;
1516 let Ok(cursor) = self.point_in_lpxs_to_cursor(
1517 Point::new(rel.x as f32, rel.y as f32)
1518 ) else {
1519 warning!("can't move cursor because layout was invalidated by earlier event");
1520 return;
1521 };
1522 self.set_cursor(
1523 cx,
1524 cursor,
1525 true
1526 );
1527 match tap_count {
1528 2 => self.select_word(cx),
1529 3 => self.select_all(cx),
1530 _ => {}
1531 }
1532 }
1533 Hit::KeyDown(KeyEvent {
1534 key_code: KeyCode::ReturnKey,
1535 modifiers: mods @ KeyModifiers {
1536 shift: false,
1537 ..
1538 },
1539 ..
1540 }) => {
1541 cx.hide_text_ime();
1542 cx.widget_action(
1543 uid,
1544 &scope.path,
1545 TextInputAction::Returned(
1546 self.text.clone(),
1547 mods,
1548 ),
1549 );
1550 },
1551
1552 Hit::KeyDown(KeyEvent {
1553 key_code: KeyCode::Escape,
1554 ..
1555 }) => {
1556 cx.widget_action(uid, &scope.path, TextInputAction::Escaped);
1557 }
1558 Hit::KeyDown(KeyEvent {
1559 key_code: KeyCode::ReturnKey,
1560 modifiers: KeyModifiers {
1561 shift: true,
1562 ..
1563 },
1564 ..
1565 }) if !self.is_read_only => {
1566 self.reset_blink_timer(cx);
1567 self.create_or_extend_edit_group(EditKind::Other);
1568 self.apply_edit(
1569 cx,
1570 Edit {
1571 start: self.selection.start().index,
1572 end: self.selection.end().index,
1573 replace_with: "\n".to_string(),
1574 }
1575 );
1576 self.draw_bg.redraw(cx);
1577 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1578 }
1579 Hit::KeyDown(KeyEvent {
1580 key_code: KeyCode::Backspace,
1581 ..
1582 }) if !self.is_read_only => {
1583 self.reset_blink_timer(cx);
1584 let mut start = self.selection.start().index;
1585 let end = self.selection.end().index;
1586 if start == end {
1587 start = prev_grapheme_boundary(&self.text, start);
1588 }
1589 self.create_or_extend_edit_group(EditKind::Backspace);
1590 self.apply_edit(
1591 cx,
1592 Edit {
1593 start,
1594 end,
1595 replace_with: String::new(),
1596 }
1597 );
1598 self.draw_bg.redraw(cx);
1599 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1600 }
1601 Hit::KeyDown(KeyEvent {
1602 key_code: KeyCode::Delete,
1603 ..
1604 }) if !self.is_read_only => {
1605 self.reset_blink_timer(cx);
1606 let start = self.selection.start().index;
1607 let mut end = self.selection.end().index;
1608 if start == end {
1609 end = next_grapheme_boundary(&self.text, end);
1610 }
1611 self.create_or_extend_edit_group(EditKind::Delete);
1612 self.apply_edit(
1613 cx,
1614 Edit {
1615 start,
1616 end,
1617 replace_with: String::new(),
1618 }
1619 );
1620 self.draw_bg.redraw(cx);
1621 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1622 }
1623 Hit::KeyDown(KeyEvent {
1624 key_code: KeyCode::KeyZ,
1625 modifiers: modifiers @ KeyModifiers {
1626 shift: false,
1627 ..
1628 },
1629 ..
1630 }) if modifiers.is_primary() && !self.is_read_only => {
1631 if !self.undo(cx) {
1632 return;
1633 }
1634 self.draw_bg.redraw(cx);
1635 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1636 }
1637 Hit::KeyDown(KeyEvent {
1638 key_code: KeyCode::KeyZ,
1639 modifiers: modifiers @ KeyModifiers {
1640 shift: true,
1641 ..
1642 },
1643 ..
1644 }) if modifiers.is_primary() && !self.is_read_only => {
1645 if !self.redo(cx) {
1646 return;
1647 }
1648 self.draw_bg.redraw(cx);
1649 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1650 }
1651 Hit::TextInput(TextInputEvent {
1652 input,
1653 replace_last,
1654 was_paste,
1655 ..
1656 }) if !self.is_read_only => {
1657 let input = self.filter_input(&input, false);
1658 if input.is_empty() {
1659 return;
1660 }
1661 self.create_or_extend_edit_group(
1662 if replace_last || was_paste {
1663 EditKind::Other
1664 } else {
1665 EditKind::Insert
1666 }
1667 );
1668 self.apply_edit(
1669 cx,
1670 Edit {
1671 start: self.selection.start().index,
1672 end: self.selection.end().index,
1673 replace_with: input
1674 }
1675 );
1676 self.animator_play(cx, id!(empty.off));
1677 self.draw_bg.redraw(cx);
1678 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1679 }
1680 Hit::TextCopy(event) => {
1681 *event.response.borrow_mut() = Some(self.selected_text().to_string());
1682 }
1683 Hit::TextCut(event) => {
1684 *event.response.borrow_mut() = Some(self.selected_text().to_string());
1685 if !self.selected_text().is_empty() {
1686 self.history.create_or_extend_edit_group(EditKind::Other, self.selection);
1687 self.apply_edit(
1688 cx,
1689 Edit {
1690 start: self.selection.start().index,
1691 end: self.selection.end().index,
1692 replace_with: String::new(),
1693 }
1694 );
1695 self.draw_bg.redraw(cx);
1696 cx.widget_action(uid, &scope.path, TextInputAction::Changed(self.text.clone()));
1697 }
1698 }
1699 Hit::KeyDown(event) => {
1700 cx.widget_action(uid, &scope.path, TextInputAction::KeyDownUnhandled(event));
1701 }
1702 _ => {}
1703 }
1704 }
1705}
1706
1707impl TextInputRef {
1708 pub fn is_password(&self) -> bool {
1709 if let Some(inner) = self.borrow(){
1710 inner.is_password()
1711 }
1712 else{
1713 false
1714 }
1715 }
1716
1717 pub fn set_is_password(&self, cx: &mut Cx, is_password: bool) {
1718 if let Some(mut inner) = self.borrow_mut(){
1719 inner.set_is_password(cx, is_password);
1720 }
1721 }
1722
1723 pub fn toggle_is_password(&self, cx: &mut Cx) {
1724 if let Some(mut inner) = self.borrow_mut(){
1725 inner.toggle_is_password(cx);
1726 }
1727 }
1728
1729 pub fn is_read_only(&self) -> bool {
1730 if let Some(inner) = self.borrow(){
1731 inner.is_read_only()
1732 }
1733 else{
1734 false
1735 }
1736 }
1737
1738 pub fn set_is_read_only(&self, cx: &mut Cx, is_read_only: bool) {
1739 if let Some(mut inner) = self.borrow_mut(){
1740 inner.set_is_read_only(cx, is_read_only);
1741 }
1742 }
1743
1744 pub fn toggle_is_read_only(&self, cx: &mut Cx) {
1745 if let Some(mut inner) = self.borrow_mut(){
1746 inner.toggle_is_read_only(cx);
1747 }
1748 }
1749
1750 pub fn is_numeric_only(&self) -> bool {
1751 if let Some(inner) = self.borrow(){
1752 inner.is_numeric_only()
1753 }
1754 else{
1755 false
1756 }
1757 }
1758
1759 pub fn set_is_numeric_only(&self, cx: &mut Cx, is_numeric_only: bool) {
1760 if let Some(mut inner) = self.borrow_mut(){
1761 inner.set_is_numeric_only(cx, is_numeric_only);
1762 }
1763 }
1764
1765 pub fn toggle_is_numeric_only(&self, cx: &mut Cx) {
1766 if let Some(mut inner) = self.borrow_mut(){
1767 inner.toggle_is_numeric_only(cx);
1768 }
1769 }
1770
1771 pub fn empty_text(&self) -> String {
1772 if let Some(inner) = self.borrow(){
1773 inner.empty_text().to_string()
1774 }
1775 else{
1776 String::new()
1777 }
1778 }
1779
1780 pub fn set_empty_text(&self, cx: &mut Cx, empty_text: String) {
1781 if let Some(mut inner) = self.borrow_mut(){
1782 inner.set_empty_text(cx, empty_text);
1783 }
1784 }
1785
1786 pub fn selection(&self) -> Selection {
1787 if let Some(inner) = self.borrow(){
1788 inner.selection()
1789 }
1790 else{
1791 Default::default()
1792 }
1793 }
1794
1795 pub fn set_selection(&self, cx: &mut Cx, selection: Selection) {
1796 if let Some(mut inner) = self.borrow_mut(){
1797 inner.set_selection(cx, selection);
1798 }
1799 }
1800
1801 pub fn cursor(&self) -> Cursor {
1802 if let Some(inner) = self.borrow(){
1803 inner.cursor()
1804 }
1805 else{
1806 Default::default()
1807 }
1808 }
1809
1810 pub fn set_cursor(&self, cx: &mut Cx, cursor: Cursor, keep_selection: bool) {
1811 if let Some(mut inner) = self.borrow_mut(){
1812 inner.set_cursor(cx, cursor, keep_selection);
1813 }
1814 }
1815
1816 pub fn selected_text(&self) -> String {
1817 if let Some(inner) = self.borrow(){
1818 inner.selected_text().to_string()
1819 }
1820 else{
1821 String::new()
1822 }
1823 }
1824
1825 pub fn returned(&self, actions: &Actions) -> Option<(String, KeyModifiers)> {
1826 for action in actions.filter_widget_actions_cast::<TextInputAction>(self.widget_uid()){
1827 if let TextInputAction::Returned(text, modifiers) = action {
1828 return Some((text, modifiers));
1829 }
1830 }
1831 None
1832 }
1833
1834 pub fn escaped(&self, actions: &Actions) -> bool {
1835 for action in actions.filter_widget_actions_cast::<TextInputAction>(self.widget_uid()){
1836 if let TextInputAction::Escaped = action {
1837 return true;
1838 }
1839 }
1840 false
1841 }
1842
1843 pub fn changed(&self, actions: &Actions) -> Option<String> {
1844 for action in actions.filter_widget_actions_cast::<TextInputAction>(self.widget_uid()){
1845 if let TextInputAction::Changed(text) = action{
1846 return Some(text);
1847 }
1848 }
1849 None
1850 }
1851
1852 pub fn key_down_unhandled(&self, actions: &Actions) -> Option<KeyEvent> {
1853 for action in actions.filter_widget_actions_cast::<TextInputAction>(self.widget_uid()){
1854 if let TextInputAction::KeyDownUnhandled(event) = action{
1855 return Some(event);
1856 }
1857 }
1858 None
1859 }
1860
1861 pub fn save_state(&self) -> TextInputState {
1864 if let Some(inner) = self.borrow() {
1865 TextInputState {
1866 text: inner.text.clone(),
1867 password_text: inner.password_text.clone(),
1868 selection: inner.selection.clone(),
1869 history: inner.history.clone(),
1870 }
1871 } else {
1872 TextInputState::default()
1873 }
1874 }
1875
1876 pub fn restore_state(&self, cx: &mut Cx, state: TextInputState) {
1879 if let Some(mut inner) = self.borrow_mut() {
1880 inner.set_text(cx, &state.text);
1881 inner.password_text = state.password_text;
1882 inner.history = state.history;
1883 inner.set_selection(cx, state.selection);
1884 }
1885 }
1886}
1887
1888#[derive(Clone, Debug, Default)]
1890pub struct TextInputState {
1891 text: String,
1892 password_text: String,
1893 selection: Selection,
1894 history: History,
1895}
1896
1897#[derive(Clone, Debug, DefaultNone)]
1898pub enum TextInputAction {
1899 None,
1900 KeyFocus,
1901 KeyFocusLost,
1902 Returned(String, KeyModifiers),
1903 Escaped,
1904 Changed(String),
1905 KeyDownUnhandled(KeyEvent),
1906}
1907
1908#[derive(Clone, Debug, Default)]
1909struct History {
1910 current_edit_kind: Option<EditKind>,
1911 undo_stack: EditStack,
1912 redo_stack: EditStack,
1913}
1914
1915impl History {
1916 fn force_new_edit_group(&mut self) {
1917 self.current_edit_kind = None;
1918 }
1919
1920 fn create_or_extend_edit_group(&mut self, edit_kind: EditKind, selection: Selection) {
1921 if !self.current_edit_kind.map_or(false, |current_edit_kind| current_edit_kind.can_merge_with(edit_kind)) {
1922 self.undo_stack.push_edit_group(selection);
1923 self.current_edit_kind = Some(edit_kind);
1924 }
1925 }
1926
1927 fn apply_edit(&mut self, edit: Edit, text: &mut String) {
1928 let inverted_edit = edit.invert(&text);
1929 edit.apply(text);
1930 self.undo_stack.push_edit(inverted_edit);
1931 self.redo_stack.clear();
1932 }
1933
1934 fn undo(
1935 &mut self,
1936 selection: Selection,
1937 text: &mut String,
1938 ) -> Option<Selection> {
1939 if let Some((new_selection, edits)) = self.undo_stack.pop_edit_group() {
1940 self.redo_stack.push_edit_group(selection);
1941 for edit in &edits {
1942 let inverted_edit = edit.invert(text);
1943 edit.apply(text);
1944 self.redo_stack.push_edit(inverted_edit);
1945 }
1946 self.current_edit_kind = None;
1947 Some(new_selection)
1948 } else {
1949 None
1950 }
1951 }
1952
1953 fn redo(
1954 &mut self,
1955 selection: Selection,
1956 text: &mut String,
1957 ) -> Option<Selection> {
1958 if let Some((new_selection, edits)) = self.redo_stack.pop_edit_group() {
1959 self.undo_stack.push_edit_group(selection);
1960 for edit in &edits {
1961 let inverted_edit = edit.invert(text);
1962 edit.apply(text);
1963 self.undo_stack.push_edit(inverted_edit);
1964 }
1965 self.current_edit_kind = None;
1966 Some(new_selection)
1967 } else {
1968 None
1969 }
1970 }
1971
1972 fn clear(&mut self) {
1973 self.current_edit_kind = None;
1974 self.undo_stack.clear();
1975 self.redo_stack.clear();
1976 }
1977}
1978
1979#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1980enum EditKind {
1981 Insert,
1982 Backspace,
1983 Delete,
1984 Other,
1985}
1986
1987impl EditKind {
1988 fn can_merge_with(self, other: EditKind) -> bool {
1989 if self == Self::Other {
1990 false
1991 } else {
1992 self == other
1993 }
1994 }
1995}
1996
1997#[derive(Clone, Debug, Default)]
1998struct EditStack {
1999 edit_groups: Vec<EditGroup>,
2000 edits: Vec<Edit>,
2001}
2002
2003impl EditStack {
2004 fn push_edit_group(&mut self, selection: Selection) {
2005 self.edit_groups.push(EditGroup {
2006 selection,
2007 edit_start: self.edits.len(),
2008 });
2009 }
2010
2011 fn push_edit(&mut self, edit: Edit) {
2012 self.edits.push(edit);
2013 }
2014
2015 fn pop_edit_group(&mut self) -> Option<(Selection, Vec<Edit>)> {
2016 match self.edit_groups.pop() {
2017 Some(edit_group) => Some((
2018 edit_group.selection,
2019 self.edits.drain(edit_group.edit_start..).rev().collect()
2020 )),
2021 None => None,
2022 }
2023 }
2024
2025 fn clear(&mut self) {
2026 self.edit_groups.clear();
2027 self.edits.clear();
2028 }
2029}
2030
2031#[derive(Clone, Copy, Debug)]
2032struct EditGroup {
2033 selection: Selection,
2034 edit_start: usize
2035}
2036
2037#[derive(Clone, Debug)]
2038struct Edit {
2039 start: usize,
2040 end: usize,
2041 replace_with: String,
2042}
2043
2044impl Edit {
2045 fn apply(&self, text: &mut String) {
2046 text.replace_range(self.start..self.end, &self.replace_with);
2047 }
2048
2049 fn invert(&self, text: &str) -> Self {
2050 Self {
2051 start: self.start,
2052 end: self.start + self.replace_with.len(),
2053 replace_with: text[self.start..self.end].to_string(),
2054 }
2055 }
2056}
2057
2058fn prev_grapheme_boundary(text: &str, index: usize) -> usize {
2059 let mut cursor = GraphemeCursor::new(index, text.len(), true);
2060 cursor.prev_boundary(text, 0).unwrap().unwrap_or(0)
2061}
2062
2063fn next_grapheme_boundary(text: &str, index: usize) -> usize {
2064 let mut cursor = GraphemeCursor::new(index, text.len(), true);
2065 cursor.next_boundary(text, 0).unwrap().unwrap_or(text.len())
2066}