1use alloc::format;
68use alloc::string::String;
69use alloc::vec::Vec;
70
71use crate::consts::*;
72use crate::keys::*;
73use crate::value::{fmt_f64_sep, parse_f64_sep, rf_fix, rf_scaled, RangeF};
74use crate::{Sp, Ui};
75use text::segment;
76#[path = "text_surface.rs"]
77mod surface;
78pub use surface::TextSurfaceLine;
79#[path = "edit_spell.rs"]
80mod spellbox;
81
82const TB_NAT_W: i32 = 200; const NB_NAT_W: i32 = 120; const TB_FLAG_MASK: u32 = 0xFF80_0000;
85const ML_MAX: usize = 4096; const PW_MAX: usize = 1024; const UD_N: usize = 16; const UD_SEG: usize = 2048; const TXQ_MAX: usize = super::ctx::TEXTQ_MAX; const NB_CAP: u32 = 32; #[derive(Clone, Copy)]
96pub struct TextboxPresentation {
97 pub font: u32,
99 pub size: i32,
101 pub line_height: i32,
103 pub baseline: i32,
105 pub color: u32,
107 pub background: u32,
109 pub underline: bool,
111}
112const CTRL_KEYS: [u32; 13] = [65, 67, 88, 86, 90, 89, VK_LEFT, VK_RIGHT, VK_HOME, VK_END, VK_BACK, VK_DELETE, VK_INSERT];
114
115pub(crate) struct S {
117 pub(crate) hclip: Option<Vec<u8>>,
119 pub(crate) hclip_fmt: u32,
120 pub(crate) hclip_rich: Vec<(u32, Vec<u8>)>,
122 buf: Vec<u8>, disp: Vec<u8>, tb_id: u32,
126 tb_max: usize, tb_flags: u32,
128 tb_chg: bool,
129 tb_ev: u32,
130 tb_natw: i32,
131 tb_base: i32,
132 tb_font: u32,
133 tb_size: i32,
134 tb_sem: Option<usize>, presentation: Option<TextboxPresentation>,
136 surface: Option<Vec<TextSurfaceLine>>,
137 surface_start: Option<usize>,
138 surface_change: Option<(usize, usize, String)>,
139 ime_frame: Option<u32>, pw_purpose: Option<u32>, pw_frame: u32, ed_id: u32, ed_win: u32,
146 ed_blur: u32, ed_blur_win: u32,
148 ed_caret: usize,
149 ed_anchor: usize,
150 ed_t0: u32, ed_merge: bool, ed_drag: bool, ed_seen: u32, ed_self: bool, ed_multi: bool, ed_prefx: i32, ud_base: usize,
159 ud_pos: usize,
160 ud_count: usize,
161 ud_hdr: [[usize; 3]; UD_N], ud_data: [Vec<u8>; UD_N],
163 nb_id: u32, nb_win: u32,
166 nb_seen: u32,
167 nb_pend: u32, nb_pend_win: u32,
169 nb_pend_ev: u32,
170 nb_kind: u32, nb_rg: Range, nb_rf: RangeF,
173 nb_shown: u64, nb_buf: Vec<u8>,
175 nb_view: Vec<u8>,
176 nb_orig: Vec<u8>, pend_text: Option<(u32, u32, u32, Vec<u8>)>, ml_start: Vec<usize>, ml_hyph: Vec<bool>, hyph: Option<alloc::sync::Arc<spell::Hyphenator>>, ml_w: i32, ml_lh: i32, ml_page: i32, spell: spellbox::S,
187}
188
189impl Default for S {
190 fn default() -> S {
191 S {
192 hclip: None,
193 hclip_fmt: 0,
194 hclip_rich: Vec::new(),
195 buf: Vec::new(),
196 disp: Vec::new(),
197 tb_id: 0,
198 tb_max: 0,
199 tb_flags: 0,
200 tb_chg: false,
201 tb_ev: 0,
202 tb_natw: 0,
203 tb_base: 0,
204 tb_font: 0,
205 tb_size: 0,
206 tb_sem: None,
207 presentation: None,
208 surface: None,
209 surface_start: None,
210 surface_change: None,
211 ime_frame: None,
212 pw_purpose: None,
213 pw_frame: 0,
214 ed_id: 0,
215 ed_win: 0,
216 ed_blur: 0,
217 ed_blur_win: 0,
218 ed_caret: 0,
219 ed_anchor: 0,
220 ed_t0: 0,
221 ed_merge: false,
222 ed_drag: false,
223 ed_seen: 0,
224 ed_self: false,
225 ed_multi: false,
226 ed_prefx: -1,
227 ud_base: 0,
228 ud_pos: 0,
229 ud_count: 0,
230 ud_hdr: [[0; 3]; UD_N],
231 ud_data: core::array::from_fn(|_| Vec::new()),
232 nb_id: 0,
233 nb_win: 0,
234 nb_seen: 0,
235 nb_pend: 0,
236 nb_pend_win: 0,
237 nb_pend_ev: 0,
238 nb_kind: 0,
239 nb_rg: Range::default(),
240 nb_rf: RangeF::default(),
241 nb_shown: 0,
242 nb_buf: Vec::new(),
243 nb_view: Vec::new(),
244 nb_orig: Vec::new(),
245 pend_text: None,
246 ml_start: Vec::new(),
247 ml_hyph: Vec::new(),
248 hyph: None,
249 ml_w: 0,
250 ml_lh: 0,
251 ml_page: 0,
252 spell: Default::default(),
253 }
254 }
255}
256
257fn disp_of(e: &S) -> &[u8] {
259 if e.tb_flags & TB_PASSWORD != 0 { &e.disp } else { &e.buf }
260}
261
262fn at(s: &[u8], i: usize) -> u8 {
264 if i < s.len() { s[i] } else { 0 }
265}
266
267fn u8_decode(s: &[u8], i: usize) -> (u32, usize) {
271 let b0 = at(s, i) as u32;
272 if b0 < 0x80 {
273 return (b0, i + 1);
274 }
275 let (mut cp, n, min) = if b0 < 0xC2 {
276 return (0xFFFD, i + 1);
277 } else if b0 < 0xE0 {
278 (b0 & 0x1F, 1, 0x80)
279 } else if b0 < 0xF0 {
280 (b0 & 0x0F, 2, 0x800)
281 } else if b0 < 0xF5 {
282 (b0 & 7, 3, 0x10000)
283 } else {
284 return (0xFFFD, i + 1);
285 };
286 let mut j = i + 1;
287 for _ in 0..n {
288 let c = at(s, j) as u32;
289 if c & 0xC0 != 0x80 {
290 return (0xFFFD, i + 1);
291 }
292 cp = cp << 6 | (c & 0x3F);
293 j += 1;
294 }
295 if cp < min || cp > 0x10FFFF || cp & 0xFFFF_F800 == 0xD800 {
296 return (0xFFFD, i + 1);
297 }
298 (cp, j)
299}
300
301fn u8_next(s: &[u8], i: usize) -> usize {
303 if at(s, i) == 0 {
304 return i;
305 }
306 u8_decode(s, i).1
307}
308
309fn u8_prev(s: &[u8], i: usize) -> usize {
311 let mut p = i;
312 if p == 0 {
313 return 0;
314 }
315 loop {
316 p -= 1;
317 if p == 0 {
318 return 0;
319 }
320 if s[p] & 0xC0 != 0x80 {
321 return p;
322 }
323 }
324}
325
326fn u8_clip(s: &[u8], n: usize) -> usize {
328 let mut k = n;
329 while k != 0 && at(s, k) & 0xC0 == 0x80 {
330 k -= 1;
331 }
332 k
333}
334
335fn u8_push(out: &mut Vec<u8>, cp: u32) {
339 let c = if cp > 0x10FFFF || cp & 0xFFFF_F800 == 0xD800 { 0xFFFD } else { cp };
340 if c < 0x80 {
341 out.push(c as u8);
342 } else if c < 0x800 {
343 out.push(0xC0 | (c >> 6) as u8);
344 out.push(0x80 | (c & 0x3F) as u8);
345 } else if c < 0x10000 {
346 out.push(0xE0 | (c >> 12) as u8);
347 out.push(0x80 | ((c >> 6) & 0x3F) as u8);
348 out.push(0x80 | (c & 0x3F) as u8);
349 } else {
350 out.push(0xF0 | (c >> 18) as u8);
351 out.push(0x80 | ((c >> 12) & 0x3F) as u8);
352 out.push(0x80 | ((c >> 6) & 0x3F) as u8);
353 out.push(0x80 | (c & 0x3F) as u8);
354 }
355}
356
357fn to_string(mut b: Vec<u8>) -> String {
360 if let Some(p) = b.iter().position(|&c| c == 0) {
361 b.truncate(p);
362 }
363 match String::from_utf8(b) {
364 Ok(s) => s,
365 Err(e) => String::from_utf8_lossy(e.as_bytes()).into_owned(),
366 }
367}
368
369fn tb_owns(entry: u32, multi: bool) -> bool {
371 let vk = entry & 0xFF;
372 let m = entry >> 16;
373 if vk == VK_TAB {
374 return false;
375 }
376 if multi {
377 if vk == VK_UP || vk == VK_DOWN || vk.wrapping_sub(VK_PRIOR) <= VK_NEXT - VK_PRIOR {
379 return m & (MOD_ALT | MOD_CTRL) == 0;
380 }
381 if vk == VK_RETURN {
382 return m & MOD_ALT == 0;
383 }
384 }
385 if vk == VK_UP || vk == VK_DOWN || vk.wrapping_sub(VK_PRIOR) <= VK_NEXT - VK_PRIOR {
388 return false;
389 }
390 if vk.wrapping_sub(VK_SHIFT) <= VK_CAPITAL - VK_SHIFT || vk.wrapping_sub(VK_LWIN) <= VK_APPS - VK_LWIN || vk.wrapping_sub(VK_F1) <= 23 || vk.wrapping_sub(VK_NUMLOCK) <= 1
394 || vk.wrapping_sub(VK_LSHIFT) <= VK_RMENU - VK_LSHIFT
395 {
396 return false;
397 }
398 if m & MOD_ALTGR != 0 || m & (MOD_CTRL | MOD_ALT) == MOD_CTRL | MOD_ALT {
400 return true;
401 }
402 if m & MOD_ALT != 0 {
403 return false;
404 }
405 if m & MOD_CTRL == 0 {
406 return true;
407 }
408 CTRL_KEYS.contains(&vk)
410}
411
412fn tb_accept(cp: u32, flags: u32) -> bool {
414 if cp < 32 || cp == 127 {
415 return false;
416 }
417 if flags & TB_DIGITS == 0 || cp.wrapping_sub(48) <= 9 {
418 return true;
419 }
420 if flags & TB_DECIMAL != 0 && (cp == 44 || cp == 46) {
421 return true; }
423 cp == 45 && flags & TB_SIGNED != 0
424}
425
426fn nb_format(v: i32) -> Vec<u8> {
428 format!("{}", v).into_bytes()
429}
430
431fn nb_parse_text(t: &[u8], stored: i32) -> i64 {
434 let mut i = 0;
435 let neg = at(t, 0) == b'-';
436 if neg {
437 i = 1;
438 }
439 let mut v: i64 = 0;
440 let mut digits = 0;
441 while i < NB_CAP as usize {
442 let d = at(t, i).wrapping_sub(48);
443 if d > 9 {
444 break;
445 }
446 digits += 1;
447 v = v * 10 + d as i64;
448 if v as u64 > 0x8000_0000 {
449 v = 0x8000_0000;
450 }
451 i += 1;
452 }
453 if digits == 0 {
454 return stored as i64;
455 }
456 if neg { -v } else { v }
457}
458
459fn rg_clamp(v: i64, rg: &Range) -> i32 {
461 let mut c = v;
462 if c < rg.min as i64 {
463 c = rg.min as i64;
464 }
465 if c > rg.max as i64 {
466 c = rg.max as i64;
467 }
468 c as i32
469}
470
471impl Ui {
472 pub(crate) fn ed_is(&self, id: u32) -> bool {
476 let e = &self.m.edit;
477 e.ed_id == id && e.ed_win == self.uiw_cur
478 }
479
480 fn nb_is(&self, id: u32) -> bool {
482 let e = &self.m.edit;
483 e.nb_id == id && e.nb_win == self.uiw_cur
484 }
485
486 pub fn textbox_selection(&self, id: u32) -> Option<(usize, usize)> {
489 if self.ed_is(id) && self.focused() == id {
490 Some((self.m.edit.ed_anchor, self.m.edit.ed_caret))
491 } else { None }
492 }
493
494 pub fn textbox_select(&mut self, id: u32, text: &str, anchor: usize, caret: usize) -> bool {
497 if !self.ed_is(id) || self.focused() != id || !text.is_char_boundary(anchor) || !text.is_char_boundary(caret) {
498 return false;
499 }
500 self.m.edit.ed_anchor = anchor;
501 self.m.edit.ed_caret = caret;
502 self.m.edit.ed_prefx = -1;
503 self.tb_blink_reset();
504 self.repass();
505 true
506 }
507
508 #[cfg_attr(jfgui_dev, track_caller)]
510 pub fn textbox(&mut self, id: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32) -> u32 {
511 let _src = self.src_scope();
512 self.textbox_icon(id, 0, placeholder, buf, cap_flags)
513 }
514
515 #[cfg_attr(jfgui_dev, track_caller)]
519 pub fn textbox_icon(&mut self, id: u32, icon: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32) -> u32 {
520 let _src = self.src_scope();
521 self.m.edit.tb_natw = TB_NAT_W;
522 let mut b = core::mem::take(buf).into_bytes();
523 let ev = self.tb_core(id, placeholder, &mut b, cap_flags, icon);
524 *buf = to_string(b);
525 ev
526 }
527
528 #[cfg_attr(jfgui_dev, track_caller)]
533 pub fn textbox_presented(&mut self, id: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32, mut presentation: TextboxPresentation) -> u32 {
534 let _src = self.src_scope();
535 presentation.size = presentation.size.clamp(256, 512 * 256);
536 presentation.line_height = presentation.line_height.clamp(1, 2048);
537 presentation.baseline = presentation.baseline.clamp(0, presentation.line_height);
538 let previous = self.m.edit.presentation.replace(presentation);
539 let old_font = self.text.get_font();
540 let ev = self.textbox(id, placeholder, buf, cap_flags | TB_MULTI);
541 self.m.edit.presentation = previous;
542 self.text.set_font(old_font.0, old_font.1);
543 ev
544 }
545
546 fn tb_core(&mut self, id: u32, placeholder: Option<&str>, buf: &mut Vec<u8>, cap_flags: u32, icon: u32) -> u32 {
549 if let Some(p) = buf.iter().position(|&c| c == 0) {
550 buf.truncate(p); }
552 core::mem::swap(buf, &mut self.m.edit.buf);
553 let set = self.a11y_text(id).map(|t| {
555 let max = ((cap_flags & TB_CAP_MASK) as usize).saturating_sub(1);
556 let mut n = if max > 0 { t.len().min(max) } else { t.len() };
557 while !t.is_char_boundary(n) {
558 n -= 1;
559 }
560 let held = self.ed_is(id);
561 let e = &mut self.m.edit;
562 e.buf = t.as_bytes()[..n].to_vec();
563 if held {
564 e.ed_caret = n;
565 e.ed_anchor = n;
566 }
567 self.repass();
568 });
569 let ph = placeholder.map(|p| p.split('\0').next().unwrap_or(""));
570 let ev = if cap_flags & TB_MULTI != 0 {
571 self.tbm_core(id, ph, cap_flags)
572 } else {
573 self.tb_core1(id, ph, cap_flags, icon)
574 };
575 core::mem::swap(buf, &mut self.m.edit.buf);
576 if set.is_some() { ev | EV_ACT | EV_COMMIT } else { ev }
577 }
578
579 fn tb_start(&mut self, id: u32, cap_flags: u32, mask: u32) -> u32 {
581 let e = &mut self.m.edit;
582 e.tb_id = id;
583 e.tb_max = ((cap_flags & TB_CAP_MASK) as usize).saturating_sub(1);
584 e.tb_flags = cap_flags & mask;
585 e.tb_chg = false;
586 e.tb_ev = 0;
587 self.take_flags()
588 }
589
590 fn tb_core1(&mut self, id: u32, placeholder: Option<&str>, cap_flags: u32, icon: u32) -> u32 {
592 let stflags = self.tb_start(id, cap_flags, TB_FLAG_MASK);
593 let st = self.style;
595 let mut sp = Sp { value: self.m.edit.tb_natw, icon, flags: 0, ..Sp::default() };
596 let (w, h) = st.textbox_metrics(self, &mut sp);
597 let (w, h) = self.ctrl_size(w, h, stflags, false); if icon != 0 { sp.s[0] += self.dp(23); }
599 self.place(w, h);
600 let mut hb = HB_FOCUS | HB_TEXT | CUR_IBEAM << HB_CURSOR_SHIFT;
601 if stflags & ST_DISABLED != 0 {
602 hb |= HB_DISABLED;
603 }
604 let ev = self.behave(id, hb);
605 let disabled = self.wp.ctx.last_state == SS_DISABLED as u32;
606 let mut editing = self.tb_focus();
607 if editing {
608 self.m.edit.ed_multi = false; }
610 let hair = self.hair();
612 let r = self.wp.ctx.last_rect;
613 let x0 = sp.s[0].wrapping_add(r[0]);
614 let tw = r[2].wrapping_sub(sp.s[0].wrapping_add(sp.s[1])).max(1);
615 let mut scroll = self.slot(id).scrollx;
616 st.textbox_font(self, &sp);
617 self.tb_build_disp();
618 if editing && !self.spell_menu_open(id) {
620 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(scroll);
621 self.tb_mouse(ev, x);
622 self.tb_keys();
623 self.tb_typed();
624 #[cfg(target_os = "android")]
625 self.tb_android_ime();
626 if self.m.edit.tb_chg {
627 self.m.edit.tb_ev |= EV_ACT;
628 self.repass();
629 self.tb_build_disp();
630 }
631 if !self.ed_is(id) {
632 editing = false; self.ime_off();
634 }
635 }
636 let (lh, base) = st.textbox_font(self, &sp);
638 let top = (r[3].wrapping_sub(lh) >> 1).wrapping_add(r[1]);
639 self.m.edit.tb_base = top.wrapping_add(base);
640 let (font, size) = self.text.get_font();
641 self.m.edit.tb_font = font;
642 self.m.edit.tb_size = size;
643 let last = tw.wrapping_sub(hair); let mut s = scroll;
646 let mut cx = 0;
647 if editing {
648 let d = self.tb_to_disp(self.m.edit.ed_caret);
649 cx = self.text.caret_x(disp_of(&self.m.edit), d);
650 if cx.wrapping_sub(s) > last {
651 s = cx.wrapping_sub(last);
652 }
653 if cx < s {
654 s = cx;
655 }
656 }
657 let dlen = disp_of(&self.m.edit).len();
659 let ex = self.text.caret_x(disp_of(&self.m.edit), dlen).wrapping_sub(last);
660 if s > ex {
661 s = ex;
662 }
663 if s < 0 {
664 s = 0;
665 }
666 scroll = s;
667 self.slot(id).scrollx = s;
668 sp.place(r);
670 sp.ev = ev;
671 sp.value = editing as i32;
672 sp.value2 = disabled as i32;
673 st.textbox_frame(self, &sp);
674 if icon != 0 {
675 let size = self.dp(16);
676 self.draw_color(self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 }));
677 self.draw_icon(icon, r[0] + self.dp(8), r[1] + (r[3] - size) / 2, size << 8);
678 }
679 self.draw_clip(x0, r[1], tw, r[3]);
681 self.ltr_begin(x0, tw);
682 let bx = x0.wrapping_sub(scroll); let tcol = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT });
684 if dlen == 0 {
685 if let Some(p) = placeholder {
687 let c = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 });
688 self.tb_emit_hint(x0, p, c, tw);
689 }
690 } else {
691 let (a, b) = self.tb_sel();
692 if editing && a != b {
693 let da = self.tb_to_disp(a);
695 let sx0 = self.text.caret_x(disp_of(&self.m.edit), da);
696 let db = self.tb_to_disp(b);
697 let sx1 = self.text.caret_x(disp_of(&self.m.edit), db);
698 let c = self.color(T_ACCENT);
699 self.draw_color(c);
700 self.draw_rect(bx.wrapping_add(sx0), top, sx1.wrapping_sub(sx0), lh);
701 self.tb_clip_x(r[0], bx.wrapping_add(sx0)); self.tb_emit_disp(bx, tcol);
703 self.draw_unclip();
704 self.tb_clip_x(bx.wrapping_add(sx0), bx.wrapping_add(sx1)); let c = self.color(T_ON_ACCENT);
706 self.tb_emit_disp(bx, c);
707 self.draw_unclip();
708 self.tb_clip_x(bx.wrapping_add(sx1), r[0].wrapping_add(r[2])); self.tb_emit_disp(bx, tcol);
710 self.draw_unclip();
711 } else {
712 self.tb_emit_disp(bx, tcol);
713 }
714 }
715 let spell = self.spell_scan(id);
717 if spell && dlen != 0 {
718 self.spell_draw(id, bx, 0, dlen, editing);
719 }
720 if editing {
721 let x = bx.wrapping_add(cx);
722 if self.tb_caret_on() {
723 let c = self.color(T_TEXT);
724 self.draw_color(c);
725 self.draw_rect(x, top, hair, lh);
726 }
727 plat::ime_inline(false); let pw = self.m.edit.tb_flags & TB_PASSWORD != 0;
729 self.tb_purpose(pw);
730 self.ime_pos(x, top, lh);
731 }
732 self.ltr_end();
733 self.draw_unclip();
734 if spell {
735 let hit = (ev & EV_RCLICK != 0).then(|| {
736 self.text.set_font(self.m.edit.tb_font, self.m.edit.tb_size);
737 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(scroll);
738 self.tb_hit(x)
739 });
740 self.spell_input(id, ev, hit, editing);
741 }
742 self.tb_finish(ev, editing)
743 }
744
745 fn tb_finish(&mut self, ev: u32, editing: bool) -> u32 {
747 #[cfg(target_os = "android")]
748 if editing {
749 let e = &self.m.edit;
750 plat::android_ime_publish(e.tb_id, &e.buf, e.ed_anchor, e.ed_caret, e.tb_flags);
751 }
752 if !editing {
753 self.wp.ctx.last_state &= !(SS_FOCUSED as u32);
754 }
755 let (v, mx) = if editing { (self.m.edit.ed_caret as i32, self.m.edit.ed_anchor as i32) } else { (0, 0) };
756 let pw = self.m.edit.tb_flags & TB_PASSWORD != 0;
757 let d = core::mem::take(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf });
758 let n0 = self.sems.len();
759 {
760 let l = String::from_utf8_lossy(&d);
761 let id = self.m.edit.tb_id;
762 let kind = self.m.edit.tb_flags & (TB_MULTI | TB_PASSWORD | TB_READONLY);
763 let s = self.sem(K_TEXTBOX, id, Some(l.as_ref()), v);
764 s.max = mx;
765 s.min = (kind >> 24) as i32; }
767 self.m.edit.tb_sem = if self.sems.len() > n0 { Some(n0) } else { None };
768 *(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf }) = d;
769 let mut out = ev & (EV_HOVER | EV_HELD | EV_DOUBLE | EV_RCLICK | EV_DRAG);
771 if editing {
772 out |= EV_FOCUS;
773 }
774 out | self.m.edit.tb_ev
775 }
776
777 fn tb_sem_set(&mut self, value: i32, max: i32) {
779 let r = match self.m.edit.tb_sem {
780 Some(i) if i < self.sems.len() => &mut self.sems[i],
781 _ => &mut self.sem_dummy,
782 };
783 r.value = value;
784 r.max = max;
785 }
786
787 fn tbm_core(&mut self, id: u32, placeholder: Option<&str>, cap_flags: u32) -> u32 {
791 let stflags = self.tb_start(id, cap_flags, TB_FLAG_MASK & !TB_PASSWORD);
792 let hy = cap_flags & (TB_HYPHENATE | TB_WRAP) == TB_HYPHENATE | TB_WRAP;
793 self.m.edit.hyph = self.hyph_take(hy); let presentation = self.m.edit.presentation;
796 let lh = presentation.map_or_else(|| self.ts_line(TS_BODY), |p| p.line_height);
797 let st = self.style;
798 let mut sp = Sp { value: self.m.edit.tb_natw, value2: lh, flags: 1, ..Sp::default() };
799 let (w, h) = if presentation.is_some() {
800 (self.dp(self.m.edit.tb_natw), lh.saturating_mul(5))
801 } else {
802 st.textbox_metrics(self, &mut sp)
803 };
804 self.place(w, h);
805 let mut hb = HB_FOCUS | HB_TEXT | CUR_IBEAM << HB_CURSOR_SHIFT;
806 if stflags & ST_DISABLED != 0 {
807 hb |= HB_DISABLED;
808 }
809 let ev = self.behave(id, hb);
810 let disabled = self.wp.ctx.last_state == SS_DISABLED as u32;
811 let mut editing = self.tb_focus();
812 if editing {
813 self.m.edit.ed_multi = true;
814 }
815 let hair = self.hair();
816 let r = self.wp.ctx.last_rect;
817 let x0 = sp.s[0].wrapping_add(r[0]);
818 let tw = r[2].wrapping_sub(sp.s[0].wrapping_add(sp.s[1])).max(1);
819 self.m.edit.ml_w = tw.wrapping_sub(hair); let top = r[1].wrapping_add(sp.s[2]);
821 let vh = r[3].wrapping_sub(sp.s[2].wrapping_add(sp.s[2])).max(lh); self.m.edit.ml_page = (if lh != 0 { vh / lh } else { 0 }).max(1);
823 let boff = if let Some(p) = presentation {
824 self.text.set_font(p.font, p.size);
825 p.baseline
826 } else {
827 self.ts_base(TS_BODY) };
829 let (font, size) = self.text.get_font();
830 self.m.edit.tb_font = font;
831 self.m.edit.tb_size = size;
832 let (mut sx, mut sy) = {
833 let s = self.slot(id);
834 (s.scrollx, s.scrolly)
835 };
836 self.tb_build_disp();
837 self.tbm_layout();
838 if editing && !self.spell_menu_open(id) {
840 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(sx);
841 let y = self.wp.ctx.my.wrapping_sub(top).wrapping_add(sy);
842 self.tbm_mouse(ev, x, y, lh);
843 self.m.edit.ml_lh = lh;
844 self.tb_keys();
845 self.tb_typed();
846 #[cfg(target_os = "android")]
847 self.tb_android_ime();
848 if self.m.edit.tb_chg {
849 self.m.edit.tb_ev |= EV_ACT;
850 self.repass();
851 self.tb_build_disp();
852 }
853 self.tbm_layout();
854 if !self.ed_is(id) {
855 editing = false; self.ime_off();
857 }
858 }
859 let ch = (self.m.edit.ml_start.len() as i32).wrapping_mul(lh);
861 let clamp_y = |y: i32| -> i32 {
862 let m = ch.wrapping_sub(vh).max(0);
863 y.min(m).max(0)
864 };
865 let wy = self.wp.ctx.wheel_y;
866 if ev & EV_HOVER != 0 && wy != 0 {
867 let d = wy.wrapping_mul(lh.wrapping_mul(3)) / 120;
868 let c = clamp_y(sy.wrapping_sub(d));
869 if c != sy {
870 sy = c;
871 self.wp.ctx.wheel_y = 0;
872 } }
874 let (_, pan) = self.pan_take(id);
876 sy = clamp_y(sy.wrapping_add(pan));
877 let hit_end = self.wp.ctx.hits[self.wp.ctx.hit_cur].len();
878 self.pan_view(id, hit_end.saturating_sub(1), super::touch::AX_V, [0, sy], [0, ch.wrapping_sub(vh).max(0)]);
879 let mut cl = 0;
881 let mut cx = 0;
882 if editing {
883 let caret = self.m.edit.ed_caret;
884 cl = self.tbm_line_of(caret);
885 let ls = self.m.edit.ml_start[cl];
886 cx = self.text.caret_x(&self.m.edit.buf[ls..], caret - ls);
887 let len = self.m.edit.buf.len() as i32;
889 let s = self.slot(id);
890 if caret as i32 != s.aux || len != s.fixed {
891 s.aux = caret as i32;
892 s.fixed = len;
893 let ct = (cl as i32).wrapping_mul(lh); if ct < sy {
895 sy = ct;
896 }
897 let cb = ct.wrapping_add(lh).wrapping_sub(vh);
898 if cb > sy {
899 sy = cb;
900 }
901 if self.m.edit.tb_flags & TB_WRAP == 0 {
902 let last = tw.wrapping_sub(hair); if cx.wrapping_sub(sx) > last {
904 sx = cx.wrapping_sub(last);
905 }
906 if cx < sx {
907 sx = cx;
908 }
909 }
910 }
911 }
912 sy = clamp_y(sy);
913 if self.m.edit.tb_flags & TB_WRAP != 0 {
914 sx = 0;
915 }
916 if sx < 0 {
917 sx = 0;
918 }
919 {
920 let s = self.slot(id);
921 s.scrollx = sx;
922 s.scrolly = sy;
923 }
924 sp.place(r);
926 sp.ev = ev;
927 sp.value = editing as i32;
928 sp.value2 = disabled as i32;
929 if let Some(p) = presentation {
930 self.draw_color(p.background);
931 self.draw_rect(r[0], r[1], r[2], r[3]);
932 } else {
933 st.textbox_frame(self, &sp);
934 }
935 let inset = if presentation.is_some() { 0 } else { hair };
937 self.draw_clip(x0, r[1].wrapping_add(inset), tw, r[3].wrapping_sub(inset).wrapping_sub(inset));
938 self.ltr_begin(x0, tw); let copy = ch > vh || self.m.edit.tb_flags & TB_WRAP == 0;
941 if copy {
942 self.scroll_copy_begin(id, x0.wrapping_sub(sx) as i64, top.wrapping_sub(sy) as i64);
943 }
944 let tcol = presentation.map_or_else(|| self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT }), |p| p.color);
945 let scol = self.color(T_ON_ACCENT); let bx = x0.wrapping_sub(sx); let spell = self.spell_scan(id); if self.m.edit.buf.is_empty() {
949 if let Some(p) = placeholder {
951 let c = if presentation.is_some() { 0xff87918f } else { self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 }) };
952 self.m.edit.tb_base = top.wrapping_sub(sy).wrapping_add(boff);
953 self.tb_emit_hint(x0, p, c, tw);
954 }
955 } else {
956 let (sa, sb) = if editing { self.tb_sel() } else { (0, 0) };
958 let n = self.m.edit.ml_start.len();
960 let first = if lh != 0 { sy / lh } else { 0 };
961 let mut last = (if lh != 0 { sy.wrapping_add(vh) / lh } else { 0 }).wrapping_add(1);
962 if last > n as i32 {
963 last = n as i32;
964 }
965 let mut l = first;
966 while l < last && (l as usize) < ML_MAX {
967 let ls = self.m.edit.ml_start[l as usize];
968 let le = self.tbm_line_end(l as usize);
969 let hy = self.m.edit.ml_hyph.get(l as usize).copied().unwrap_or(false);
970 let ly = l.wrapping_mul(lh).wrapping_add(top).wrapping_sub(sy); self.m.edit.tb_base = ly.wrapping_add(boff);
972 let s0 = sa.max(ls);
974 let s1 = sb.min(le);
975 let sel = sa != sb && (s0 < s1 || (s0 == s1 && sb > le));
976 if sel {
977 let x0r = self.text.caret_x(&self.m.edit.buf[ls..], s0 - ls);
978 let mut x1r = self.text.caret_x(&self.m.edit.buf[ls..], s1 - ls);
979 if sb > le {
980 x1r = x1r.wrapping_add(self.dp(4)); }
982 let c = self.color(T_ACCENT);
983 self.draw_color(c);
984 self.draw_rect(bx.wrapping_add(x0r), ly, x1r.wrapping_sub(x0r), lh);
985 self.tb_clip_x(r[0], bx.wrapping_add(x0r));
987 self.tbm_run(bx, ls, le, tcol, hy);
988 self.draw_unclip();
989 self.tb_clip_x(bx.wrapping_add(x0r), bx.wrapping_add(x1r));
990 self.tbm_run(bx, ls, le, scol, hy);
991 self.draw_unclip();
992 self.tb_clip_x(bx.wrapping_add(x1r), r[0].wrapping_add(r[2]));
993 self.tbm_run(bx, ls, le, tcol, hy);
994 self.draw_unclip();
995 } else {
996 self.tbm_run(bx, ls, le, tcol, hy);
997 }
998 if spell {
999 self.spell_draw(id, bx, ls, le, editing);
1000 }
1001 l += 1;
1002 }
1003 }
1004 if editing {
1005 let ly = (cl as i32).wrapping_mul(lh).wrapping_add(top).wrapping_sub(sy);
1006 let x = bx.wrapping_add(cx);
1007 if self.tb_caret_on() {
1008 let c = presentation.map_or_else(|| self.color(T_TEXT), |p| p.color);
1009 self.draw_color(c);
1010 self.draw_rect(x, ly, hair, lh);
1011 }
1012 plat::ime_inline(false); self.tb_purpose(false);
1014 self.ime_pos(x, ly, lh);
1015 }
1016 if copy {
1017 self.scroll_copy_end();
1018 }
1019 self.ltr_end();
1020 self.draw_unclip();
1021 if ch > vh {
1023 sp.place(r);
1024 sp.value = vh;
1025 sp.value2 = ch;
1026 sp.s[0] = sy;
1027 sp.s[1] = top;
1028 st.textbox_vbar(self, &sp);
1029 }
1030 if spell {
1031 let hit = (ev & EV_RCLICK != 0).then(|| {
1032 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(sx);
1033 let y = self.wp.ctx.my.wrapping_sub(top).wrapping_add(sy);
1034 self.m.edit.ml_lh = lh;
1035 self.tbm_hit(x, y)
1036 });
1037 self.spell_input(id, ev, hit, editing);
1038 }
1039 self.tb_finish(ev, editing)
1040 }
1041
1042 fn tbm_run(&mut self, x: i32, ls: usize, le: usize, c: u32, hy: bool) {
1044 let b = core::mem::take(&mut self.m.edit.buf);
1045 if hy {
1046 let mut t = b[ls..le].to_vec();
1047 t.push(b'-');
1048 self.tb_emit(x, &t, c);
1049 } else {
1050 self.tb_emit(x, &b[ls..le], c);
1051 }
1052 self.m.edit.buf = b;
1053 }
1054
1055 fn tbm_layout(&mut self) {
1060 if let Some(lines) = &self.m.edit.surface {
1061 self.m.edit.ml_start = lines.iter().map(|l| Self::tb_fix_index(&self.m.edit.buf, l.start)).collect();
1062 self.m.edit.ml_hyph.clear();
1063 return;
1064 }
1065 let len = self.m.edit.buf.len();
1066 let wrap = self.m.edit.tb_flags & TB_WRAP != 0;
1067 let mut starts = core::mem::take(&mut self.m.edit.ml_start);
1068 let mut hyph = core::mem::take(&mut self.m.edit.ml_hyph);
1069 starts.clear();
1070 hyph.clear();
1071 let mut pos = 0;
1072 'para: loop {
1073 let mut pe = pos;
1075 while pe < len && self.m.edit.buf[pe] != b'\n' {
1076 pe += 1;
1077 }
1078 loop {
1079 if starts.len() >= ML_MAX {
1080 break 'para; }
1082 starts.push(pos);
1083 hyph.push(false);
1084 if !wrap {
1085 break;
1086 }
1087 let (e, hy) = self.tbm_wrap(pos, pe);
1088 if e >= pe {
1089 break;
1090 }
1091 if let Some(h) = hyph.last_mut() {
1092 *h = hy;
1093 }
1094 pos = e; }
1096 if pe >= len {
1097 break;
1098 }
1099 pos = pe + 1; }
1101 self.m.edit.ml_start = starts;
1102 self.m.edit.ml_hyph = hyph;
1103 }
1104
1105 fn tbm_wrap(&mut self, pos: usize, pe: usize) -> (usize, bool) {
1109 if self.tw_fits(pos, pe - pos) {
1110 return (pe, false);
1111 }
1112 let w = self.m.edit.ml_w;
1113 let hw = self.text.width("-");
1114 let hyph = self.m.edit.hyph.clone();
1115 let text = &mut self.text;
1116 match core::str::from_utf8(&self.m.edit.buf) {
1117 Ok(s) => {
1118 let mut fits = |a: usize, b: usize, h: bool| text.width_n(&s.as_bytes()[a..], b - a) + if h { hw } else { 0 } <= w;
1119 match hyph.as_deref() {
1120 Some(hy) => segment::wrap_end_hyph(s, pos, pe, &mut fits, Some(&mut |wd: &str, out: &mut Vec<usize>| *out = hy.hyphenate(wd))),
1121 None => segment::wrap_end_hyph(s, pos, pe, &mut fits, None),
1122 }
1123 }
1124 Err(_) => {
1125 let buf = &self.m.edit.buf;
1127 let mut best = u8_next(buf, pos);
1128 while best < pe {
1129 let n = u8_next(buf, best);
1130 if text.width_n(&buf[pos..], n - pos) > w {
1131 break;
1132 }
1133 best = n;
1134 }
1135 (best, false)
1136 }
1137 }
1138 }
1139
1140 fn tw_fits(&mut self, pos: usize, n: usize) -> bool {
1142 self.text.width_n(&self.m.edit.buf[pos..], n) <= self.m.edit.ml_w
1143 }
1144
1145 fn tbm_line_of(&self, i: usize) -> usize {
1148 let st = &self.m.edit.ml_start;
1149 let mut l = st.len() as i32;
1150 loop {
1151 l -= 1;
1152 if l <= 0 {
1153 return 0;
1154 }
1155 if st[l as usize] <= i {
1156 return l as usize;
1157 }
1158 }
1159 }
1160
1161 fn tbm_line_end(&self, line: usize) -> usize {
1163 if let Some(lines) = &self.m.edit.surface { return Self::tb_fix_index(&self.m.edit.buf, lines[line].end); }
1164 let e = &self.m.edit;
1165 if line + 1 >= e.ml_start.len() {
1166 return e.buf.len();
1167 }
1168 let s = e.ml_start[line + 1];
1169 if s != 0 && e.buf[s - 1] == b'\n' { s - 1 } else { s }
1170 }
1171
1172 fn tbm_hit(&mut self, x: i32, y: i32) -> usize {
1175 let y = y.max(0);
1176 let lh = self.m.edit.ml_lh.max(1);
1177 let n = self.m.edit.ml_start.len() as i32;
1178 let l = (y / lh).min(n - 1).max(0) as usize;
1179 let ls = self.m.edit.ml_start[l];
1180 let le = self.tbm_line_end(l);
1181 let i = ls + self.text.hit(&self.m.edit.buf[ls..], Some(le - ls), x);
1182 self.tb_snap(i)
1183 }
1184
1185 fn tbm_mouse(&mut self, ev: u32, x: i32, y: i32, lh: i32) {
1187 self.m.edit.ml_lh = lh;
1188 let c = &self.wp.ctx;
1189 if c.pressed & (1 << BTN_LEFT) != 0 && ev & EV_HOVER != 0 && c.active == self.m.edit.tb_id {
1190 self.m.edit.ed_prefx = -1;
1191 let i = self.tbm_hit(x, y);
1192 self.tb_press(ev, i);
1193 } else if ev & EV_HELD != 0 {
1194 if self.m.edit.ed_drag {
1195 let i = self.tbm_hit(x, y);
1196 self.tb_drag_to(i);
1197 }
1198 } else {
1199 self.m.edit.ed_drag = false;
1200 }
1201 }
1202
1203 fn tbm_vmove(&mut self, lines: i32, shift: bool) {
1207 if self.m.edit.surface.is_some() { self.surface_vmove(lines, shift); return; }
1208 self.tbm_layout();
1209 let caret = self.m.edit.ed_caret;
1210 let cur = self.tbm_line_of(caret);
1211 let mut px = self.m.edit.ed_prefx;
1213 if px < 0 {
1214 let ls = self.m.edit.ml_start[cur];
1215 px = self.text.caret_x(&self.m.edit.buf[ls..], caret - ls);
1216 }
1217 let t = (cur as i32).wrapping_add(lines);
1218 let pos = if t < 0 {
1219 0 } else if t >= self.m.edit.ml_start.len() as i32 {
1221 self.m.edit.buf.len() } else {
1223 let ls = self.m.edit.ml_start[t as usize];
1224 let le = self.tbm_line_end(t as usize);
1225 let i = ls + self.text.hit(&self.m.edit.buf[ls..], Some(le - ls), px);
1226 self.tb_snap(i)
1227 };
1228 self.tb_move(pos, shift);
1229 self.m.edit.ed_prefx = px;
1230 }
1231
1232 fn tb_emit_hint(&mut self, x: i32, p: &str, c: u32, w: i32) {
1237 let e = &self.m.edit;
1238 self.text.set_font(e.tb_font, e.tb_size);
1239 if w > 0 && self.text.width(p) > w {
1240 let (cut, _) = self.ellipsize(p.as_bytes(), w);
1241 self.tb_emit(x, cut.as_bytes(), c);
1242 } else {
1243 self.tb_emit(x, p.as_bytes(), c);
1244 }
1245 }
1246
1247 fn tb_emit(&mut self, x: i32, s: &[u8], c: u32) {
1249 let e = &self.m.edit;
1250 let (y, font, size) = (e.tb_base, e.tb_font, e.tb_size);
1251 self.cmd_text(x, y, s, font, size, c, 0);
1252 if self.m.edit.presentation.is_some_and(|p| p.underline) {
1253 self.text.set_font(font, size);
1254 let width = (self.text.measure(core::str::from_utf8(s).unwrap_or("")) + 128) / 256;
1255 self.draw_color(c);
1256 self.draw_rect(x, y + (size / 256 / 12).max(1), width, (size / 256 / 16).max(1));
1257 }
1258 }
1259
1260 fn tb_emit_disp(&mut self, x: i32, c: u32) {
1262 let pw = self.m.edit.tb_flags & TB_PASSWORD != 0;
1263 let d = core::mem::take(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf });
1264 self.tb_emit(x, &d, c);
1265 *(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf }) = d;
1266 }
1267
1268 fn tb_clip_x(&mut self, x0: i32, x1: i32) {
1270 let r = self.wp.ctx.last_rect;
1271 self.draw_clip(x0, r[1], x1.wrapping_sub(x0), r[3]);
1272 }
1273
1274 fn tb_caret_on(&mut self) -> bool {
1276 let (a, b) = self.tb_sel();
1277 if a != b {
1278 return false; }
1280 if self.headless {
1281 return true; }
1283 let blink = self.blink_ms; if blink == 0 {
1285 return true;
1286 }
1287 let t = self.now_ms.wrapping_sub(self.m.edit.ed_t0);
1288 let phase = t / blink;
1289 self.timer(UIT_CARET, blink - t % blink);
1290 phase & 1 == 0
1291 }
1292
1293 pub fn ime_preedit(&self) -> plat::Preedit {
1298 self.ed_preedit()
1299 }
1300
1301 pub fn ime_enable(&mut self, on: bool, purpose: u32) {
1308 self.m.edit.pw_purpose = None; plat::ime_purpose(if on { purpose } else { plat::IP_TEXT });
1310 plat::ime_inline(on);
1311 if !on {
1312 self.ime_pos(0, 0, 0);
1313 }
1314 }
1315
1316 pub fn ime_pos(&mut self, x: i32, y: i32, h: i32) {
1319 let px = self.phys_x(x);
1320 self.a11y_caret(px, y, h);
1321 if !self.headless {
1322 plat::ime_pos(px, y, h);
1323 }
1324 self.m.edit.ime_frame = (h > 0).then_some(self.frame);
1325 }
1326
1327 #[cfg(target_os = "android")]
1330 pub(crate) fn ime_frame_end(&mut self) {
1331 if self.m.edit.ime_frame.is_some_and(|f| f != self.frame) {
1332 self.ime_off();
1333 }
1334 }
1335
1336 fn ime_off(&mut self) {
1338 #[cfg(target_os = "android")]
1339 plat::android_ime_clear();
1340 self.tb_purpose(false);
1341 self.ime_pos(0, 0, 0);
1342 }
1343
1344 fn tb_purpose(&mut self, pw: bool) {
1349 if pw {
1350 if self.m.edit.pw_purpose.is_none() {
1351 self.m.edit.pw_purpose = Some(plat::ime_purpose_of());
1352 }
1353 self.m.edit.pw_frame = self.frame;
1354 plat::ime_purpose(plat::IP_PASSWORD);
1355 } else if let Some(p) = self.m.edit.pw_purpose.take() {
1356 plat::ime_purpose(p);
1357 }
1358 }
1359
1360 pub(crate) fn pw_frame_end(&mut self) {
1364 if self.m.edit.pw_purpose.is_some() && self.m.edit.pw_frame != self.frame {
1365 self.tb_purpose(false);
1366 }
1367 }
1368
1369 pub(crate) fn tb_rekey(&mut self, old: u32, new: u32) {
1374 let w = self.uiw_cur;
1375 let e = &mut self.m.edit;
1376 if e.ed_id == old && e.ed_win == w {
1377 e.ed_id = new;
1378 }
1379 if e.ed_blur == old && e.ed_blur_win == w {
1380 e.ed_blur = new;
1381 }
1382 }
1383
1384 fn tb_focus(&mut self) -> bool {
1388 let id = self.m.edit.tb_id;
1389 let w = self.uiw_cur;
1390 if self.m.edit.ed_blur == id && self.m.edit.ed_blur_win == w {
1391 self.m.edit.ed_blur = 0;
1392 self.m.edit.tb_ev |= EV_COMMIT;
1393 }
1394 if self.wp.ctx.focus == id && self.wp.ctx.last_state != SS_DISABLED as u32 && w == self.m.win.active {
1397 let fv = self.wp.ctx.focus_visible;
1398 let e = &mut self.m.edit;
1399 if e.ed_id != id || e.ed_win != w {
1400 if e.ed_id != 0 {
1402 e.ed_blur = e.ed_id;
1403 e.ed_blur_win = e.ed_win;
1404 }
1405 e.ed_id = id;
1406 e.ed_win = w;
1407 e.ed_caret = e.buf.len();
1408 e.ed_anchor = e.buf.len();
1409 if fv {
1410 e.ed_anchor = 0; }
1412 e.ed_drag = false;
1413 self.ud_reset();
1414 self.tb_blink_reset();
1415 }
1416 self.m.edit.ed_seen = self.frame;
1417 let e = &mut self.m.edit;
1419 e.ed_caret = Self::tb_fix_index(&e.buf, e.ed_caret);
1420 e.ed_anchor = Self::tb_fix_index(&e.buf, e.ed_anchor);
1421 return true;
1422 }
1423 if self.ed_is(id) {
1424 self.m.edit.ed_id = 0;
1425 self.m.edit.tb_ev |= EV_COMMIT;
1426 self.ime_off();
1427 }
1428 false
1429 }
1430
1431 fn tb_fix_index(buf: &[u8], i: usize) -> usize {
1433 u8_clip(buf, i.min(buf.len()))
1434 }
1435
1436 fn tb_blink_reset(&mut self) {
1438 self.m.edit.ed_t0 = self.now_ms;
1439 }
1440
1441 fn tb_move(&mut self, pos: usize, shift: bool) {
1443 let e = &mut self.m.edit;
1444 e.ed_caret = pos;
1445 if !shift {
1446 e.ed_anchor = pos;
1447 }
1448 e.ed_merge = false;
1449 self.tb_blink_reset();
1450 }
1451
1452 fn tb_sel(&self) -> (usize, usize) {
1454 let e = &self.m.edit;
1455 if e.ed_caret <= e.ed_anchor { (e.ed_caret, e.ed_anchor) } else { (e.ed_anchor, e.ed_caret) }
1456 }
1457
1458 fn tb_press(&mut self, ev: u32, i: usize) {
1462 self.m.edit.ed_drag = true;
1463 self.m.edit.ed_merge = false;
1464 self.tb_blink_reset();
1465 if ev & EV_DOUBLE != 0 {
1466 self.tb_word_at(i);
1467 self.m.edit.ed_drag = false;
1468 return;
1469 }
1470 self.m.edit.ed_caret = i;
1471 if self.mods & MOD_SHIFT == 0 {
1472 self.m.edit.ed_anchor = i; }
1474 }
1475
1476 fn tb_drag_to(&mut self, i: usize) {
1478 if i != self.m.edit.ed_caret {
1479 self.m.edit.ed_caret = i;
1480 self.tb_blink_reset();
1481 }
1482 }
1483
1484 fn tb_mouse(&mut self, ev: u32, x: i32) {
1486 let c = &self.wp.ctx;
1487 if c.pressed & (1 << BTN_LEFT) != 0 && ev & EV_HOVER != 0 && c.active == self.m.edit.tb_id {
1488 let i = self.tb_hit(x);
1489 self.tb_press(ev, i);
1490 } else if ev & EV_HELD != 0 {
1491 if self.m.edit.ed_drag {
1492 let i = self.tb_hit(x);
1493 self.tb_drag_to(i);
1494 }
1495 } else {
1496 self.m.edit.ed_drag = false;
1497 }
1498 }
1499
1500 fn tb_hit(&mut self, x: i32) -> usize {
1502 let e = &self.m.edit;
1503 let d = disp_of(e);
1504 let i = self.text.hit(d, Some(d.len()), x);
1505 let i = self.tb_from_disp(i);
1506 self.tb_snap(i)
1507 }
1508
1509 fn tb_build_disp(&mut self) {
1513 let e = &mut self.m.edit;
1514 if e.tb_flags & TB_PASSWORD == 0 {
1515 return;
1516 }
1517 e.disp.clear();
1518 let mut i = 0;
1519 let mut n = 0;
1520 while at(&e.buf, i) != 0 && n < PW_MAX {
1521 e.disp.extend_from_slice(&[0xE2, 0x97, 0x8F]); n += 1;
1523 i = u8_next(&e.buf, i);
1524 }
1525 }
1526
1527 fn tb_to_disp(&self, i: usize) -> usize {
1529 let e = &self.m.edit;
1530 if e.tb_flags & TB_PASSWORD == 0 {
1531 return i;
1532 }
1533 let mut p = 0;
1534 let mut n = 0;
1535 while p < i && at(&e.buf, p) != 0 && n < PW_MAX {
1536 p = u8_next(&e.buf, p);
1537 n += 1;
1538 }
1539 n * 3
1540 }
1541
1542 fn tb_from_disp(&self, d: usize) -> usize {
1544 let e = &self.m.edit;
1545 if e.tb_flags & TB_PASSWORD == 0 {
1546 return d;
1547 }
1548 let mut n = d / 3;
1549 let mut p = 0;
1550 while n != 0 && at(&e.buf, p) != 0 {
1551 p = u8_next(&e.buf, p);
1552 n -= 1;
1553 }
1554 p
1555 }
1556
1557 fn tb_str(&self) -> Option<&str> {
1562 core::str::from_utf8(&self.m.edit.buf).ok()
1563 }
1564
1565 fn tb_prev(&self, i: usize) -> usize {
1567 match self.tb_str() {
1568 Some(s) => segment::grapheme_prev(s, i),
1569 None => u8_prev(&self.m.edit.buf, i),
1570 }
1571 }
1572
1573 fn tb_next(&self, i: usize) -> usize {
1574 match self.tb_str() {
1575 Some(s) => segment::grapheme_next(s, i),
1576 None => u8_next(&self.m.edit.buf, i),
1577 }
1578 }
1579
1580 fn tb_back(&self, i: usize) -> usize {
1583 let p = self.tb_prev(i);
1584 match self.tb_str() {
1585 Some(s) if !segment::pictographic_cluster(s, p, i) => u8_prev(&self.m.edit.buf, i).max(p),
1586 _ => p,
1587 }
1588 }
1589
1590 fn tb_snap(&self, i: usize) -> usize {
1592 match self.tb_str() {
1593 Some(s) if i < s.len() && !segment::is_grapheme_boundary(s, i) => segment::grapheme_prev(s, i),
1594 _ => i,
1595 }
1596 }
1597
1598 fn tb_word_left(&self, i: usize) -> usize {
1600 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1601 return 0;
1602 }
1603 match self.tb_str() {
1604 Some(s) => segment::word_prev(s, i),
1605 None => 0,
1606 }
1607 }
1608
1609 fn tb_word_right(&self, i: usize) -> usize {
1611 let len = self.m.edit.buf.len();
1612 if self.m.edit.tb_flags & TB_PASSWORD != 0 || i >= len {
1613 return len;
1614 }
1615 match self.tb_str() {
1616 Some(s) => segment::word_next(s, i),
1617 None => len,
1618 }
1619 }
1620
1621 fn tb_word_at(&mut self, i: usize) {
1623 let len = self.m.edit.buf.len();
1624 if len == 0 {
1625 self.m.edit.ed_anchor = 0;
1626 self.m.edit.ed_caret = 0;
1627 return;
1628 }
1629 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1630 self.m.edit.ed_anchor = 0;
1631 self.m.edit.ed_caret = len;
1632 return;
1633 }
1634 let (a, b) = match self.tb_str() {
1635 Some(s) => {
1636 let (mut a, mut b) = segment::word_at(s, i);
1637 let kind = segment::word_kind(s, a, b);
1638 if kind != segment::WK_WORD {
1639 while a > 0 {
1641 let (pa, pb) = segment::word_at(s, a - 1);
1642 if segment::word_kind(s, pa, pb) != kind || s[pa..pb].contains('\n') {
1643 break;
1644 }
1645 a = pa;
1646 }
1647 while b < s.len() {
1648 let (na, nb) = segment::word_at(s, b);
1649 if segment::word_kind(s, na, nb) != kind || s[na..nb].contains('\n') {
1650 break;
1651 }
1652 b = nb;
1653 }
1654 }
1655 (a, b)
1656 }
1657 None => (0, len),
1658 };
1659 self.m.edit.ed_anchor = a;
1660 self.m.edit.ed_caret = b;
1661 }
1662
1663 fn tb_delete(&mut self, a: usize, b: usize) {
1667 let e = &mut self.m.edit;
1668 let b = b.min(e.buf.len());
1669 if b <= a {
1670 return;
1671 }
1672 e.buf.drain(a..b);
1673 if e.surface.is_some() { e.surface_start = Some(e.surface_start.map_or(a, |v| v.min(a))); }
1674 e.tb_chg = true;
1675 }
1676
1677 fn tb_insert(&mut self, pos: usize, src: &[u8]) -> usize {
1679 let e = &mut self.m.edit;
1680 if e.tb_max <= e.buf.len() {
1681 return 0;
1682 }
1683 let room = e.tb_max - e.buf.len();
1684 let n = if src.len() > room { u8_clip(src, room) } else { src.len() };
1685 if n == 0 {
1686 return 0;
1687 }
1688 e.buf.splice(pos..pos, src[..n].iter().copied());
1689 if e.surface.is_some() { e.surface_start = Some(e.surface_start.map_or(pos, |v| v.min(pos))); }
1690 e.tb_chg = true;
1691 n
1692 }
1693
1694 fn tb_replace(&mut self, src: &[u8]) {
1696 let (a, b) = self.tb_sel();
1697 if self.m.edit.surface.is_some()
1700 && self.m.edit.buf.len().saturating_sub(b - a).saturating_add(src.len()) > self.m.edit.tb_max
1701 {
1702 return;
1703 }
1704 if a != b {
1705 self.tb_delete(a, b);
1706 }
1707 let mut p = a;
1708 self.m.edit.ed_caret = p;
1709 self.m.edit.ed_anchor = p;
1710 if src.is_empty() {
1711 return;
1712 }
1713 p += self.tb_insert(p, src);
1714 self.m.edit.ed_caret = p;
1715 self.m.edit.ed_anchor = p;
1716 }
1717
1718 fn tb_typed(&mut self) {
1720 let cps = self.text_take(TXQ_MAX);
1721 let flags = self.m.edit.tb_flags;
1722 if flags & TB_READONLY != 0 {
1723 return;
1724 }
1725 let mut stage = Vec::new();
1726 for &c in cps.iter() {
1727 if tb_accept(c, flags) {
1728 u8_push(&mut stage, c);
1729 }
1730 }
1731 if stage.is_empty() {
1732 return;
1733 }
1734 if !self.m.edit.ed_merge {
1735 self.ud_push(); }
1737 self.tb_replace(&stage);
1738 self.m.edit.ed_merge = true;
1739 self.tb_blink_reset();
1740 }
1741
1742 #[cfg(target_os = "android")]
1747 fn tb_android_ime(&mut self) {
1748 let Some((bytes, anchor, caret)) = plat::android_ime_take(self.m.edit.tb_id) else { return; };
1749 if self.m.edit.tb_flags & TB_READONLY != 0 { return; }
1750 let mut filtered = Vec::new();
1751 let mut positions = [0; 2];
1752 for (i, c) in core::str::from_utf8(&bytes).unwrap_or("").char_indices() {
1753 if tb_accept(c as u32, self.m.edit.tb_flags) { u8_push(&mut filtered, c as u32); }
1754 if i < anchor { positions[0] = filtered.len(); }
1755 if i < caret { positions[1] = filtered.len(); }
1756 }
1757 if filtered != self.m.edit.buf {
1758 if !self.m.edit.ed_merge { self.ud_push(); }
1759 self.m.edit.ed_anchor = 0;
1760 self.m.edit.ed_caret = self.m.edit.buf.len();
1761 self.tb_replace(&filtered);
1762 self.m.edit.ed_merge = true;
1763 }
1764 self.m.edit.ed_anchor = Self::tb_fix_index(&self.m.edit.buf, positions[0]);
1765 self.m.edit.ed_caret = Self::tb_fix_index(&self.m.edit.buf, positions[1]);
1766 self.m.edit.ed_prefx = -1;
1767 self.tb_blink_reset();
1768 self.repass();
1769 }
1770
1771 pub(crate) fn text_key_filter(&mut self, k: Option<usize>) -> Option<usize> {
1774 let i = k?;
1775 let e = &self.m.edit;
1776 if e.ed_self {
1777 return k; }
1779 if e.ed_id == 0 || e.ed_id != self.wp.ctx.focus || e.ed_win != self.uiw_cur {
1780 return k; }
1782 if self.frame.wrapping_sub(e.ed_seen) > 2 {
1783 return k; }
1785 if self.spell_menu_open(e.ed_id) {
1786 return k; }
1788 let q = self.wp.ctx.keyq[i];
1789 if tb_owns(q, e.ed_multi) && !self.tb_esc_passes(q) { None } else { k }
1790 }
1791
1792 fn tb_esc_passes(&self, k: u32) -> bool {
1795 k & 0xFFFF == VK_ESCAPE && self.wp.ctx.kb.soft != 0 && self.wp.ctx.kb.soft == self.wp.ctx.focus
1796 }
1797
1798 fn tb_keys(&mut self) {
1800 self.m.edit.ed_self = true;
1801 loop {
1802 if self.m.edit.ed_id != self.m.edit.tb_id {
1803 break;
1804 }
1805 let k = self.key_first();
1806 if k == 0 || !tb_owns(k, self.m.edit.ed_multi) || self.tb_esc_passes(k) {
1807 break;
1808 }
1809 self.key(k & 0xFFFF); let k = self.rtl_text_key(k); let vk = k & 0xFFFF;
1812 let m = k >> 16;
1813 let shift = m & MOD_SHIFT != 0;
1814 let ctrl = m & (MOD_ALT | MOD_ALTGR) == 0 && m & MOD_CTRL != 0; let flags = self.m.edit.tb_flags;
1816 if flags & TB_MULTI != 0 {
1817 let page = self.m.edit.ml_page;
1819 match vk {
1820 VK_UP => {
1821 self.tbm_vmove(-1, shift);
1822 continue;
1823 }
1824 VK_DOWN => {
1825 self.tbm_vmove(1, shift);
1826 continue;
1827 }
1828 VK_PRIOR => {
1829 self.tbm_vmove(page.wrapping_neg(), shift);
1830 continue;
1831 }
1832 VK_NEXT => {
1833 self.tbm_vmove(page, shift);
1834 continue;
1835 }
1836 VK_RETURN => {
1837 if ctrl {
1838 self.m.edit.tb_ev |= EV_COMMIT; } else if flags & TB_READONLY == 0 {
1840 self.m.edit.ed_prefx = -1;
1841 self.ud_push();
1842 self.tb_replace(b"\n");
1843 self.tb_blink_reset();
1844 }
1845 continue;
1846 }
1847 VK_HOME if !ctrl => {
1848 self.m.edit.ed_prefx = -1;
1849 self.tbm_layout();
1850 let l = self.tbm_line_of(self.m.edit.ed_caret);
1851 let p = self.m.edit.ml_start[l];
1852 self.tb_move(p, shift);
1853 continue;
1854 }
1855 VK_END if !ctrl => {
1856 self.m.edit.ed_prefx = -1;
1857 self.tbm_layout();
1858 let l = self.tbm_line_of(self.m.edit.ed_caret);
1859 let p = self.tbm_line_end(l);
1860 self.tb_move(p, shift);
1861 continue;
1862 }
1863 _ => {} }
1865 }
1866 self.m.edit.ed_prefx = -1;
1867 let len = self.m.edit.buf.len();
1868 let caret = self.m.edit.ed_caret;
1869 match vk {
1870 VK_LEFT => {
1871 let (a, b) = self.tb_sel();
1872 let p = if ctrl {
1873 self.tb_word_left(caret)
1874 } else if !shift && a != b {
1875 a } else {
1877 self.tb_prev(caret)
1878 };
1879 self.tb_move(p, shift);
1880 }
1881 VK_RIGHT => {
1882 let (a, b) = self.tb_sel();
1883 let p = if ctrl {
1884 self.tb_word_right(caret)
1885 } else if !shift && a != b {
1886 b } else {
1888 self.tb_next(caret)
1889 };
1890 self.tb_move(p, shift);
1891 }
1892 VK_HOME => self.tb_move(0, shift),
1893 VK_END => self.tb_move(len, shift),
1894 VK_BACK => {
1895 if flags & TB_READONLY != 0 {
1896 continue;
1897 }
1898 let (a, b) = self.tb_sel();
1899 if a != b {
1900 self.tb_delrange(a, b);
1901 } else {
1902 let s = if ctrl { self.tb_word_left(a) } else { self.tb_back(a) };
1903 self.tb_delrange(s, a);
1904 }
1905 }
1906 VK_DELETE => {
1907 if shift && !ctrl {
1908 self.tb_cut(); continue;
1910 }
1911 if flags & TB_READONLY != 0 {
1912 continue;
1913 }
1914 let (a, b) = self.tb_sel();
1915 if a != b {
1916 self.tb_delrange(a, b);
1917 } else {
1918 let e = if ctrl { self.tb_word_right(a) } else { self.tb_next(a) };
1919 self.tb_delrange(a, e);
1920 }
1921 }
1922 VK_INSERT => {
1923 if ctrl {
1924 self.tb_copy(); } else if shift && flags & TB_READONLY == 0 {
1926 self.tb_paste(); }
1928 }
1929 VK_RETURN => self.m.edit.tb_ev |= EV_COMMIT,
1930 VK_ESCAPE => {
1931 self.wp.ctx.focus = 0;
1932 self.m.edit.ed_id = 0;
1933 self.m.edit.tb_ev |= EV_COMMIT;
1934 self.repass();
1935 break;
1936 }
1937 _ if !ctrl => {} 65 => {
1939 let e = &mut self.m.edit;
1941 e.ed_anchor = 0;
1942 e.ed_caret = len;
1943 e.ed_merge = false;
1944 }
1945 67 => self.tb_copy(),
1946 88 => self.tb_cut(),
1947 86 => {
1948 if flags & TB_READONLY == 0 {
1949 self.tb_paste();
1950 }
1951 }
1952 90 | 89 => {
1953 if flags & TB_READONLY == 0 {
1955 if vk == 90 && !shift {
1956 self.ud_undo();
1957 } else {
1958 self.ud_redo();
1959 }
1960 }
1961 }
1962 _ => {}
1963 }
1964 }
1965 self.m.edit.ed_self = false;
1966 }
1967
1968 fn tb_delrange(&mut self, a: usize, b: usize) {
1970 if a >= b {
1971 return;
1972 }
1973 self.ud_push();
1974 self.tb_delete(a, b);
1975 self.tb_move(a, false);
1976 }
1977
1978 fn tb_cut(&mut self) {
1980 if self.m.edit.tb_flags & (TB_READONLY | TB_PASSWORD) != 0 {
1981 return;
1982 }
1983 self.tb_copy();
1984 let (a, b) = self.tb_sel();
1985 if a != b {
1986 self.tb_delrange(a, b);
1987 }
1988 }
1989
1990 fn tb_copy(&mut self) {
1994 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1995 return;
1996 }
1997 let (a, b) = self.tb_sel();
1998 if a == b {
1999 return;
2000 }
2001 let s = self.m.edit.buf[a..b].to_vec();
2002 self.clip_set(CLIP_TEXT, &s);
2003 }
2004
2005 fn tb_paste(&mut self) {
2008 let Some(clip) = self.clip_get(CLIP_TEXT) else { return };
2009 let flags = self.m.edit.tb_flags;
2010 let cap = clip.len() * 3; let mut out = Vec::with_capacity(cap + 4);
2012 let mut i = 0;
2013 loop {
2014 let (c, n) = u8_decode(&clip, i);
2015 if c == 0 {
2016 break;
2017 }
2018 i = n;
2019 if c == 10 {
2020 if flags & TB_MULTI == 0 || out.len() >= cap {
2021 break;
2022 }
2023 out.push(10); continue;
2025 }
2026 if c == 13 {
2027 if flags & TB_MULTI != 0 {
2028 if self.m.edit.surface.is_some() && clip.get(i) != Some(&10) {
2029 out.push(10); }
2031 continue; }
2033 break;
2034 }
2035 if tb_accept(c, flags) {
2036 u8_push(&mut out, c);
2037 }
2038 }
2039 if out.is_empty() {
2040 return;
2041 }
2042 self.ud_push();
2043 self.tb_replace(&out);
2044 self.m.edit.ed_merge = false;
2045 self.tb_blink_reset();
2046 }
2047
2048 pub fn clip_set_many(&mut self, entries: &[(u32, &[u8])]) -> i32 {
2051 if !plat::clip_many_valid(entries) { return -1; }
2052 if !self.headless && !self.hr_replaying() { return if plat::clip_set_many(entries) { 0 } else { -1 }; }
2054 let rich: Vec<_> = entries.iter().filter(|e| e.0 != CLIP_TEXT).map(|e| (e.0, e.1.to_vec())).collect();
2055 let text = entries.iter().find(|e| e.0 == CLIP_TEXT).map(|e| e.1.to_vec());
2056 self.hclip_img_clear();
2057 self.m.edit.hclip = text;
2058 self.m.edit.hclip_fmt = CLIP_TEXT;
2059 self.m.edit.hclip_rich = rich;
2060 0
2061 }
2062
2063 pub fn clip_set(&mut self, fmt: u32, data: &[u8]) -> i32 {
2065 if matches!(fmt, CLIP_HTML | CLIP_RTF | CLIP_DOCX) { return self.clip_set_many(&[(fmt, data)]); }
2066 if !self.headless && !self.hr_replaying() {
2067 return if plat::clip_set(fmt, data) { 0 } else { -1 };
2068 }
2069 if fmt != CLIP_TEXT {
2070 return self.hclip_img_set(fmt, data); }
2072 self.hclip_img_clear(); self.m.edit.hclip_rich.clear();
2074 self.m.edit.hclip = Some(data.to_vec());
2075 self.m.edit.hclip_fmt = fmt;
2076 0
2077 }
2078
2079 pub fn clip_get(&mut self, fmt: u32) -> Option<Vec<u8>> {
2081 if self.hr_on() {
2082 return self.hr_clip_get(fmt); }
2084 self.clip_read(fmt)
2085 }
2086
2087 pub(crate) fn clip_read(&mut self, fmt: u32) -> Option<Vec<u8>> {
2089 if !self.headless {
2090 return plat::clip_get(fmt);
2091 }
2092 if fmt != CLIP_TEXT {
2093 if matches!(fmt, CLIP_HTML | CLIP_RTF | CLIP_DOCX) {
2094 return self.m.edit.hclip_rich.iter().find(|e| e.0 == fmt).map(|e| e.1.clone());
2095 }
2096 return self.hclip_img_get(fmt); }
2098 match &self.m.edit.hclip {
2099 Some(d) if self.m.edit.hclip_fmt == fmt => Some(d.clone()),
2100 _ => None,
2101 }
2102 }
2103
2104 pub fn open_folder(&mut self, path: &str) -> i32 {
2108 if !self.headless && !self.hr_replaying() {
2109 return plat::open_folder(path);
2110 }
2111 plat::write_stdout(format!("ui_open_folder: {}\n", path).as_bytes());
2112 0
2113 }
2114
2115 pub fn open_url(&mut self, url: &str) -> i32 {
2118 if !self.headless && !self.hr_replaying() {
2119 return plat::open_url(url);
2120 }
2121 plat::write_stdout(format!("ui_open_url: {}\n", url).as_bytes());
2122 0
2123 }
2124
2125 pub(crate) fn open_doc_link(&mut self, url: &str) {
2129 let scheme = url.split_once(':').map_or("", |(s, _)| s);
2130 if ["http", "https", "mailto"].iter().any(|w| scheme.eq_ignore_ascii_case(w)) {
2131 self.open_url(url);
2132 }
2133 }
2134
2135 pub(crate) fn hclip_text_has(&self) -> bool {
2137 self.m.edit.hclip.is_some()
2138 }
2139
2140 pub(crate) fn hclip_text_clear(&mut self) {
2142 self.m.edit.hclip = None;
2143 self.m.edit.hclip_rich.clear();
2144 }
2145
2146 fn ud_reset(&mut self) {
2151 let e = &mut self.m.edit;
2152 e.ud_base = 0;
2153 e.ud_pos = 0;
2154 e.ud_count = 0;
2155 e.ed_merge = false;
2156 }
2157
2158 fn ud_slot(&self, i: usize) -> usize {
2160 (self.m.edit.ud_base + i) & (UD_N - 1)
2161 }
2162
2163 fn ud_room(&mut self) {
2165 let e = &mut self.m.edit;
2166 if e.ud_count < UD_N {
2167 return;
2168 }
2169 e.ud_base = (e.ud_base + 1) & (UD_N - 1);
2170 e.ud_count -= 1;
2171 if e.ud_pos != 0 {
2172 e.ud_pos -= 1;
2173 }
2174 }
2175
2176 fn ud_store(&mut self, i: usize) {
2178 let k = self.ud_slot(i);
2179 let e = &mut self.m.edit;
2180 e.ud_hdr[k] = [e.buf.len(), e.ed_caret, e.ed_anchor];
2181 e.ud_data[k].clear();
2182 e.ud_data[k].extend_from_slice(&e.buf);
2183 }
2184
2185 fn ud_restore(&mut self, i: usize) {
2187 let k = self.ud_slot(i);
2188 let e = &mut self.m.edit;
2189 let [len, caret, anchor] = e.ud_hdr[k];
2190 let n = len.min(e.tb_max);
2191 e.buf.clear();
2192 e.buf.extend_from_slice(&e.ud_data[k][..n]);
2193 if e.surface.is_some() { e.surface_start = Some(0); }
2194 e.tb_chg = true;
2195 e.ed_merge = false;
2196 e.ed_anchor = Self::tb_fix_index(&e.buf, anchor);
2197 e.ed_caret = Self::tb_fix_index(&e.buf, caret);
2198 self.tb_blink_reset();
2199 }
2200
2201 fn ud_push(&mut self) {
2203 self.m.edit.ed_merge = false;
2204 if self.m.edit.buf.len() >= UD_SEG {
2205 self.ud_reset(); return;
2207 }
2208 self.m.edit.ud_count = self.m.edit.ud_pos;
2209 self.ud_room();
2210 self.ud_store(self.m.edit.ud_pos);
2211 self.m.edit.ud_pos += 1;
2212 self.m.edit.ud_count = self.m.edit.ud_pos;
2213 }
2214
2215 fn ud_undo(&mut self) -> bool {
2217 if self.m.edit.ud_pos == 0 {
2218 return false;
2219 }
2220 if self.m.edit.ud_pos == self.m.edit.ud_count && self.m.edit.buf.len() < UD_SEG {
2221 self.ud_room();
2223 self.ud_store(self.m.edit.ud_count);
2224 self.m.edit.ud_count += 1;
2225 }
2226 self.m.edit.ud_pos -= 1;
2227 self.ud_restore(self.m.edit.ud_pos);
2228 true
2229 }
2230
2231 fn ud_redo(&mut self) -> bool {
2233 let p = self.m.edit.ud_pos + 1;
2234 if p >= self.m.edit.ud_count {
2235 return false;
2236 }
2237 self.m.edit.ud_pos = p;
2238 self.ud_restore(p);
2239 true
2240 }
2241
2242 #[cfg_attr(jfgui_dev, track_caller)]
2246 pub fn number_box(&mut self, id: u32, v: &mut i32, rg: &Range) -> u32 {
2247 let _src = self.src_scope();
2248 self.nb_pend_store(id, Some(&mut *v), None, Some(rg), None);
2249 let entry = *v;
2250 let mut deferred = 0;
2251 let step = if rg.step >= 1 { rg.step } else { 1 };
2252 let mut flags = NB_CAP | TB_DIGITS;
2253 if rg.min < 0 {
2254 flags |= TB_SIGNED;
2255 }
2256 if self.nb_pend_is(id) {
2258 self.m.edit.nb_pend = 0;
2259 deferred = self.m.edit.nb_pend_ev;
2260 }
2261 let view;
2262 if self.wp.ctx.focus == id && self.uiw_cur == self.m.win.active {
2264 if !self.nb_is(id) {
2265 self.nb_release(); let w = self.uiw_cur;
2267 let e = &mut self.m.edit;
2268 e.nb_id = id;
2269 e.nb_win = w;
2270 e.nb_kind = 0;
2271 e.nb_buf = nb_format(*v);
2272 self.nb_sync(*v as u32 as u64); }
2274 self.m.edit.nb_rg = *rg; self.m.edit.nb_seen = self.frame;
2276 self.nb_follow(*v as u32 as u64, 0); if self.ed_is(id) {
2279 let n = self.nb_step_keys(); if n != 0 {
2281 let d = n.wrapping_mul(step);
2282 let p = nb_parse_text(&self.m.edit.nb_buf, *v);
2283 self.nb_store(p.wrapping_add(d as i64), v);
2284 self.repass();
2285 }
2286 }
2287 view = false;
2288 } else if self.nb_is(id) {
2289 self.m.edit.nb_rg = *rg;
2290 view = false; } else {
2292 self.m.edit.nb_view = nb_format(*v);
2293 view = true;
2294 }
2295 let ev = self.nb_core(id, flags, view);
2296 if ev & EV_COMMIT != 0 && self.nb_is(id) {
2298 if self.nb_edited() {
2299 let p = nb_parse_text(&self.m.edit.nb_buf, *v);
2300 self.nb_store(p, v);
2301 self.repass();
2302 }
2303 if self.wp.ctx.focus != id {
2304 self.m.edit.nb_id = 0;
2305 }
2306 }
2307 let mut out = (ev & !EV_ACT) | deferred; if *v != entry {
2309 out |= EV_ACT;
2310 }
2311 self.tb_sem_set(*v, rg.max);
2312 out
2313 }
2314
2315 fn nb_core(&mut self, id: u32, flags: u32, view: bool) -> u32 {
2317 self.m.edit.tb_natw = NB_NAT_W;
2318 let mut b = core::mem::take(if view { &mut self.m.edit.nb_view } else { &mut self.m.edit.nb_buf });
2319 let ev = self.tb_core(id, None, &mut b, flags, 0);
2320 *(if view { &mut self.m.edit.nb_view } else { &mut self.m.edit.nb_buf }) = b;
2321 ev
2322 }
2323
2324 fn nb_step_keys(&mut self) -> i32 {
2327 self.m.edit.ed_self = true;
2328 let mut n = self.key(VK_UP) as i32;
2329 n -= self.key(VK_DOWN) as i32;
2330 n += self.key(VK_PRIOR) as i32 * 10;
2331 n -= self.key(VK_NEXT) as i32 * 10;
2332 self.m.edit.ed_self = false;
2333 n
2334 }
2335
2336 #[cfg_attr(jfgui_dev, track_caller)]
2338 pub fn number_box_f(&mut self, id: u32, v: &mut f64, rf: &RangeF) -> u32 {
2339 let _src = self.src_scope();
2340 self.nb_pend_store(id, None, Some(&mut *v), None, Some(rf));
2341 let entry = v.to_bits();
2342 let mut deferred = 0;
2343 let mut flags = NB_CAP | TB_DIGITS | TB_DECIMAL;
2344 if 0.0 > rf.min {
2345 flags |= TB_SIGNED;
2346 }
2347 if self.nb_pend_is(id) {
2348 self.m.edit.nb_pend = 0;
2349 deferred = self.m.edit.nb_pend_ev;
2350 }
2351 let view;
2352 if self.wp.ctx.focus == id && self.uiw_cur == self.m.win.active {
2353 if !self.nb_is(id) {
2355 self.nb_release();
2356 let t = self.nbf_format(*v, rf.dec);
2357 let w = self.uiw_cur;
2358 let e = &mut self.m.edit;
2359 e.nb_id = id;
2360 e.nb_win = w;
2361 e.nb_kind = 1;
2362 e.nb_buf = t;
2363 self.nb_sync(v.to_bits()); }
2365 self.m.edit.nb_rf = *rf;
2366 self.m.edit.nb_seen = self.frame;
2367 self.nb_follow(v.to_bits(), rf.dec); if self.ed_is(id) {
2369 let n = self.nb_step_keys();
2370 if n != 0 {
2371 let x = self.nbf_parse(*v);
2372 let mut step = rf.step;
2373 if !(step > 0.0) {
2374 step = (rf.max - rf.min) * 0.01;
2375 }
2376 let d = n as f64 * step;
2377 self.nbf_store(x + d, v);
2378 self.repass();
2379 }
2380 }
2381 view = false;
2382 } else if self.nb_is(id) {
2383 self.m.edit.nb_rf = *rf;
2384 view = false; } else {
2386 let t = self.nbf_format(*v, rf.dec);
2387 self.m.edit.nb_view = t;
2388 view = true;
2389 }
2390 let ev = self.nb_core(id, flags, view);
2391 if ev & EV_COMMIT != 0 && self.nb_is(id) {
2392 if self.nb_edited() {
2393 let x = self.nbf_parse(*v);
2395 self.nbf_store(x, v);
2396 self.repass();
2397 }
2398 if self.wp.ctx.focus != id {
2399 self.m.edit.nb_id = 0;
2400 }
2401 }
2402 let mut out = (ev & !EV_ACT) | deferred;
2403 if v.to_bits() != entry {
2404 out |= EV_ACT;
2405 }
2406 let sv = rf_scaled(rf, *v);
2407 let mx = rf_scaled(rf, rf.max);
2408 self.tb_sem_set(sv, mx);
2409 out
2410 }
2411
2412 #[cfg_attr(jfgui_dev, track_caller)]
2416 pub fn nb_live(&mut self, id: u32, v: &mut i32) -> bool {
2417 let _src = self.src_scope();
2418 if id == 0 || !self.nb_is(id) || !self.ed_is(id) || self.m.edit.nb_kind != 0 {
2419 return false;
2420 }
2421 let e = &self.m.edit;
2422 let x = rg_clamp(nb_parse_text(&e.nb_buf, *v), &e.nb_rg); if x == *v {
2424 return false;
2425 }
2426 *v = x;
2427 true
2428 }
2429
2430 #[cfg_attr(jfgui_dev, track_caller)]
2432 pub fn nb_live_f(&mut self, id: u32, v: &mut f64) -> bool {
2433 let _src = self.src_scope();
2434 if id == 0 || !self.nb_is(id) || !self.ed_is(id) || self.m.edit.nb_kind != 1 {
2435 return false;
2436 }
2437 let e = &self.m.edit;
2438 let Some(x) = parse_f64_sep(&e.nb_buf, self.m.value.decimal_sep) else { return false };
2439 let x = rf_fix(&e.nb_rf, x);
2440 if x.to_bits() == v.to_bits() {
2441 return false;
2442 }
2443 *v = x;
2444 true
2445 }
2446
2447 fn nbf_format(&self, v: f64, dec: u32) -> Vec<u8> {
2449 fmt_f64_sep(v, dec, self.m.value.decimal_sep).into_bytes()
2450 }
2451
2452 fn nbf_parse(&self, stored: f64) -> f64 {
2454 parse_f64_sep(&self.m.edit.nb_buf, self.m.value.decimal_sep).unwrap_or(stored)
2455 }
2456
2457 fn nbf_store(&mut self, x: f64, v: &mut f64) {
2459 let rf = self.m.edit.nb_rf;
2460 *v = rf_fix(&rf, x);
2461 let t = self.nbf_format(*v, rf.dec);
2462 self.m.edit.nb_buf = t;
2463 self.nb_sync(v.to_bits()); self.nb_caret_end();
2465 }
2466
2467 fn nb_store(&mut self, x: i64, v: &mut i32) {
2469 *v = rg_clamp(x, &self.m.edit.nb_rg);
2470 self.m.edit.nb_buf = nb_format(*v);
2471 self.nb_sync(*v as u32 as u64); self.nb_caret_end();
2473 }
2474
2475 fn nb_caret_end(&mut self) {
2477 let e = &mut self.m.edit;
2478 if e.nb_id != 0 && e.ed_id == e.nb_id && e.ed_win == e.nb_win {
2479 e.ed_caret = e.nb_buf.len();
2480 e.ed_anchor = e.nb_buf.len();
2481 }
2482 }
2483
2484 fn nb_sync(&mut self, shown: u64) {
2487 let e = &mut self.m.edit;
2488 e.nb_orig.clear();
2489 e.nb_orig.extend_from_slice(&e.nb_buf[..e.nb_buf.len().min(NB_CAP as usize - 1)]);
2490 e.nb_shown = shown;
2491 }
2492
2493 fn nb_edited(&self) -> bool {
2495 self.m.edit.nb_buf != self.m.edit.nb_orig
2496 }
2497
2498 fn nb_follow(&mut self, cur: u64, dec: u32) {
2503 if cur == self.m.edit.nb_shown || self.nb_edited() {
2504 return; }
2506 let e = &self.m.edit;
2507 let old = e.nb_buf.len();
2508 let (lo, hi) = if e.ed_caret >= e.ed_anchor { (e.ed_anchor, e.ed_caret) } else { (e.ed_caret, e.ed_anchor) };
2509 let all = lo == 0 && hi == old && old != 0;
2510 let t = if e.nb_kind == 0 { nb_format(cur as u32 as i32) } else { self.nbf_format(f64::from_bits(cur), dec) };
2511 self.m.edit.nb_buf = t;
2512 self.nb_sync(cur);
2513 let e = &mut self.m.edit;
2514 if e.ed_id == e.nb_id && e.ed_win == e.nb_win {
2515 e.ed_caret = e.nb_buf.len();
2516 e.ed_anchor = e.nb_buf.len();
2517 if all {
2518 e.ed_anchor = 0;
2519 }
2520 }
2521 }
2522
2523 fn nb_release(&mut self) {
2526 let old = self.m.edit.nb_id;
2527 if old == 0 {
2528 return;
2529 }
2530 self.m.edit.nb_id = 0;
2531 if self.frame.wrapping_sub(self.m.edit.nb_seen) > 2 {
2532 return; }
2534 if self.nb_edited() {
2535 let e = &mut self.m.edit;
2537 e.pend_text = Some((e.nb_win, old, e.nb_kind, e.nb_buf.clone()));
2538 self.repass();
2539 }
2540 self.m.edit.nb_pend = old;
2541 self.m.edit.nb_pend_win = self.m.edit.nb_win;
2542 self.m.edit.nb_pend_ev = EV_COMMIT;
2543 }
2544
2545 fn nb_pend_is(&self, id: u32) -> bool {
2547 let e = &self.m.edit;
2548 e.nb_pend == id && e.nb_pend_win == self.uiw_cur
2549 }
2550
2551 fn nb_pend_store(&mut self, id: u32, vi: Option<&mut i32>, vf: Option<&mut f64>, rg: Option<&Range>, rf: Option<&RangeF>) {
2554 match &self.m.edit.pend_text {
2555 Some((w, pid, _, _)) if *pid == id && *w == self.uiw_cur => {}
2556 _ => return,
2557 }
2558 let Some((_, _, kind, t)) = self.m.edit.pend_text.take() else { return };
2559 let changed = match (kind, vi, vf, rg, rf) {
2560 (0, Some(v), _, Some(rg), _) => {
2561 let old = *v;
2562 *v = rg_clamp(nb_parse_text(&t, *v), rg);
2563 *v != old
2564 }
2565 (1, _, Some(v), _, Some(rf)) => {
2566 let old = v.to_bits();
2567 let x = parse_f64_sep(&t, self.m.value.decimal_sep).unwrap_or(*v);
2568 *v = rf_fix(rf, x);
2569 v.to_bits() != old
2570 }
2571 _ => false,
2572 };
2573 if changed && self.nb_pend_is(id) {
2574 self.m.edit.nb_pend_ev |= EV_ACT;
2575 }
2576 }
2577}
2578
2579impl Ui {
2583 pub(crate) fn ed_hold(&mut self, id: u32, anchor: usize, caret: usize) {
2588 let w = self.uiw_cur;
2589 let e = &mut self.m.edit;
2590 if e.ed_id != id || e.ed_win != w {
2591 if e.ed_id != 0 {
2592 e.ed_blur = e.ed_id;
2593 e.ed_blur_win = e.ed_win;
2594 }
2595 e.ed_id = id;
2596 e.ed_win = w;
2597 e.ed_drag = false;
2598 self.ud_reset();
2599 }
2600 let frame = self.frame;
2601 let e = &mut self.m.edit;
2602 if e.ed_blur == id && e.ed_blur_win == w {
2603 e.ed_blur = 0;
2604 }
2605 e.ed_anchor = anchor;
2606 e.ed_caret = caret;
2607 e.ed_seen = frame;
2608 e.ed_multi = true;
2609 e.ed_prefx = -1;
2610 }
2611
2612 pub(crate) fn ed_drop(&mut self, id: u32) {
2615 if self.ed_is(id) {
2616 self.m.edit.ed_id = 0;
2617 self.ime_off();
2618 }
2619 }
2620}