1use std::cell::RefCell;
20use std::rc::Rc;
21use std::sync::Arc;
22
23use web_time::Instant;
26
27use super::text_field_core::{
28 byte_at_x, next_char_boundary, next_word_boundary, prev_char_boundary, prev_word_boundary,
29 word_range_at, TextEditCommand, TextEditState,
30};
31use crate::draw_ctx::DrawCtx;
32use crate::event::{Event, EventResult, Key, Modifiers, MouseButton};
33use crate::geometry::{Rect, Size};
34use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
35use crate::text::{measure_advance, Font};
36use crate::undo::UndoBuffer;
37use crate::widget::{BackbufferCache, BackbufferMode, Widget};
38
39mod binding;
44mod clipboard;
45mod widget_impl;
46
47use clipboard::{clipboard_get, clipboard_set};
48pub struct TextField {
54 bounds: Rect,
55 children: Vec<Box<dyn Widget>>,
56 base: WidgetBase,
57
58 edit: Rc<RefCell<TextEditState>>,
60
61 undo: UndoBuffer,
63
64 pending_insert: Option<TextEditCommand>,
66
67 text_on_focus: String,
69
70 font: Arc<Font>,
72 font_size: f64,
73
74 pub read_only: bool,
76 pub select_all_on_focus: bool,
77 pub password_mode: bool,
80
81 focused: bool,
83 hovered: bool,
84 mouse_down: bool,
85 scroll_x: f64,
86
87 focus_time: Option<Instant>,
89 blink_last_phase: std::cell::Cell<u64>,
96
97 last_click_time: Option<Instant>,
99
100 pub placeholder: String,
102
103 pub padding: f64,
105
106 on_change: Option<Box<dyn FnMut(&str)>>,
108 on_enter: Option<Box<dyn FnMut(&str)>>,
109 on_edit_complete: Option<Box<dyn FnMut(&str)>>,
110 text_cell: Option<Rc<RefCell<String>>>,
111
112 cache: BackbufferCache,
119 last_sig: Option<TextFieldSig>,
120}
121
122#[derive(Clone, PartialEq)]
123struct TextFieldSig {
124 text: String,
125 cursor: usize,
126 anchor: usize,
127 focused: bool,
128 hovered: bool,
129 scroll_x_bits: u64,
130 w_bits: u64,
131 h_bits: u64,
132 font_ptr: usize,
139 font_size_bits: u64,
140}
141
142impl TextField {
143 pub fn new(font: Arc<Font>) -> Self {
144 Self {
145 bounds: Rect::default(),
146 children: Vec::new(),
147 base: WidgetBase::new(),
148 edit: Rc::new(RefCell::new(TextEditState::default())),
149 undo: UndoBuffer::new(),
150 pending_insert: None,
151 text_on_focus: String::new(),
152 font,
153 font_size: 14.0,
154 read_only: false,
155 select_all_on_focus: false,
156 password_mode: false,
157 focused: false,
158 hovered: false,
159 mouse_down: false,
160 scroll_x: 0.0,
161 focus_time: None,
162 blink_last_phase: std::cell::Cell::new(u64::MAX),
163 last_click_time: None,
164 placeholder: String::new(),
165 padding: 8.0,
166 on_change: None,
167 on_enter: None,
168 on_edit_complete: None,
169 text_cell: None,
170 cache: BackbufferCache::default(),
171 last_sig: None,
172 }
173 }
174
175 fn active_font(&self) -> Arc<Font> {
180 crate::font_settings::current_system_font().unwrap_or_else(|| Arc::clone(&self.font))
181 }
182
183 pub fn with_font_size(mut self, s: f64) -> Self {
186 self.font_size = s;
187 self
188 }
189 pub fn with_padding(mut self, p: f64) -> Self {
190 self.padding = p;
191 self
192 }
193 pub fn with_read_only(mut self, v: bool) -> Self {
194 self.read_only = v;
195 self
196 }
197 pub fn with_select_all_on_focus(mut self, v: bool) -> Self {
198 self.select_all_on_focus = v;
199 self
200 }
201 pub fn with_password_mode(mut self, v: bool) -> Self {
202 self.password_mode = v;
203 self
204 }
205
206 pub fn with_placeholder(mut self, s: impl Into<String>) -> Self {
207 self.placeholder = s.into();
208 self
209 }
210 pub fn with_text(self, s: impl Into<String>) -> Self {
211 let t = s.into();
212 let len = t.len();
213 let mut st = self.edit.borrow_mut();
214 st.text = t;
215 st.cursor = len;
216 st.anchor = len;
217 drop(st);
218 self
219 }
220
221 pub fn on_change(mut self, cb: impl FnMut(&str) + 'static) -> Self {
222 self.on_change = Some(Box::new(cb));
223 self
224 }
225 pub fn on_enter(mut self, cb: impl FnMut(&str) + 'static) -> Self {
226 self.on_enter = Some(Box::new(cb));
227 self
228 }
229 pub fn on_edit_complete(mut self, cb: impl FnMut(&str) + 'static) -> Self {
230 self.on_edit_complete = Some(Box::new(cb));
231 self
232 }
233
234 pub fn with_margin(mut self, m: Insets) -> Self {
235 self.base.margin = m;
236 self
237 }
238 pub fn with_h_anchor(mut self, h: HAnchor) -> Self {
239 self.base.h_anchor = h;
240 self
241 }
242 pub fn with_v_anchor(mut self, v: VAnchor) -> Self {
243 self.base.v_anchor = v;
244 self
245 }
246 pub fn with_min_size(mut self, s: Size) -> Self {
247 self.base.min_size = s;
248 self
249 }
250 pub fn with_max_size(mut self, s: Size) -> Self {
251 self.base.max_size = s;
252 self
253 }
254
255 pub fn text(&self) -> String {
258 self.edit.borrow().text.clone()
259 }
260 pub fn cursor_pos(&self) -> usize {
261 self.edit.borrow().cursor
262 }
263 pub fn selection(&self) -> String {
264 let st = self.edit.borrow();
265 let lo = st.cursor.min(st.anchor);
266 let hi = st.cursor.max(st.anchor);
267 st.text[lo..hi].to_string()
268 }
269
270 pub fn set_text(&mut self, s: impl Into<String>) {
271 let t = s.into();
272 let len = t.len();
273 let mut st = self.edit.borrow_mut();
274 st.text = t.clone();
275 st.cursor = len;
276 st.anchor = len;
277 drop(st);
278 if let Some(cell) = &self.text_cell {
279 *cell.borrow_mut() = t;
280 }
281 self.undo.clear_history();
282 self.pending_insert = None;
283 }
284
285 fn snap(&self) -> TextEditState {
288 self.edit.borrow().clone()
289 }
290 #[allow(dead_code)]
291 fn apply(&self, s: TextEditState) {
292 *self.edit.borrow_mut() = s;
293 }
294
295 #[allow(dead_code)]
296 fn sel_min(&self) -> usize {
297 let s = self.edit.borrow();
298 s.cursor.min(s.anchor)
299 }
300 #[allow(dead_code)]
301 fn sel_max(&self) -> usize {
302 let s = self.edit.borrow();
303 s.cursor.max(s.anchor)
304 }
305 fn has_selection(&self) -> bool {
306 let s = self.edit.borrow();
307 s.cursor != s.anchor
308 }
309
310 fn flush_pending(&mut self) {
312 if let Some(cmd) = self.pending_insert.take() {
313 self.undo.add(Box::new(cmd));
314 }
315 }
316
317 fn click_to_cursor(&self, real_text: &str, tx: f64) -> usize {
320 let font = self.active_font();
321 if self.password_mode {
322 const BULLET: char = '•';
323 const BULLET_LEN: usize = 3;
324 let n = real_text.chars().count();
325 let masked = BULLET.to_string().repeat(n);
326 let disp = byte_at_x(&font, &masked, self.font_size, tx);
327 let char_idx = disp / BULLET_LEN;
329 real_text
330 .char_indices()
331 .nth(char_idx)
332 .map(|(i, _)| i)
333 .unwrap_or(real_text.len())
334 } else {
335 byte_at_x(&font, real_text, self.font_size, tx)
336 }
337 }
338
339 fn ensure_cursor_visible(&mut self) {
341 if self.bounds.width < 1.0 {
342 return;
343 }
344 let inner_w = (self.bounds.width - self.padding * 2.0).max(0.0);
345 let font = self.active_font();
346 let cx = {
347 let st = self.edit.borrow();
348 if self.password_mode {
349 const BULLET: char = '•';
350 #[allow(dead_code)]
351 const BULLET_LEN: usize = 3;
352 let n = st.text[..st.cursor].chars().count();
353 let masked = BULLET.to_string().repeat(n);
354 measure_advance(&font, &masked, self.font_size)
355 } else {
356 measure_advance(&font, &st.text[..st.cursor], self.font_size)
357 }
358 };
359 if cx < self.scroll_x {
360 self.scroll_x = cx;
361 } else if cx > self.scroll_x + inner_w {
362 self.scroll_x = cx - inner_w;
363 }
364 }
365
366 fn do_insert(&mut self, s: &str, is_single_char: bool) {
371 let before = self.snap();
372 let had_selection = before.cursor != before.anchor;
373
374 {
376 let mut st = self.edit.borrow_mut();
377 if st.cursor != st.anchor {
378 let lo = st.cursor.min(st.anchor);
379 let hi = st.cursor.max(st.anchor);
380 st.text.drain(lo..hi);
381 st.cursor = lo;
382 st.anchor = lo;
383 }
384 let cursor = st.cursor;
385 st.text.insert_str(cursor, s);
386 st.cursor = cursor + s.len();
387 st.anchor = st.cursor;
388 }
389
390 let after = self.snap();
391
392 if is_single_char && !had_selection {
393 if let Some(ref mut pending) = self.pending_insert {
395 pending.after = after;
396 } else {
397 self.pending_insert = Some(TextEditCommand {
398 name: "insert text",
399 before,
400 after,
401 target: Rc::clone(&self.edit),
402 });
403 }
404 } else {
405 self.flush_pending();
407 self.undo.add(Box::new(TextEditCommand {
408 name: "insert text",
409 before,
410 after,
411 target: Rc::clone(&self.edit),
412 }));
413 }
414
415 self.ensure_cursor_visible();
416 self.notify_change();
417 }
418
419 fn do_delete(&mut self, forward: bool, word: bool) {
421 self.flush_pending();
422 let before = self.snap();
423 {
424 let mut st = self.edit.borrow_mut();
425 if st.cursor != st.anchor {
426 let lo = st.cursor.min(st.anchor);
427 let hi = st.cursor.max(st.anchor);
428 st.text.drain(lo..hi);
429 st.cursor = lo;
430 st.anchor = lo;
431 } else if forward {
432 let cursor = st.cursor;
433 let end = if word {
434 next_word_boundary(&st.text, cursor)
435 } else {
436 next_char_boundary(&st.text, cursor)
437 };
438 if end > cursor {
439 st.text.drain(cursor..end);
440 }
441 st.anchor = st.cursor;
442 } else {
443 let cursor = st.cursor;
444 let start = if word {
445 prev_word_boundary(&st.text, cursor)
446 } else {
447 prev_char_boundary(&st.text, cursor)
448 };
449 if start < cursor {
450 st.text.drain(start..cursor);
451 st.cursor = start;
452 st.anchor = start;
453 }
454 }
455 }
456 let after = self.snap();
457 self.undo.add(Box::new(TextEditCommand {
458 name: "delete text",
459 before,
460 after,
461 target: Rc::clone(&self.edit),
462 }));
463 self.ensure_cursor_visible();
464 self.notify_change();
465 }
466
467 fn do_undo(&mut self) {
468 self.flush_pending();
469 self.undo.undo();
470 let len = self.edit.borrow().text.len();
472 let mut st = self.edit.borrow_mut();
473 st.cursor = st.cursor.min(len);
474 st.anchor = st.anchor.min(len);
475 drop(st);
476 self.ensure_cursor_visible();
477 self.notify_change();
478 }
479
480 fn do_redo(&mut self) {
481 self.flush_pending();
482 self.undo.redo();
483 let len = self.edit.borrow().text.len();
484 let mut st = self.edit.borrow_mut();
485 st.cursor = st.cursor.min(len);
486 st.anchor = st.anchor.min(len);
487 drop(st);
488 self.ensure_cursor_visible();
489 self.notify_change();
490 }
491
492 fn notify_change(&mut self) {
495 let t = self.text();
496 if let Some(cell) = &self.text_cell {
497 *cell.borrow_mut() = t.clone();
498 }
499 if let Some(mut cb) = self.on_change.take() {
500 cb(&t);
501 self.on_change = Some(cb);
502 }
503 }
504 fn notify_enter(&mut self) {
505 if let Some(mut cb) = self.on_enter.take() {
506 let t = self.text();
507 cb(&t);
508 self.on_enter = Some(cb);
509 }
510 }
511 fn notify_edit_complete(&mut self) {
512 if let Some(mut cb) = self.on_edit_complete.take() {
513 let t = self.text();
514 cb(&t);
515 self.on_edit_complete = Some(cb);
516 }
517 }
518
519 fn handle_key(&mut self, key: &Key, mods: Modifiers) -> EventResult {
522 let anchor_before = self.edit.borrow().anchor;
524
525 let cmd = mods.ctrl || mods.meta;
529 let word = mods.ctrl || mods.alt;
533
534 match key {
535 Key::Char(c) if !self.read_only || cmd => {
537 if cmd {
538 return match c {
539 'a' | 'A' => {
540 let len = self.edit.borrow().text.len();
541 let mut st = self.edit.borrow_mut();
542 st.anchor = 0;
543 st.cursor = len;
544 EventResult::Consumed
545 }
546 'z' | 'Z' if !mods.shift => {
547 if !self.read_only {
548 self.do_undo();
549 }
550 EventResult::Consumed
551 }
552 'z' | 'Z' | 'y' | 'Y' => {
553 if !self.read_only {
554 self.do_redo();
555 }
556 EventResult::Consumed
557 }
558 'x' | 'X' => {
559 if !self.read_only && self.has_selection() {
560 clipboard_set(&self.selection());
561 self.do_delete(false, false); }
563 EventResult::Consumed
564 }
565 'c' | 'C' => {
566 if self.has_selection() {
567 clipboard_set(&self.selection());
568 }
569 EventResult::Consumed
570 }
571 'v' | 'V' => {
572 if !self.read_only {
573 if let Some(clip) = clipboard_get() {
574 self.do_insert(&clip, false);
575 }
576 }
577 EventResult::Consumed
578 }
579 _ => EventResult::Ignored,
580 };
581 }
582 if self.read_only {
583 return EventResult::Ignored;
584 }
585 let mut buf = [0u8; 4];
586 let s = c.encode_utf8(&mut buf);
587 self.do_insert(s, true);
588 EventResult::Consumed
589 }
590
591 Key::Insert => {
598 if mods.shift && !self.read_only {
599 if let Some(clip) = clipboard_get() {
600 self.do_insert(&clip, false);
601 }
602 return EventResult::Consumed;
603 }
604 if cmd {
605 if self.has_selection() {
606 clipboard_set(&self.selection());
607 }
608 return EventResult::Consumed;
609 }
610 EventResult::Ignored
611 }
612
613 Key::Backspace if !self.read_only => {
615 self.do_delete(false, word);
616 EventResult::Consumed
617 }
618
619 Key::Delete if !self.read_only => {
621 if mods.shift {
622 if self.has_selection() {
624 clipboard_set(&self.selection());
625 self.do_delete(false, false);
626 }
627 } else {
628 self.do_delete(true, word);
629 }
630 EventResult::Consumed
631 }
632
633 Key::ArrowLeft => {
638 self.flush_pending();
639 let (cur, anchor) = {
640 let st = self.edit.borrow();
641 (st.cursor, st.anchor)
642 };
643 let new_cur = if mods.meta {
644 0 } else if !mods.shift && cur != anchor {
646 cur.min(anchor) } else if word {
648 prev_word_boundary(&self.edit.borrow().text, cur)
649 } else {
650 prev_char_boundary(&self.edit.borrow().text, cur)
651 };
652 let new_anchor = if mods.shift { anchor } else { new_cur };
653 let mut st = self.edit.borrow_mut();
654 st.cursor = new_cur;
655 st.anchor = new_anchor;
656 drop(st);
657 if new_cur == 0 {
658 self.scroll_x = 0.0;
659 }
660 self.ensure_cursor_visible();
661 EventResult::Consumed
662 }
663
664 Key::ArrowRight => {
667 self.flush_pending();
668 let text_len = self.edit.borrow().text.len();
669 let (cur, anchor) = {
670 let st = self.edit.borrow();
671 (st.cursor, st.anchor)
672 };
673 let new_cur = if mods.meta {
674 text_len } else if !mods.shift && cur != anchor {
676 cur.max(anchor) } else if word {
678 next_word_boundary(&self.edit.borrow().text, cur)
679 } else if cur < text_len {
680 next_char_boundary(&self.edit.borrow().text, cur)
681 } else {
682 cur
683 };
684 let new_anchor = if mods.shift { anchor } else { new_cur };
685 let mut st = self.edit.borrow_mut();
686 st.cursor = new_cur;
687 st.anchor = new_anchor;
688 drop(st);
689 self.ensure_cursor_visible();
690 EventResult::Consumed
691 }
692
693 Key::ArrowUp if mods.meta => {
699 self.flush_pending();
700 let (_, anchor) = {
701 let st = self.edit.borrow();
702 (st.cursor, st.anchor)
703 };
704 let new_cur = 0;
705 let new_anchor = if mods.shift { anchor } else { new_cur };
706 let mut st = self.edit.borrow_mut();
707 st.cursor = new_cur;
708 st.anchor = new_anchor;
709 drop(st);
710 self.scroll_x = 0.0;
711 EventResult::Consumed
712 }
713 Key::ArrowDown if mods.meta => {
714 self.flush_pending();
715 let len = self.edit.borrow().text.len();
716 let (_, anchor) = {
717 let st = self.edit.borrow();
718 (st.cursor, st.anchor)
719 };
720 let new_cur = len;
721 let new_anchor = if mods.shift { anchor } else { new_cur };
722 let mut st = self.edit.borrow_mut();
723 st.cursor = new_cur;
724 st.anchor = new_anchor;
725 drop(st);
726 self.ensure_cursor_visible();
727 EventResult::Consumed
728 }
729
730 Key::Home => {
734 self.flush_pending();
735 let mut st = self.edit.borrow_mut();
736 st.cursor = 0;
737 if !mods.shift {
738 st.anchor = 0;
739 }
740 drop(st);
741 self.scroll_x = 0.0;
742 EventResult::Consumed
743 }
744
745 Key::End => {
748 self.flush_pending();
749 let len = self.edit.borrow().text.len();
750 let mut st = self.edit.borrow_mut();
751 st.cursor = len;
752 if !mods.shift {
753 st.anchor = len;
754 }
755 drop(st);
756 self.ensure_cursor_visible();
757 EventResult::Consumed
758 }
759
760 Key::Enter => {
765 self.flush_pending();
766 self.notify_enter();
767 if self.text() != self.text_on_focus {
768 self.notify_edit_complete();
769 self.text_on_focus = self.text();
770 }
771 EventResult::Consumed
772 }
773
774 Key::Escape => {
776 self.flush_pending();
777 let cur = self.edit.borrow().cursor;
778 self.edit.borrow_mut().anchor = cur;
779 EventResult::Consumed
780 }
781
782 _ => {
783 let _ = anchor_before;
784 EventResult::Ignored
785 }
786 }
787 }
788}
789
790