1use alloc::format;
66use alloc::string::String;
67use alloc::vec::Vec;
68
69use crate::consts::*;
70use crate::keys::*;
71use crate::value::{fmt_f64_sep, parse_f64_sep, rf_fix, rf_scaled, RangeF};
72use crate::{Sp, Ui};
73use text::segment;
74#[path = "text_surface.rs"]
75mod surface;
76pub use surface::TextSurfaceLine;
77#[path = "edit_spell.rs"]
78mod spellbox;
79
80const TB_NAT_W: i32 = 200; const NB_NAT_W: i32 = 120; const TB_FLAG_MASK: u32 = 0xFF80_0000;
83const 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)]
94pub struct TextboxPresentation {
95 pub font: u32,
97 pub size: i32,
99 pub line_height: i32,
101 pub baseline: i32,
103 pub color: u32,
105 pub background: u32,
107 pub underline: bool,
109}
110const CTRL_KEYS: [u32; 13] = [65, 67, 88, 86, 90, 89, VK_LEFT, VK_RIGHT, VK_HOME, VK_END, VK_BACK, VK_DELETE, VK_INSERT];
112
113pub(crate) struct S {
115 pub(crate) hclip: Option<Vec<u8>>,
117 pub(crate) hclip_fmt: u32,
118 pub(crate) hclip_rich: Vec<(u32, Vec<u8>)>,
120 buf: Vec<u8>, disp: Vec<u8>, tb_id: u32,
124 tb_max: usize, tb_flags: u32,
126 tb_chg: bool,
127 tb_ev: u32,
128 tb_natw: i32,
129 tb_base: i32,
130 tb_font: u32,
131 tb_size: i32,
132 tb_sem: Option<usize>, presentation: Option<TextboxPresentation>,
134 surface: Option<Vec<TextSurfaceLine>>,
135 surface_start: Option<usize>,
136 surface_change: Option<(usize, usize, String)>,
137 ime_frame: Option<u32>, ed_id: u32, ed_blur: u32, ed_caret: usize,
142 ed_anchor: usize,
143 ed_t0: u32, ed_merge: bool, ed_drag: bool, ed_seen: u32, ed_self: bool, ed_multi: bool, ed_prefx: i32, ud_base: usize,
152 ud_pos: usize,
153 ud_count: usize,
154 ud_hdr: [[usize; 3]; UD_N], ud_data: [Vec<u8>; UD_N],
156 nb_id: u32, nb_seen: u32,
159 nb_pend: u32,
160 nb_pend_ev: u32,
161 nb_kind: u32, nb_rg: Range, nb_rf: RangeF,
164 nb_shown: u64, nb_buf: Vec<u8>,
166 nb_view: Vec<u8>,
167 nb_orig: Vec<u8>, pend_text: Option<(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,
178}
179
180impl Default for S {
181 fn default() -> S {
182 S {
183 hclip: None,
184 hclip_fmt: 0,
185 hclip_rich: Vec::new(),
186 buf: Vec::new(),
187 disp: Vec::new(),
188 tb_id: 0,
189 tb_max: 0,
190 tb_flags: 0,
191 tb_chg: false,
192 tb_ev: 0,
193 tb_natw: 0,
194 tb_base: 0,
195 tb_font: 0,
196 tb_size: 0,
197 tb_sem: None,
198 presentation: None,
199 surface: None,
200 surface_start: None,
201 surface_change: None,
202 ime_frame: None,
203 ed_id: 0,
204 ed_blur: 0,
205 ed_caret: 0,
206 ed_anchor: 0,
207 ed_t0: 0,
208 ed_merge: false,
209 ed_drag: false,
210 ed_seen: 0,
211 ed_self: false,
212 ed_multi: false,
213 ed_prefx: -1,
214 ud_base: 0,
215 ud_pos: 0,
216 ud_count: 0,
217 ud_hdr: [[0; 3]; UD_N],
218 ud_data: core::array::from_fn(|_| Vec::new()),
219 nb_id: 0,
220 nb_seen: 0,
221 nb_pend: 0,
222 nb_pend_ev: 0,
223 nb_kind: 0,
224 nb_rg: Range::default(),
225 nb_rf: RangeF::default(),
226 nb_shown: 0,
227 nb_buf: Vec::new(),
228 nb_view: Vec::new(),
229 nb_orig: Vec::new(),
230 pend_text: None,
231 ml_start: Vec::new(),
232 ml_hyph: Vec::new(),
233 hyph: None,
234 ml_w: 0,
235 ml_lh: 0,
236 ml_page: 0,
237 spell: Default::default(),
238 }
239 }
240}
241
242fn disp_of(e: &S) -> &[u8] {
244 if e.tb_flags & TB_PASSWORD != 0 { &e.disp } else { &e.buf }
245}
246
247fn at(s: &[u8], i: usize) -> u8 {
249 if i < s.len() { s[i] } else { 0 }
250}
251
252fn u8_decode(s: &[u8], i: usize) -> (u32, usize) {
256 let b0 = at(s, i) as u32;
257 if b0 < 0x80 {
258 return (b0, i + 1);
259 }
260 let (mut cp, n, min) = if b0 < 0xC2 {
261 return (0xFFFD, i + 1);
262 } else if b0 < 0xE0 {
263 (b0 & 0x1F, 1, 0x80)
264 } else if b0 < 0xF0 {
265 (b0 & 0x0F, 2, 0x800)
266 } else if b0 < 0xF5 {
267 (b0 & 7, 3, 0x10000)
268 } else {
269 return (0xFFFD, i + 1);
270 };
271 let mut j = i + 1;
272 for _ in 0..n {
273 let c = at(s, j) as u32;
274 if c & 0xC0 != 0x80 {
275 return (0xFFFD, i + 1);
276 }
277 cp = cp << 6 | (c & 0x3F);
278 j += 1;
279 }
280 if cp < min || cp > 0x10FFFF || cp & 0xFFFF_F800 == 0xD800 {
281 return (0xFFFD, i + 1);
282 }
283 (cp, j)
284}
285
286fn u8_next(s: &[u8], i: usize) -> usize {
288 if at(s, i) == 0 {
289 return i;
290 }
291 u8_decode(s, i).1
292}
293
294fn u8_prev(s: &[u8], i: usize) -> usize {
296 let mut p = i;
297 if p == 0 {
298 return 0;
299 }
300 loop {
301 p -= 1;
302 if p == 0 {
303 return 0;
304 }
305 if s[p] & 0xC0 != 0x80 {
306 return p;
307 }
308 }
309}
310
311fn u8_clip(s: &[u8], n: usize) -> usize {
313 let mut k = n;
314 while k != 0 && at(s, k) & 0xC0 == 0x80 {
315 k -= 1;
316 }
317 k
318}
319
320fn u8_push(out: &mut Vec<u8>, cp: u32) {
324 let c = if cp > 0x10FFFF || cp & 0xFFFF_F800 == 0xD800 { 0xFFFD } else { cp };
325 if c < 0x80 {
326 out.push(c as u8);
327 } else if c < 0x800 {
328 out.push(0xC0 | (c >> 6) as u8);
329 out.push(0x80 | (c & 0x3F) as u8);
330 } else if c < 0x10000 {
331 out.push(0xE0 | (c >> 12) as u8);
332 out.push(0x80 | ((c >> 6) & 0x3F) as u8);
333 out.push(0x80 | (c & 0x3F) as u8);
334 } else {
335 out.push(0xF0 | (c >> 18) as u8);
336 out.push(0x80 | ((c >> 12) & 0x3F) as u8);
337 out.push(0x80 | ((c >> 6) & 0x3F) as u8);
338 out.push(0x80 | (c & 0x3F) as u8);
339 }
340}
341
342fn to_string(mut b: Vec<u8>) -> String {
345 if let Some(p) = b.iter().position(|&c| c == 0) {
346 b.truncate(p);
347 }
348 match String::from_utf8(b) {
349 Ok(s) => s,
350 Err(e) => String::from_utf8_lossy(e.as_bytes()).into_owned(),
351 }
352}
353
354fn tb_owns(entry: u32, multi: bool) -> bool {
356 let vk = entry & 0xFF;
357 let m = entry >> 16;
358 if vk == VK_TAB {
359 return false;
360 }
361 if multi {
362 if vk == VK_UP || vk == VK_DOWN || vk.wrapping_sub(VK_PRIOR) <= VK_NEXT - VK_PRIOR {
364 return m & (MOD_ALT | MOD_CTRL) == 0;
365 }
366 if vk == VK_RETURN {
367 return m & MOD_ALT == 0;
368 }
369 }
370 if vk == VK_UP || vk == VK_DOWN || vk.wrapping_sub(VK_PRIOR) <= VK_NEXT - VK_PRIOR {
373 return false;
374 }
375 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
379 || vk.wrapping_sub(VK_LSHIFT) <= VK_RMENU - VK_LSHIFT
380 {
381 return false;
382 }
383 if m & MOD_ALTGR != 0 || m & (MOD_CTRL | MOD_ALT) == MOD_CTRL | MOD_ALT {
385 return true;
386 }
387 if m & MOD_ALT != 0 {
388 return false;
389 }
390 if m & MOD_CTRL == 0 {
391 return true;
392 }
393 CTRL_KEYS.contains(&vk)
395}
396
397fn tb_accept(cp: u32, flags: u32) -> bool {
399 if cp < 32 || cp == 127 {
400 return false;
401 }
402 if flags & TB_DIGITS == 0 || cp.wrapping_sub(48) <= 9 {
403 return true;
404 }
405 if flags & TB_DECIMAL != 0 && (cp == 44 || cp == 46) {
406 return true; }
408 cp == 45 && flags & TB_SIGNED != 0
409}
410
411fn nb_format(v: i32) -> Vec<u8> {
413 format!("{}", v).into_bytes()
414}
415
416fn nb_parse_text(t: &[u8], stored: i32) -> i64 {
419 let mut i = 0;
420 let neg = at(t, 0) == b'-';
421 if neg {
422 i = 1;
423 }
424 let mut v: i64 = 0;
425 let mut digits = 0;
426 while i < NB_CAP as usize {
427 let d = at(t, i).wrapping_sub(48);
428 if d > 9 {
429 break;
430 }
431 digits += 1;
432 v = v * 10 + d as i64;
433 if v as u64 > 0x8000_0000 {
434 v = 0x8000_0000;
435 }
436 i += 1;
437 }
438 if digits == 0 {
439 return stored as i64;
440 }
441 if neg { -v } else { v }
442}
443
444fn rg_clamp(v: i64, rg: &Range) -> i32 {
446 let mut c = v;
447 if c < rg.min as i64 {
448 c = rg.min as i64;
449 }
450 if c > rg.max as i64 {
451 c = rg.max as i64;
452 }
453 c as i32
454}
455
456impl Ui {
457 pub fn textbox_selection(&self, id: u32) -> Option<(usize, usize)> {
462 if self.m.edit.ed_id == id && self.focused() == id {
463 Some((self.m.edit.ed_anchor, self.m.edit.ed_caret))
464 } else { None }
465 }
466
467 pub fn textbox_select(&mut self, id: u32, text: &str, anchor: usize, caret: usize) -> bool {
470 if self.m.edit.ed_id != id || self.focused() != id || !text.is_char_boundary(anchor) || !text.is_char_boundary(caret) {
471 return false;
472 }
473 self.m.edit.ed_anchor = anchor;
474 self.m.edit.ed_caret = caret;
475 self.m.edit.ed_prefx = -1;
476 self.tb_blink_reset();
477 self.repass();
478 true
479 }
480
481 #[cfg_attr(jfgui_dev, track_caller)]
483 pub fn textbox(&mut self, id: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32) -> u32 {
484 let _src = self.src_scope();
485 self.textbox_icon(id, 0, placeholder, buf, cap_flags)
486 }
487
488 #[cfg_attr(jfgui_dev, track_caller)]
492 pub fn textbox_icon(&mut self, id: u32, icon: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32) -> u32 {
493 let _src = self.src_scope();
494 self.m.edit.tb_natw = TB_NAT_W;
495 let mut b = core::mem::take(buf).into_bytes();
496 let ev = self.tb_core(id, placeholder, &mut b, cap_flags, icon);
497 *buf = to_string(b);
498 ev
499 }
500
501 #[cfg_attr(jfgui_dev, track_caller)]
506 pub fn textbox_presented(&mut self, id: u32, placeholder: Option<&str>, buf: &mut String, cap_flags: u32, mut presentation: TextboxPresentation) -> u32 {
507 let _src = self.src_scope();
508 presentation.size = presentation.size.clamp(256, 512 * 256);
509 presentation.line_height = presentation.line_height.clamp(1, 2048);
510 presentation.baseline = presentation.baseline.clamp(0, presentation.line_height);
511 let previous = self.m.edit.presentation.replace(presentation);
512 let old_font = self.text.get_font();
513 let ev = self.textbox(id, placeholder, buf, cap_flags | TB_MULTI);
514 self.m.edit.presentation = previous;
515 self.text.set_font(old_font.0, old_font.1);
516 ev
517 }
518
519 fn tb_core(&mut self, id: u32, placeholder: Option<&str>, buf: &mut Vec<u8>, cap_flags: u32, icon: u32) -> u32 {
522 if let Some(p) = buf.iter().position(|&c| c == 0) {
523 buf.truncate(p); }
525 core::mem::swap(buf, &mut self.m.edit.buf);
526 let set = self.a11y_text(id).map(|t| {
528 let max = ((cap_flags & TB_CAP_MASK) as usize).saturating_sub(1);
529 let mut n = if max > 0 { t.len().min(max) } else { t.len() };
530 while !t.is_char_boundary(n) {
531 n -= 1;
532 }
533 let e = &mut self.m.edit;
534 e.buf = t.as_bytes()[..n].to_vec();
535 if e.ed_id == id {
536 e.ed_caret = n;
537 e.ed_anchor = n;
538 }
539 self.repass();
540 });
541 let ph = placeholder.map(|p| p.split('\0').next().unwrap_or(""));
542 let ev = if cap_flags & TB_MULTI != 0 {
543 self.tbm_core(id, ph, cap_flags)
544 } else {
545 self.tb_core1(id, ph, cap_flags, icon)
546 };
547 core::mem::swap(buf, &mut self.m.edit.buf);
548 if set.is_some() { ev | EV_ACT | EV_COMMIT } else { ev }
549 }
550
551 fn tb_start(&mut self, id: u32, cap_flags: u32, mask: u32) -> u32 {
553 let e = &mut self.m.edit;
554 e.tb_id = id;
555 e.tb_max = ((cap_flags & TB_CAP_MASK) as usize).saturating_sub(1);
556 e.tb_flags = cap_flags & mask;
557 e.tb_chg = false;
558 e.tb_ev = 0;
559 self.take_flags()
560 }
561
562 fn tb_core1(&mut self, id: u32, placeholder: Option<&str>, cap_flags: u32, icon: u32) -> u32 {
564 let stflags = self.tb_start(id, cap_flags, TB_FLAG_MASK);
565 let st = self.style;
567 let mut sp = Sp { value: self.m.edit.tb_natw, icon, flags: 0, ..Sp::default() };
568 let (w, h) = st.textbox_metrics(self, &mut sp);
569 let (w, h) = self.ctrl_size(w, h, stflags, false); if icon != 0 { sp.s[0] += self.dp(23); }
571 self.place(w, h);
572 let mut hb = HB_FOCUS | HB_TEXT | CUR_IBEAM << HB_CURSOR_SHIFT;
573 if stflags & ST_DISABLED != 0 {
574 hb |= HB_DISABLED;
575 }
576 let ev = self.behave(id, hb);
577 let disabled = self.wp.ctx.last_state == SS_DISABLED as u32;
578 let mut editing = self.tb_focus();
579 if editing {
580 self.m.edit.ed_multi = false; }
582 let hair = self.hair();
584 let r = self.wp.ctx.last_rect;
585 let x0 = sp.s[0].wrapping_add(r[0]);
586 let tw = r[2].wrapping_sub(sp.s[0].wrapping_add(sp.s[1])).max(1);
587 let mut scroll = self.slot(id).scrollx;
588 st.textbox_font(self, &sp);
589 self.tb_build_disp();
590 if editing && !self.spell_menu_open(id) {
592 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(scroll);
593 self.tb_mouse(ev, x);
594 self.tb_keys();
595 self.tb_typed();
596 #[cfg(target_os = "android")]
597 self.tb_android_ime();
598 if self.m.edit.tb_chg {
599 self.m.edit.tb_ev |= EV_ACT;
600 self.repass();
601 self.tb_build_disp();
602 }
603 if self.m.edit.ed_id != id {
604 editing = false; self.ime_off();
606 }
607 }
608 let (lh, base) = st.textbox_font(self, &sp);
610 let top = (r[3].wrapping_sub(lh) >> 1).wrapping_add(r[1]);
611 self.m.edit.tb_base = top.wrapping_add(base);
612 let (font, size) = self.text.get_font();
613 self.m.edit.tb_font = font;
614 self.m.edit.tb_size = size;
615 let last = tw.wrapping_sub(hair); let mut s = scroll;
618 let mut cx = 0;
619 if editing {
620 let d = self.tb_to_disp(self.m.edit.ed_caret);
621 cx = self.text.caret_x(disp_of(&self.m.edit), d);
622 if cx.wrapping_sub(s) > last {
623 s = cx.wrapping_sub(last);
624 }
625 if cx < s {
626 s = cx;
627 }
628 }
629 let dlen = disp_of(&self.m.edit).len();
631 let ex = self.text.caret_x(disp_of(&self.m.edit), dlen).wrapping_sub(last);
632 if s > ex {
633 s = ex;
634 }
635 if s < 0 {
636 s = 0;
637 }
638 scroll = s;
639 self.slot(id).scrollx = s;
640 sp.place(r);
642 sp.ev = ev;
643 sp.value = editing as i32;
644 sp.value2 = disabled as i32;
645 st.textbox_frame(self, &sp);
646 if icon != 0 {
647 let size = self.dp(16);
648 self.draw_color(self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 }));
649 self.draw_icon(icon, r[0] + self.dp(8), r[1] + (r[3] - size) / 2, size << 8);
650 }
651 self.draw_clip(x0, r[1], tw, r[3]);
653 self.ltr_begin(x0, tw);
654 let bx = x0.wrapping_sub(scroll); let tcol = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT });
656 if dlen == 0 {
657 if let Some(p) = placeholder {
659 let c = self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 });
660 self.tb_emit_hint(x0, p, c, tw);
661 }
662 } else {
663 let (a, b) = self.tb_sel();
664 if editing && a != b {
665 let da = self.tb_to_disp(a);
667 let sx0 = self.text.caret_x(disp_of(&self.m.edit), da);
668 let db = self.tb_to_disp(b);
669 let sx1 = self.text.caret_x(disp_of(&self.m.edit), db);
670 let c = self.color(T_ACCENT);
671 self.draw_color(c);
672 self.draw_rect(bx.wrapping_add(sx0), top, sx1.wrapping_sub(sx0), lh);
673 self.tb_clip_x(r[0], bx.wrapping_add(sx0)); self.tb_emit_disp(bx, tcol);
675 self.draw_unclip();
676 self.tb_clip_x(bx.wrapping_add(sx0), bx.wrapping_add(sx1)); let c = self.color(T_ON_ACCENT);
678 self.tb_emit_disp(bx, c);
679 self.draw_unclip();
680 self.tb_clip_x(bx.wrapping_add(sx1), r[0].wrapping_add(r[2])); self.tb_emit_disp(bx, tcol);
682 self.draw_unclip();
683 } else {
684 self.tb_emit_disp(bx, tcol);
685 }
686 }
687 let spell = self.spell_scan(id);
689 if spell && dlen != 0 {
690 self.spell_draw(id, bx, 0, dlen, editing);
691 }
692 if editing {
693 let x = bx.wrapping_add(cx);
694 if self.tb_caret_on() {
695 let c = self.color(T_TEXT);
696 self.draw_color(c);
697 self.draw_rect(x, top, hair, lh);
698 }
699 self.ime_pos(x, top, lh);
700 }
701 self.ltr_end();
702 self.draw_unclip();
703 if spell {
704 let hit = (ev & EV_RCLICK != 0).then(|| {
705 self.text.set_font(self.m.edit.tb_font, self.m.edit.tb_size);
706 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(scroll);
707 self.tb_hit(x)
708 });
709 self.spell_input(id, ev, hit, editing);
710 }
711 self.tb_finish(ev, editing)
712 }
713
714 fn tb_finish(&mut self, ev: u32, editing: bool) -> u32 {
716 #[cfg(target_os = "android")]
717 if editing {
718 let e = &self.m.edit;
719 plat::android_ime_publish(e.tb_id, &e.buf, e.ed_anchor, e.ed_caret, e.tb_flags);
720 }
721 if !editing {
722 self.wp.ctx.last_state &= !(SS_FOCUSED as u32);
723 }
724 let (v, mx) = if editing { (self.m.edit.ed_caret as i32, self.m.edit.ed_anchor as i32) } else { (0, 0) };
725 let pw = self.m.edit.tb_flags & TB_PASSWORD != 0;
726 let d = core::mem::take(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf });
727 let n0 = self.sems.len();
728 {
729 let l = String::from_utf8_lossy(&d);
730 let id = self.m.edit.tb_id;
731 let kind = self.m.edit.tb_flags & (TB_MULTI | TB_PASSWORD | TB_READONLY);
732 let s = self.sem(K_TEXTBOX, id, Some(l.as_ref()), v);
733 s.max = mx;
734 s.min = (kind >> 24) as i32; }
736 self.m.edit.tb_sem = if self.sems.len() > n0 { Some(n0) } else { None };
737 *(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf }) = d;
738 let mut out = ev & (EV_HOVER | EV_HELD | EV_DOUBLE | EV_RCLICK | EV_DRAG);
740 if editing {
741 out |= EV_FOCUS;
742 }
743 out | self.m.edit.tb_ev
744 }
745
746 fn tb_sem_set(&mut self, value: i32, max: i32) {
748 let r = match self.m.edit.tb_sem {
749 Some(i) if i < self.sems.len() => &mut self.sems[i],
750 _ => &mut self.sem_dummy,
751 };
752 r.value = value;
753 r.max = max;
754 }
755
756 fn tbm_core(&mut self, id: u32, placeholder: Option<&str>, cap_flags: u32) -> u32 {
760 let stflags = self.tb_start(id, cap_flags, TB_FLAG_MASK & !TB_PASSWORD);
761 let hy = cap_flags & (TB_HYPHENATE | TB_WRAP) == TB_HYPHENATE | TB_WRAP;
762 self.m.edit.hyph = self.hyph_take(hy); let presentation = self.m.edit.presentation;
765 let lh = presentation.map_or_else(|| self.ts_line(TS_BODY), |p| p.line_height);
766 let st = self.style;
767 let mut sp = Sp { value: self.m.edit.tb_natw, value2: lh, flags: 1, ..Sp::default() };
768 let (w, h) = if presentation.is_some() {
769 (self.dp(self.m.edit.tb_natw), lh.saturating_mul(5))
770 } else {
771 st.textbox_metrics(self, &mut sp)
772 };
773 self.place(w, h);
774 let mut hb = HB_FOCUS | HB_TEXT | CUR_IBEAM << HB_CURSOR_SHIFT;
775 if stflags & ST_DISABLED != 0 {
776 hb |= HB_DISABLED;
777 }
778 let ev = self.behave(id, hb);
779 let disabled = self.wp.ctx.last_state == SS_DISABLED as u32;
780 let mut editing = self.tb_focus();
781 if editing {
782 self.m.edit.ed_multi = true;
783 }
784 let hair = self.hair();
785 let r = self.wp.ctx.last_rect;
786 let x0 = sp.s[0].wrapping_add(r[0]);
787 let tw = r[2].wrapping_sub(sp.s[0].wrapping_add(sp.s[1])).max(1);
788 self.m.edit.ml_w = tw.wrapping_sub(hair); let top = r[1].wrapping_add(sp.s[2]);
790 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);
792 let boff = if let Some(p) = presentation {
793 self.text.set_font(p.font, p.size);
794 p.baseline
795 } else {
796 self.ts_base(TS_BODY) };
798 let (font, size) = self.text.get_font();
799 self.m.edit.tb_font = font;
800 self.m.edit.tb_size = size;
801 let (mut sx, mut sy) = {
802 let s = self.slot(id);
803 (s.scrollx, s.scrolly)
804 };
805 self.tb_build_disp();
806 self.tbm_layout();
807 if editing && !self.spell_menu_open(id) {
809 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(sx);
810 let y = self.wp.ctx.my.wrapping_sub(top).wrapping_add(sy);
811 self.tbm_mouse(ev, x, y, lh);
812 self.m.edit.ml_lh = lh;
813 self.tb_keys();
814 self.tb_typed();
815 #[cfg(target_os = "android")]
816 self.tb_android_ime();
817 if self.m.edit.tb_chg {
818 self.m.edit.tb_ev |= EV_ACT;
819 self.repass();
820 self.tb_build_disp();
821 }
822 self.tbm_layout();
823 if self.m.edit.ed_id != id {
824 editing = false; self.ime_off();
826 }
827 }
828 let ch = (self.m.edit.ml_start.len() as i32).wrapping_mul(lh);
830 let clamp_y = |y: i32| -> i32 {
831 let m = ch.wrapping_sub(vh).max(0);
832 y.min(m).max(0)
833 };
834 let wy = self.wp.ctx.wheel_y;
835 if ev & EV_HOVER != 0 && wy != 0 {
836 let d = wy.wrapping_mul(lh.wrapping_mul(3)) / 120;
837 let c = clamp_y(sy.wrapping_sub(d));
838 if c != sy {
839 sy = c;
840 self.wp.ctx.wheel_y = 0;
841 } }
843 let (_, pan) = self.pan_take(id);
845 sy = clamp_y(sy.wrapping_add(pan));
846 let hit_end = self.wp.ctx.hits[self.wp.ctx.hit_cur].len();
847 self.pan_view(id, hit_end.saturating_sub(1), super::touch::AX_V, [0, sy], [0, ch.wrapping_sub(vh).max(0)]);
848 let mut cl = 0;
850 let mut cx = 0;
851 if editing {
852 let caret = self.m.edit.ed_caret;
853 cl = self.tbm_line_of(caret);
854 let ls = self.m.edit.ml_start[cl];
855 cx = self.text.caret_x(&self.m.edit.buf[ls..], caret - ls);
856 let len = self.m.edit.buf.len() as i32;
858 let s = self.slot(id);
859 if caret as i32 != s.aux || len != s.fixed {
860 s.aux = caret as i32;
861 s.fixed = len;
862 let ct = (cl as i32).wrapping_mul(lh); if ct < sy {
864 sy = ct;
865 }
866 let cb = ct.wrapping_add(lh).wrapping_sub(vh);
867 if cb > sy {
868 sy = cb;
869 }
870 if self.m.edit.tb_flags & TB_WRAP == 0 {
871 let last = tw.wrapping_sub(hair); if cx.wrapping_sub(sx) > last {
873 sx = cx.wrapping_sub(last);
874 }
875 if cx < sx {
876 sx = cx;
877 }
878 }
879 }
880 }
881 sy = clamp_y(sy);
882 if self.m.edit.tb_flags & TB_WRAP != 0 {
883 sx = 0;
884 }
885 if sx < 0 {
886 sx = 0;
887 }
888 {
889 let s = self.slot(id);
890 s.scrollx = sx;
891 s.scrolly = sy;
892 }
893 sp.place(r);
895 sp.ev = ev;
896 sp.value = editing as i32;
897 sp.value2 = disabled as i32;
898 if let Some(p) = presentation {
899 self.draw_color(p.background);
900 self.draw_rect(r[0], r[1], r[2], r[3]);
901 } else {
902 st.textbox_frame(self, &sp);
903 }
904 let inset = if presentation.is_some() { 0 } else { hair };
906 self.draw_clip(x0, r[1].wrapping_add(inset), tw, r[3].wrapping_sub(inset).wrapping_sub(inset));
907 self.ltr_begin(x0, tw); let copy = ch > vh || self.m.edit.tb_flags & TB_WRAP == 0;
910 if copy {
911 self.scroll_copy_begin(id, x0.wrapping_sub(sx) as i64, top.wrapping_sub(sy) as i64);
912 }
913 let tcol = presentation.map_or_else(|| self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT }), |p| p.color);
914 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() {
918 if let Some(p) = placeholder {
920 let c = if presentation.is_some() { 0xff87918f } else { self.color(if disabled { T_TEXT_DISABLED } else { T_TEXT2 }) };
921 self.m.edit.tb_base = top.wrapping_sub(sy).wrapping_add(boff);
922 self.tb_emit_hint(x0, p, c, tw);
923 }
924 } else {
925 let (sa, sb) = if editing { self.tb_sel() } else { (0, 0) };
927 let n = self.m.edit.ml_start.len();
929 let first = if lh != 0 { sy / lh } else { 0 };
930 let mut last = (if lh != 0 { sy.wrapping_add(vh) / lh } else { 0 }).wrapping_add(1);
931 if last > n as i32 {
932 last = n as i32;
933 }
934 let mut l = first;
935 while l < last && (l as usize) < ML_MAX {
936 let ls = self.m.edit.ml_start[l as usize];
937 let le = self.tbm_line_end(l as usize);
938 let hy = self.m.edit.ml_hyph.get(l as usize).copied().unwrap_or(false);
939 let ly = l.wrapping_mul(lh).wrapping_add(top).wrapping_sub(sy); self.m.edit.tb_base = ly.wrapping_add(boff);
941 let s0 = sa.max(ls);
943 let s1 = sb.min(le);
944 let sel = sa != sb && (s0 < s1 || (s0 == s1 && sb > le));
945 if sel {
946 let x0r = self.text.caret_x(&self.m.edit.buf[ls..], s0 - ls);
947 let mut x1r = self.text.caret_x(&self.m.edit.buf[ls..], s1 - ls);
948 if sb > le {
949 x1r = x1r.wrapping_add(self.dp(4)); }
951 let c = self.color(T_ACCENT);
952 self.draw_color(c);
953 self.draw_rect(bx.wrapping_add(x0r), ly, x1r.wrapping_sub(x0r), lh);
954 self.tb_clip_x(r[0], bx.wrapping_add(x0r));
956 self.tbm_run(bx, ls, le, tcol, hy);
957 self.draw_unclip();
958 self.tb_clip_x(bx.wrapping_add(x0r), bx.wrapping_add(x1r));
959 self.tbm_run(bx, ls, le, scol, hy);
960 self.draw_unclip();
961 self.tb_clip_x(bx.wrapping_add(x1r), r[0].wrapping_add(r[2]));
962 self.tbm_run(bx, ls, le, tcol, hy);
963 self.draw_unclip();
964 } else {
965 self.tbm_run(bx, ls, le, tcol, hy);
966 }
967 if spell {
968 self.spell_draw(id, bx, ls, le, editing);
969 }
970 l += 1;
971 }
972 }
973 if editing {
974 let ly = (cl as i32).wrapping_mul(lh).wrapping_add(top).wrapping_sub(sy);
975 let x = bx.wrapping_add(cx);
976 if self.tb_caret_on() {
977 let c = presentation.map_or_else(|| self.color(T_TEXT), |p| p.color);
978 self.draw_color(c);
979 self.draw_rect(x, ly, hair, lh);
980 }
981 self.ime_pos(x, ly, lh);
982 }
983 if copy {
984 self.scroll_copy_end();
985 }
986 self.ltr_end();
987 self.draw_unclip();
988 if ch > vh {
990 sp.place(r);
991 sp.value = vh;
992 sp.value2 = ch;
993 sp.s[0] = sy;
994 sp.s[1] = top;
995 st.textbox_vbar(self, &sp);
996 }
997 if spell {
998 let hit = (ev & EV_RCLICK != 0).then(|| {
999 let x = self.rtl_map(self.wp.ctx.mx, x0, tw).wrapping_sub(x0).wrapping_add(sx);
1000 let y = self.wp.ctx.my.wrapping_sub(top).wrapping_add(sy);
1001 self.m.edit.ml_lh = lh;
1002 self.tbm_hit(x, y)
1003 });
1004 self.spell_input(id, ev, hit, editing);
1005 }
1006 self.tb_finish(ev, editing)
1007 }
1008
1009 fn tbm_run(&mut self, x: i32, ls: usize, le: usize, c: u32, hy: bool) {
1011 let b = core::mem::take(&mut self.m.edit.buf);
1012 if hy {
1013 let mut t = b[ls..le].to_vec();
1014 t.push(b'-');
1015 self.tb_emit(x, &t, c);
1016 } else {
1017 self.tb_emit(x, &b[ls..le], c);
1018 }
1019 self.m.edit.buf = b;
1020 }
1021
1022 fn tbm_layout(&mut self) {
1027 if let Some(lines) = &self.m.edit.surface {
1028 self.m.edit.ml_start = lines.iter().map(|l| Self::tb_fix_index(&self.m.edit.buf, l.start)).collect();
1029 self.m.edit.ml_hyph.clear();
1030 return;
1031 }
1032 let len = self.m.edit.buf.len();
1033 let wrap = self.m.edit.tb_flags & TB_WRAP != 0;
1034 let mut starts = core::mem::take(&mut self.m.edit.ml_start);
1035 let mut hyph = core::mem::take(&mut self.m.edit.ml_hyph);
1036 starts.clear();
1037 hyph.clear();
1038 let mut pos = 0;
1039 'para: loop {
1040 let mut pe = pos;
1042 while pe < len && self.m.edit.buf[pe] != b'\n' {
1043 pe += 1;
1044 }
1045 loop {
1046 if starts.len() >= ML_MAX {
1047 break 'para; }
1049 starts.push(pos);
1050 hyph.push(false);
1051 if !wrap {
1052 break;
1053 }
1054 let (e, hy) = self.tbm_wrap(pos, pe);
1055 if e >= pe {
1056 break;
1057 }
1058 if let Some(h) = hyph.last_mut() {
1059 *h = hy;
1060 }
1061 pos = e; }
1063 if pe >= len {
1064 break;
1065 }
1066 pos = pe + 1; }
1068 self.m.edit.ml_start = starts;
1069 self.m.edit.ml_hyph = hyph;
1070 }
1071
1072 fn tbm_wrap(&mut self, pos: usize, pe: usize) -> (usize, bool) {
1076 if self.tw_fits(pos, pe - pos) {
1077 return (pe, false);
1078 }
1079 let w = self.m.edit.ml_w;
1080 let hw = self.text.width("-");
1081 let hyph = self.m.edit.hyph.clone();
1082 let text = &mut self.text;
1083 match core::str::from_utf8(&self.m.edit.buf) {
1084 Ok(s) => {
1085 let mut fits = |a: usize, b: usize, h: bool| text.width_n(&s.as_bytes()[a..], b - a) + if h { hw } else { 0 } <= w;
1086 match hyph.as_deref() {
1087 Some(hy) => segment::wrap_end_hyph(s, pos, pe, &mut fits, Some(&mut |wd: &str, out: &mut Vec<usize>| *out = hy.hyphenate(wd))),
1088 None => segment::wrap_end_hyph(s, pos, pe, &mut fits, None),
1089 }
1090 }
1091 Err(_) => {
1092 let buf = &self.m.edit.buf;
1094 let mut best = u8_next(buf, pos);
1095 while best < pe {
1096 let n = u8_next(buf, best);
1097 if text.width_n(&buf[pos..], n - pos) > w {
1098 break;
1099 }
1100 best = n;
1101 }
1102 (best, false)
1103 }
1104 }
1105 }
1106
1107 fn tw_fits(&mut self, pos: usize, n: usize) -> bool {
1109 self.text.width_n(&self.m.edit.buf[pos..], n) <= self.m.edit.ml_w
1110 }
1111
1112 fn tbm_line_of(&self, i: usize) -> usize {
1115 let st = &self.m.edit.ml_start;
1116 let mut l = st.len() as i32;
1117 loop {
1118 l -= 1;
1119 if l <= 0 {
1120 return 0;
1121 }
1122 if st[l as usize] <= i {
1123 return l as usize;
1124 }
1125 }
1126 }
1127
1128 fn tbm_line_end(&self, line: usize) -> usize {
1130 if let Some(lines) = &self.m.edit.surface { return Self::tb_fix_index(&self.m.edit.buf, lines[line].end); }
1131 let e = &self.m.edit;
1132 if line + 1 >= e.ml_start.len() {
1133 return e.buf.len();
1134 }
1135 let s = e.ml_start[line + 1];
1136 if s != 0 && e.buf[s - 1] == b'\n' { s - 1 } else { s }
1137 }
1138
1139 fn tbm_hit(&mut self, x: i32, y: i32) -> usize {
1142 let y = y.max(0);
1143 let lh = self.m.edit.ml_lh.max(1);
1144 let n = self.m.edit.ml_start.len() as i32;
1145 let l = (y / lh).min(n - 1).max(0) as usize;
1146 let ls = self.m.edit.ml_start[l];
1147 let le = self.tbm_line_end(l);
1148 let i = ls + self.text.hit(&self.m.edit.buf[ls..], Some(le - ls), x);
1149 self.tb_snap(i)
1150 }
1151
1152 fn tbm_mouse(&mut self, ev: u32, x: i32, y: i32, lh: i32) {
1154 self.m.edit.ml_lh = lh;
1155 let c = &self.wp.ctx;
1156 if c.pressed & (1 << BTN_LEFT) != 0 && ev & EV_HOVER != 0 && c.active == self.m.edit.tb_id {
1157 self.m.edit.ed_prefx = -1;
1158 let i = self.tbm_hit(x, y);
1159 self.tb_press(ev, i);
1160 } else if ev & EV_HELD != 0 {
1161 if self.m.edit.ed_drag {
1162 let i = self.tbm_hit(x, y);
1163 self.tb_drag_to(i);
1164 }
1165 } else {
1166 self.m.edit.ed_drag = false;
1167 }
1168 }
1169
1170 fn tbm_vmove(&mut self, lines: i32, shift: bool) {
1174 if self.m.edit.surface.is_some() { self.surface_vmove(lines, shift); return; }
1175 self.tbm_layout();
1176 let caret = self.m.edit.ed_caret;
1177 let cur = self.tbm_line_of(caret);
1178 let mut px = self.m.edit.ed_prefx;
1180 if px < 0 {
1181 let ls = self.m.edit.ml_start[cur];
1182 px = self.text.caret_x(&self.m.edit.buf[ls..], caret - ls);
1183 }
1184 let t = (cur as i32).wrapping_add(lines);
1185 let pos = if t < 0 {
1186 0 } else if t >= self.m.edit.ml_start.len() as i32 {
1188 self.m.edit.buf.len() } else {
1190 let ls = self.m.edit.ml_start[t as usize];
1191 let le = self.tbm_line_end(t as usize);
1192 let i = ls + self.text.hit(&self.m.edit.buf[ls..], Some(le - ls), px);
1193 self.tb_snap(i)
1194 };
1195 self.tb_move(pos, shift);
1196 self.m.edit.ed_prefx = px;
1197 }
1198
1199 fn tb_emit_hint(&mut self, x: i32, p: &str, c: u32, w: i32) {
1204 let e = &self.m.edit;
1205 self.text.set_font(e.tb_font, e.tb_size);
1206 if w > 0 && self.text.width(p) > w {
1207 let (cut, _) = self.ellipsize(p.as_bytes(), w);
1208 self.tb_emit(x, cut.as_bytes(), c);
1209 } else {
1210 self.tb_emit(x, p.as_bytes(), c);
1211 }
1212 }
1213
1214 fn tb_emit(&mut self, x: i32, s: &[u8], c: u32) {
1216 let e = &self.m.edit;
1217 let (y, font, size) = (e.tb_base, e.tb_font, e.tb_size);
1218 self.cmd_text(x, y, s, font, size, c, 0);
1219 if self.m.edit.presentation.is_some_and(|p| p.underline) {
1220 self.text.set_font(font, size);
1221 let width = (self.text.measure(core::str::from_utf8(s).unwrap_or("")) + 128) / 256;
1222 self.draw_color(c);
1223 self.draw_rect(x, y + (size / 256 / 12).max(1), width, (size / 256 / 16).max(1));
1224 }
1225 }
1226
1227 fn tb_emit_disp(&mut self, x: i32, c: u32) {
1229 let pw = self.m.edit.tb_flags & TB_PASSWORD != 0;
1230 let d = core::mem::take(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf });
1231 self.tb_emit(x, &d, c);
1232 *(if pw { &mut self.m.edit.disp } else { &mut self.m.edit.buf }) = d;
1233 }
1234
1235 fn tb_clip_x(&mut self, x0: i32, x1: i32) {
1237 let r = self.wp.ctx.last_rect;
1238 self.draw_clip(x0, r[1], x1.wrapping_sub(x0), r[3]);
1239 }
1240
1241 fn tb_caret_on(&mut self) -> bool {
1243 let (a, b) = self.tb_sel();
1244 if a != b {
1245 return false; }
1247 if self.headless {
1248 return true; }
1250 let blink = self.blink_ms; if blink == 0 {
1252 return true;
1253 }
1254 let t = self.now_ms.wrapping_sub(self.m.edit.ed_t0);
1255 let phase = t / blink;
1256 self.timer(UIT_CARET, blink - t % blink);
1257 phase & 1 == 0
1258 }
1259
1260 pub fn ime_pos(&mut self, x: i32, y: i32, h: i32) {
1263 let px = self.phys_x(x);
1264 self.a11y_caret(px, y, h);
1265 if !self.headless {
1266 plat::ime_pos(px, y, h);
1267 }
1268 self.m.edit.ime_frame = (h > 0).then_some(self.frame);
1269 }
1270
1271 #[cfg(target_os = "android")]
1274 pub(crate) fn ime_frame_end(&mut self) {
1275 if self.m.edit.ime_frame.is_some_and(|f| f != self.frame) {
1276 self.ime_off();
1277 }
1278 }
1279
1280 fn ime_off(&mut self) {
1282 #[cfg(target_os = "android")]
1283 plat::android_ime_clear();
1284 self.ime_pos(0, 0, 0);
1285 }
1286
1287 pub(crate) fn tb_rekey(&mut self, old: u32, new: u32) {
1292 let e = &mut self.m.edit;
1293 if e.ed_id == old {
1294 e.ed_id = new;
1295 }
1296 if e.ed_blur == old {
1297 e.ed_blur = new;
1298 }
1299 }
1300
1301 fn tb_focus(&mut self) -> bool {
1304 let id = self.m.edit.tb_id;
1305 if self.m.edit.ed_blur == id {
1306 self.m.edit.ed_blur = 0;
1307 self.m.edit.tb_ev |= EV_COMMIT;
1308 }
1309 if self.wp.ctx.focus == id && self.wp.ctx.last_state != SS_DISABLED as u32 && self.uiw_cur == self.m.win.active {
1312 let fv = self.wp.ctx.focus_visible;
1313 let e = &mut self.m.edit;
1314 if e.ed_id != id {
1315 if e.ed_id != 0 {
1317 e.ed_blur = e.ed_id;
1318 }
1319 e.ed_id = id;
1320 e.ed_caret = e.buf.len();
1321 e.ed_anchor = e.buf.len();
1322 if fv {
1323 e.ed_anchor = 0; }
1325 e.ed_drag = false;
1326 self.ud_reset();
1327 self.tb_blink_reset();
1328 }
1329 self.m.edit.ed_seen = self.frame;
1330 let e = &mut self.m.edit;
1332 e.ed_caret = Self::tb_fix_index(&e.buf, e.ed_caret);
1333 e.ed_anchor = Self::tb_fix_index(&e.buf, e.ed_anchor);
1334 return true;
1335 }
1336 if self.m.edit.ed_id == id {
1337 self.m.edit.ed_id = 0;
1338 self.m.edit.tb_ev |= EV_COMMIT;
1339 self.ime_off();
1340 }
1341 false
1342 }
1343
1344 fn tb_fix_index(buf: &[u8], i: usize) -> usize {
1346 u8_clip(buf, i.min(buf.len()))
1347 }
1348
1349 fn tb_blink_reset(&mut self) {
1351 self.m.edit.ed_t0 = self.now_ms;
1352 }
1353
1354 fn tb_move(&mut self, pos: usize, shift: bool) {
1356 let e = &mut self.m.edit;
1357 e.ed_caret = pos;
1358 if !shift {
1359 e.ed_anchor = pos;
1360 }
1361 e.ed_merge = false;
1362 self.tb_blink_reset();
1363 }
1364
1365 fn tb_sel(&self) -> (usize, usize) {
1367 let e = &self.m.edit;
1368 if e.ed_caret <= e.ed_anchor { (e.ed_caret, e.ed_anchor) } else { (e.ed_anchor, e.ed_caret) }
1369 }
1370
1371 fn tb_press(&mut self, ev: u32, i: usize) {
1375 self.m.edit.ed_drag = true;
1376 self.m.edit.ed_merge = false;
1377 self.tb_blink_reset();
1378 if ev & EV_DOUBLE != 0 {
1379 self.tb_word_at(i);
1380 self.m.edit.ed_drag = false;
1381 return;
1382 }
1383 self.m.edit.ed_caret = i;
1384 if self.mods & MOD_SHIFT == 0 {
1385 self.m.edit.ed_anchor = i; }
1387 }
1388
1389 fn tb_drag_to(&mut self, i: usize) {
1391 if i != self.m.edit.ed_caret {
1392 self.m.edit.ed_caret = i;
1393 self.tb_blink_reset();
1394 }
1395 }
1396
1397 fn tb_mouse(&mut self, ev: u32, x: i32) {
1399 let c = &self.wp.ctx;
1400 if c.pressed & (1 << BTN_LEFT) != 0 && ev & EV_HOVER != 0 && c.active == self.m.edit.tb_id {
1401 let i = self.tb_hit(x);
1402 self.tb_press(ev, i);
1403 } else if ev & EV_HELD != 0 {
1404 if self.m.edit.ed_drag {
1405 let i = self.tb_hit(x);
1406 self.tb_drag_to(i);
1407 }
1408 } else {
1409 self.m.edit.ed_drag = false;
1410 }
1411 }
1412
1413 fn tb_hit(&mut self, x: i32) -> usize {
1415 let e = &self.m.edit;
1416 let d = disp_of(e);
1417 let i = self.text.hit(d, Some(d.len()), x);
1418 let i = self.tb_from_disp(i);
1419 self.tb_snap(i)
1420 }
1421
1422 fn tb_build_disp(&mut self) {
1426 let e = &mut self.m.edit;
1427 if e.tb_flags & TB_PASSWORD == 0 {
1428 return;
1429 }
1430 e.disp.clear();
1431 let mut i = 0;
1432 let mut n = 0;
1433 while at(&e.buf, i) != 0 && n < PW_MAX {
1434 e.disp.extend_from_slice(&[0xE2, 0x97, 0x8F]); n += 1;
1436 i = u8_next(&e.buf, i);
1437 }
1438 }
1439
1440 fn tb_to_disp(&self, i: usize) -> usize {
1442 let e = &self.m.edit;
1443 if e.tb_flags & TB_PASSWORD == 0 {
1444 return i;
1445 }
1446 let mut p = 0;
1447 let mut n = 0;
1448 while p < i && at(&e.buf, p) != 0 && n < PW_MAX {
1449 p = u8_next(&e.buf, p);
1450 n += 1;
1451 }
1452 n * 3
1453 }
1454
1455 fn tb_from_disp(&self, d: usize) -> usize {
1457 let e = &self.m.edit;
1458 if e.tb_flags & TB_PASSWORD == 0 {
1459 return d;
1460 }
1461 let mut n = d / 3;
1462 let mut p = 0;
1463 while n != 0 && at(&e.buf, p) != 0 {
1464 p = u8_next(&e.buf, p);
1465 n -= 1;
1466 }
1467 p
1468 }
1469
1470 fn tb_str(&self) -> Option<&str> {
1475 core::str::from_utf8(&self.m.edit.buf).ok()
1476 }
1477
1478 fn tb_prev(&self, i: usize) -> usize {
1480 match self.tb_str() {
1481 Some(s) => segment::grapheme_prev(s, i),
1482 None => u8_prev(&self.m.edit.buf, i),
1483 }
1484 }
1485
1486 fn tb_next(&self, i: usize) -> usize {
1487 match self.tb_str() {
1488 Some(s) => segment::grapheme_next(s, i),
1489 None => u8_next(&self.m.edit.buf, i),
1490 }
1491 }
1492
1493 fn tb_back(&self, i: usize) -> usize {
1496 let p = self.tb_prev(i);
1497 match self.tb_str() {
1498 Some(s) if !segment::pictographic_cluster(s, p, i) => u8_prev(&self.m.edit.buf, i).max(p),
1499 _ => p,
1500 }
1501 }
1502
1503 fn tb_snap(&self, i: usize) -> usize {
1505 match self.tb_str() {
1506 Some(s) if i < s.len() && !segment::is_grapheme_boundary(s, i) => segment::grapheme_prev(s, i),
1507 _ => i,
1508 }
1509 }
1510
1511 fn tb_word_left(&self, i: usize) -> usize {
1513 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1514 return 0;
1515 }
1516 match self.tb_str() {
1517 Some(s) => segment::word_prev(s, i),
1518 None => 0,
1519 }
1520 }
1521
1522 fn tb_word_right(&self, i: usize) -> usize {
1524 let len = self.m.edit.buf.len();
1525 if self.m.edit.tb_flags & TB_PASSWORD != 0 || i >= len {
1526 return len;
1527 }
1528 match self.tb_str() {
1529 Some(s) => segment::word_next(s, i),
1530 None => len,
1531 }
1532 }
1533
1534 fn tb_word_at(&mut self, i: usize) {
1536 let len = self.m.edit.buf.len();
1537 if len == 0 {
1538 self.m.edit.ed_anchor = 0;
1539 self.m.edit.ed_caret = 0;
1540 return;
1541 }
1542 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1543 self.m.edit.ed_anchor = 0;
1544 self.m.edit.ed_caret = len;
1545 return;
1546 }
1547 let (a, b) = match self.tb_str() {
1548 Some(s) => {
1549 let (mut a, mut b) = segment::word_at(s, i);
1550 let kind = segment::word_kind(s, a, b);
1551 if kind != segment::WK_WORD {
1552 while a > 0 {
1554 let (pa, pb) = segment::word_at(s, a - 1);
1555 if segment::word_kind(s, pa, pb) != kind || s[pa..pb].contains('\n') {
1556 break;
1557 }
1558 a = pa;
1559 }
1560 while b < s.len() {
1561 let (na, nb) = segment::word_at(s, b);
1562 if segment::word_kind(s, na, nb) != kind || s[na..nb].contains('\n') {
1563 break;
1564 }
1565 b = nb;
1566 }
1567 }
1568 (a, b)
1569 }
1570 None => (0, len),
1571 };
1572 self.m.edit.ed_anchor = a;
1573 self.m.edit.ed_caret = b;
1574 }
1575
1576 fn tb_delete(&mut self, a: usize, b: usize) {
1580 let e = &mut self.m.edit;
1581 let b = b.min(e.buf.len());
1582 if b <= a {
1583 return;
1584 }
1585 e.buf.drain(a..b);
1586 if e.surface.is_some() { e.surface_start = Some(e.surface_start.map_or(a, |v| v.min(a))); }
1587 e.tb_chg = true;
1588 }
1589
1590 fn tb_insert(&mut self, pos: usize, src: &[u8]) -> usize {
1592 let e = &mut self.m.edit;
1593 if e.tb_max <= e.buf.len() {
1594 return 0;
1595 }
1596 let room = e.tb_max - e.buf.len();
1597 let n = if src.len() > room { u8_clip(src, room) } else { src.len() };
1598 if n == 0 {
1599 return 0;
1600 }
1601 e.buf.splice(pos..pos, src[..n].iter().copied());
1602 if e.surface.is_some() { e.surface_start = Some(e.surface_start.map_or(pos, |v| v.min(pos))); }
1603 e.tb_chg = true;
1604 n
1605 }
1606
1607 fn tb_replace(&mut self, src: &[u8]) {
1609 let (a, b) = self.tb_sel();
1610 if self.m.edit.surface.is_some()
1613 && self.m.edit.buf.len().saturating_sub(b - a).saturating_add(src.len()) > self.m.edit.tb_max
1614 {
1615 return;
1616 }
1617 if a != b {
1618 self.tb_delete(a, b);
1619 }
1620 let mut p = a;
1621 self.m.edit.ed_caret = p;
1622 self.m.edit.ed_anchor = p;
1623 if src.is_empty() {
1624 return;
1625 }
1626 p += self.tb_insert(p, src);
1627 self.m.edit.ed_caret = p;
1628 self.m.edit.ed_anchor = p;
1629 }
1630
1631 fn tb_typed(&mut self) {
1633 let cps = self.text_take(TXQ_MAX);
1634 let flags = self.m.edit.tb_flags;
1635 if flags & TB_READONLY != 0 {
1636 return;
1637 }
1638 let mut stage = Vec::new();
1639 for &c in cps.iter() {
1640 if tb_accept(c, flags) {
1641 u8_push(&mut stage, c);
1642 }
1643 }
1644 if stage.is_empty() {
1645 return;
1646 }
1647 if !self.m.edit.ed_merge {
1648 self.ud_push(); }
1650 self.tb_replace(&stage);
1651 self.m.edit.ed_merge = true;
1652 self.tb_blink_reset();
1653 }
1654
1655 #[cfg(target_os = "android")]
1660 fn tb_android_ime(&mut self) {
1661 let Some((bytes, anchor, caret)) = plat::android_ime_take(self.m.edit.tb_id) else { return; };
1662 if self.m.edit.tb_flags & TB_READONLY != 0 { return; }
1663 let mut filtered = Vec::new();
1664 let mut positions = [0; 2];
1665 for (i, c) in core::str::from_utf8(&bytes).unwrap_or("").char_indices() {
1666 if tb_accept(c as u32, self.m.edit.tb_flags) { u8_push(&mut filtered, c as u32); }
1667 if i < anchor { positions[0] = filtered.len(); }
1668 if i < caret { positions[1] = filtered.len(); }
1669 }
1670 if filtered != self.m.edit.buf {
1671 if !self.m.edit.ed_merge { self.ud_push(); }
1672 self.m.edit.ed_anchor = 0;
1673 self.m.edit.ed_caret = self.m.edit.buf.len();
1674 self.tb_replace(&filtered);
1675 self.m.edit.ed_merge = true;
1676 }
1677 self.m.edit.ed_anchor = Self::tb_fix_index(&self.m.edit.buf, positions[0]);
1678 self.m.edit.ed_caret = Self::tb_fix_index(&self.m.edit.buf, positions[1]);
1679 self.m.edit.ed_prefx = -1;
1680 self.tb_blink_reset();
1681 self.repass();
1682 }
1683
1684 pub(crate) fn text_key_filter(&mut self, k: Option<usize>) -> Option<usize> {
1687 let i = k?;
1688 let e = &self.m.edit;
1689 if e.ed_self {
1690 return k; }
1692 if e.ed_id == 0 || e.ed_id != self.wp.ctx.focus {
1693 return k;
1694 }
1695 if self.frame.wrapping_sub(e.ed_seen) > 2 {
1696 return k; }
1698 if self.spell_menu_open(e.ed_id) {
1699 return k; }
1701 let q = self.wp.ctx.keyq[i];
1702 if tb_owns(q, e.ed_multi) && !self.tb_esc_passes(q) { None } else { k }
1703 }
1704
1705 fn tb_esc_passes(&self, k: u32) -> bool {
1708 k & 0xFFFF == VK_ESCAPE && self.wp.ctx.kb.soft != 0 && self.wp.ctx.kb.soft == self.wp.ctx.focus
1709 }
1710
1711 fn tb_keys(&mut self) {
1713 self.m.edit.ed_self = true;
1714 loop {
1715 if self.m.edit.ed_id != self.m.edit.tb_id {
1716 break;
1717 }
1718 let k = self.key_first();
1719 if k == 0 || !tb_owns(k, self.m.edit.ed_multi) || self.tb_esc_passes(k) {
1720 break;
1721 }
1722 self.key(k & 0xFFFF); let k = self.rtl_text_key(k); let vk = k & 0xFFFF;
1725 let m = k >> 16;
1726 let shift = m & MOD_SHIFT != 0;
1727 let ctrl = m & (MOD_ALT | MOD_ALTGR) == 0 && m & MOD_CTRL != 0; let flags = self.m.edit.tb_flags;
1729 if flags & TB_MULTI != 0 {
1730 let page = self.m.edit.ml_page;
1732 match vk {
1733 VK_UP => {
1734 self.tbm_vmove(-1, shift);
1735 continue;
1736 }
1737 VK_DOWN => {
1738 self.tbm_vmove(1, shift);
1739 continue;
1740 }
1741 VK_PRIOR => {
1742 self.tbm_vmove(page.wrapping_neg(), shift);
1743 continue;
1744 }
1745 VK_NEXT => {
1746 self.tbm_vmove(page, shift);
1747 continue;
1748 }
1749 VK_RETURN => {
1750 if ctrl {
1751 self.m.edit.tb_ev |= EV_COMMIT; } else if flags & TB_READONLY == 0 {
1753 self.m.edit.ed_prefx = -1;
1754 self.ud_push();
1755 self.tb_replace(b"\n");
1756 self.tb_blink_reset();
1757 }
1758 continue;
1759 }
1760 VK_HOME if !ctrl => {
1761 self.m.edit.ed_prefx = -1;
1762 self.tbm_layout();
1763 let l = self.tbm_line_of(self.m.edit.ed_caret);
1764 let p = self.m.edit.ml_start[l];
1765 self.tb_move(p, shift);
1766 continue;
1767 }
1768 VK_END if !ctrl => {
1769 self.m.edit.ed_prefx = -1;
1770 self.tbm_layout();
1771 let l = self.tbm_line_of(self.m.edit.ed_caret);
1772 let p = self.tbm_line_end(l);
1773 self.tb_move(p, shift);
1774 continue;
1775 }
1776 _ => {} }
1778 }
1779 self.m.edit.ed_prefx = -1;
1780 let len = self.m.edit.buf.len();
1781 let caret = self.m.edit.ed_caret;
1782 match vk {
1783 VK_LEFT => {
1784 let (a, b) = self.tb_sel();
1785 let p = if ctrl {
1786 self.tb_word_left(caret)
1787 } else if !shift && a != b {
1788 a } else {
1790 self.tb_prev(caret)
1791 };
1792 self.tb_move(p, shift);
1793 }
1794 VK_RIGHT => {
1795 let (a, b) = self.tb_sel();
1796 let p = if ctrl {
1797 self.tb_word_right(caret)
1798 } else if !shift && a != b {
1799 b } else {
1801 self.tb_next(caret)
1802 };
1803 self.tb_move(p, shift);
1804 }
1805 VK_HOME => self.tb_move(0, shift),
1806 VK_END => self.tb_move(len, shift),
1807 VK_BACK => {
1808 if flags & TB_READONLY != 0 {
1809 continue;
1810 }
1811 let (a, b) = self.tb_sel();
1812 if a != b {
1813 self.tb_delrange(a, b);
1814 } else {
1815 let s = if ctrl { self.tb_word_left(a) } else { self.tb_back(a) };
1816 self.tb_delrange(s, a);
1817 }
1818 }
1819 VK_DELETE => {
1820 if shift && !ctrl {
1821 self.tb_cut(); continue;
1823 }
1824 if flags & TB_READONLY != 0 {
1825 continue;
1826 }
1827 let (a, b) = self.tb_sel();
1828 if a != b {
1829 self.tb_delrange(a, b);
1830 } else {
1831 let e = if ctrl { self.tb_word_right(a) } else { self.tb_next(a) };
1832 self.tb_delrange(a, e);
1833 }
1834 }
1835 VK_INSERT => {
1836 if ctrl {
1837 self.tb_copy(); } else if shift && flags & TB_READONLY == 0 {
1839 self.tb_paste(); }
1841 }
1842 VK_RETURN => self.m.edit.tb_ev |= EV_COMMIT,
1843 VK_ESCAPE => {
1844 self.wp.ctx.focus = 0;
1845 self.m.edit.ed_id = 0;
1846 self.m.edit.tb_ev |= EV_COMMIT;
1847 self.repass();
1848 break;
1849 }
1850 _ if !ctrl => {} 65 => {
1852 let e = &mut self.m.edit;
1854 e.ed_anchor = 0;
1855 e.ed_caret = len;
1856 e.ed_merge = false;
1857 }
1858 67 => self.tb_copy(),
1859 88 => self.tb_cut(),
1860 86 => {
1861 if flags & TB_READONLY == 0 {
1862 self.tb_paste();
1863 }
1864 }
1865 90 | 89 => {
1866 if flags & TB_READONLY == 0 {
1868 if vk == 90 && !shift {
1869 self.ud_undo();
1870 } else {
1871 self.ud_redo();
1872 }
1873 }
1874 }
1875 _ => {}
1876 }
1877 }
1878 self.m.edit.ed_self = false;
1879 }
1880
1881 fn tb_delrange(&mut self, a: usize, b: usize) {
1883 if a >= b {
1884 return;
1885 }
1886 self.ud_push();
1887 self.tb_delete(a, b);
1888 self.tb_move(a, false);
1889 }
1890
1891 fn tb_cut(&mut self) {
1893 if self.m.edit.tb_flags & (TB_READONLY | TB_PASSWORD) != 0 {
1894 return;
1895 }
1896 self.tb_copy();
1897 let (a, b) = self.tb_sel();
1898 if a != b {
1899 self.tb_delrange(a, b);
1900 }
1901 }
1902
1903 fn tb_copy(&mut self) {
1907 if self.m.edit.tb_flags & TB_PASSWORD != 0 {
1908 return;
1909 }
1910 let (a, b) = self.tb_sel();
1911 if a == b {
1912 return;
1913 }
1914 let s = self.m.edit.buf[a..b].to_vec();
1915 self.clip_set(CLIP_TEXT, &s);
1916 }
1917
1918 fn tb_paste(&mut self) {
1921 let Some(clip) = self.clip_get(CLIP_TEXT) else { return };
1922 let flags = self.m.edit.tb_flags;
1923 let cap = clip.len() * 3; let mut out = Vec::with_capacity(cap + 4);
1925 let mut i = 0;
1926 loop {
1927 let (c, n) = u8_decode(&clip, i);
1928 if c == 0 {
1929 break;
1930 }
1931 i = n;
1932 if c == 10 {
1933 if flags & TB_MULTI == 0 || out.len() >= cap {
1934 break;
1935 }
1936 out.push(10); continue;
1938 }
1939 if c == 13 {
1940 if flags & TB_MULTI != 0 {
1941 if self.m.edit.surface.is_some() && clip.get(i) != Some(&10) {
1942 out.push(10); }
1944 continue; }
1946 break;
1947 }
1948 if tb_accept(c, flags) {
1949 u8_push(&mut out, c);
1950 }
1951 }
1952 if out.is_empty() {
1953 return;
1954 }
1955 self.ud_push();
1956 self.tb_replace(&out);
1957 self.m.edit.ed_merge = false;
1958 self.tb_blink_reset();
1959 }
1960
1961 pub fn clip_set_many(&mut self, entries: &[(u32, &[u8])]) -> i32 {
1964 if !plat::clip_many_valid(entries) { return -1; }
1965 if !self.headless && !self.hr_replaying() { return if plat::clip_set_many(entries) { 0 } else { -1 }; }
1967 let rich: Vec<_> = entries.iter().filter(|e| e.0 != CLIP_TEXT).map(|e| (e.0, e.1.to_vec())).collect();
1968 let text = entries.iter().find(|e| e.0 == CLIP_TEXT).map(|e| e.1.to_vec());
1969 self.hclip_img_clear();
1970 self.m.edit.hclip = text;
1971 self.m.edit.hclip_fmt = CLIP_TEXT;
1972 self.m.edit.hclip_rich = rich;
1973 0
1974 }
1975
1976 pub fn clip_set(&mut self, fmt: u32, data: &[u8]) -> i32 {
1978 if matches!(fmt, CLIP_HTML | CLIP_RTF | CLIP_DOCX) { return self.clip_set_many(&[(fmt, data)]); }
1979 if !self.headless && !self.hr_replaying() {
1980 return if plat::clip_set(fmt, data) { 0 } else { -1 };
1981 }
1982 if fmt != CLIP_TEXT {
1983 return self.hclip_img_set(fmt, data); }
1985 self.hclip_img_clear(); self.m.edit.hclip_rich.clear();
1987 self.m.edit.hclip = Some(data.to_vec());
1988 self.m.edit.hclip_fmt = fmt;
1989 0
1990 }
1991
1992 pub fn clip_get(&mut self, fmt: u32) -> Option<Vec<u8>> {
1994 if self.hr_on() {
1995 return self.hr_clip_get(fmt); }
1997 self.clip_read(fmt)
1998 }
1999
2000 pub(crate) fn clip_read(&mut self, fmt: u32) -> Option<Vec<u8>> {
2002 if !self.headless {
2003 return plat::clip_get(fmt);
2004 }
2005 if fmt != CLIP_TEXT {
2006 if matches!(fmt, CLIP_HTML | CLIP_RTF | CLIP_DOCX) {
2007 return self.m.edit.hclip_rich.iter().find(|e| e.0 == fmt).map(|e| e.1.clone());
2008 }
2009 return self.hclip_img_get(fmt); }
2011 match &self.m.edit.hclip {
2012 Some(d) if self.m.edit.hclip_fmt == fmt => Some(d.clone()),
2013 _ => None,
2014 }
2015 }
2016
2017 pub fn open_folder(&mut self, path: &str) -> i32 {
2021 if !self.headless && !self.hr_replaying() {
2022 return plat::open_folder(path);
2023 }
2024 plat::write_stdout(format!("ui_open_folder: {}\n", path).as_bytes());
2025 0
2026 }
2027
2028 pub fn open_url(&mut self, url: &str) -> i32 {
2031 if !self.headless && !self.hr_replaying() {
2032 return plat::open_url(url);
2033 }
2034 plat::write_stdout(format!("ui_open_url: {}\n", url).as_bytes());
2035 0
2036 }
2037
2038 pub(crate) fn hclip_text_has(&self) -> bool {
2040 self.m.edit.hclip.is_some()
2041 }
2042
2043 pub(crate) fn hclip_text_clear(&mut self) {
2045 self.m.edit.hclip = None;
2046 self.m.edit.hclip_rich.clear();
2047 }
2048
2049 fn ud_reset(&mut self) {
2054 let e = &mut self.m.edit;
2055 e.ud_base = 0;
2056 e.ud_pos = 0;
2057 e.ud_count = 0;
2058 e.ed_merge = false;
2059 }
2060
2061 fn ud_slot(&self, i: usize) -> usize {
2063 (self.m.edit.ud_base + i) & (UD_N - 1)
2064 }
2065
2066 fn ud_room(&mut self) {
2068 let e = &mut self.m.edit;
2069 if e.ud_count < UD_N {
2070 return;
2071 }
2072 e.ud_base = (e.ud_base + 1) & (UD_N - 1);
2073 e.ud_count -= 1;
2074 if e.ud_pos != 0 {
2075 e.ud_pos -= 1;
2076 }
2077 }
2078
2079 fn ud_store(&mut self, i: usize) {
2081 let k = self.ud_slot(i);
2082 let e = &mut self.m.edit;
2083 e.ud_hdr[k] = [e.buf.len(), e.ed_caret, e.ed_anchor];
2084 e.ud_data[k].clear();
2085 e.ud_data[k].extend_from_slice(&e.buf);
2086 }
2087
2088 fn ud_restore(&mut self, i: usize) {
2090 let k = self.ud_slot(i);
2091 let e = &mut self.m.edit;
2092 let [len, caret, anchor] = e.ud_hdr[k];
2093 let n = len.min(e.tb_max);
2094 e.buf.clear();
2095 e.buf.extend_from_slice(&e.ud_data[k][..n]);
2096 if e.surface.is_some() { e.surface_start = Some(0); }
2097 e.tb_chg = true;
2098 e.ed_merge = false;
2099 e.ed_anchor = Self::tb_fix_index(&e.buf, anchor);
2100 e.ed_caret = Self::tb_fix_index(&e.buf, caret);
2101 self.tb_blink_reset();
2102 }
2103
2104 fn ud_push(&mut self) {
2106 self.m.edit.ed_merge = false;
2107 if self.m.edit.buf.len() >= UD_SEG {
2108 self.ud_reset(); return;
2110 }
2111 self.m.edit.ud_count = self.m.edit.ud_pos;
2112 self.ud_room();
2113 self.ud_store(self.m.edit.ud_pos);
2114 self.m.edit.ud_pos += 1;
2115 self.m.edit.ud_count = self.m.edit.ud_pos;
2116 }
2117
2118 fn ud_undo(&mut self) -> bool {
2120 if self.m.edit.ud_pos == 0 {
2121 return false;
2122 }
2123 if self.m.edit.ud_pos == self.m.edit.ud_count && self.m.edit.buf.len() < UD_SEG {
2124 self.ud_room();
2126 self.ud_store(self.m.edit.ud_count);
2127 self.m.edit.ud_count += 1;
2128 }
2129 self.m.edit.ud_pos -= 1;
2130 self.ud_restore(self.m.edit.ud_pos);
2131 true
2132 }
2133
2134 fn ud_redo(&mut self) -> bool {
2136 let p = self.m.edit.ud_pos + 1;
2137 if p >= self.m.edit.ud_count {
2138 return false;
2139 }
2140 self.m.edit.ud_pos = p;
2141 self.ud_restore(p);
2142 true
2143 }
2144
2145 #[cfg_attr(jfgui_dev, track_caller)]
2149 pub fn number_box(&mut self, id: u32, v: &mut i32, rg: &Range) -> u32 {
2150 let _src = self.src_scope();
2151 self.nb_pend_store(id, Some(&mut *v), None, Some(rg), None);
2152 let entry = *v;
2153 let mut deferred = 0;
2154 let step = if rg.step >= 1 { rg.step } else { 1 };
2155 let mut flags = NB_CAP | TB_DIGITS;
2156 if rg.min < 0 {
2157 flags |= TB_SIGNED;
2158 }
2159 if self.m.edit.nb_pend == id {
2161 self.m.edit.nb_pend = 0;
2162 deferred = self.m.edit.nb_pend_ev;
2163 }
2164 let view;
2165 if self.wp.ctx.focus == id && self.uiw_cur == self.m.win.active {
2167 if self.m.edit.nb_id != id {
2168 self.nb_release(); let e = &mut self.m.edit;
2170 e.nb_id = id;
2171 e.nb_kind = 0;
2172 e.nb_buf = nb_format(*v);
2173 self.nb_sync(*v as u32 as u64); }
2175 self.m.edit.nb_rg = *rg; self.m.edit.nb_seen = self.frame;
2177 self.nb_follow(*v as u32 as u64, 0); if self.m.edit.ed_id == id {
2180 let n = self.nb_step_keys(); if n != 0 {
2182 let d = n.wrapping_mul(step);
2183 let p = nb_parse_text(&self.m.edit.nb_buf, *v);
2184 self.nb_store(p.wrapping_add(d as i64), v);
2185 self.repass();
2186 }
2187 }
2188 view = false;
2189 } else if self.m.edit.nb_id == id {
2190 self.m.edit.nb_rg = *rg;
2191 view = false; } else {
2193 self.m.edit.nb_view = nb_format(*v);
2194 view = true;
2195 }
2196 let ev = self.nb_core(id, flags, view);
2197 if ev & EV_COMMIT != 0 && self.m.edit.nb_id == id {
2199 if self.nb_edited() {
2200 let p = nb_parse_text(&self.m.edit.nb_buf, *v);
2201 self.nb_store(p, v);
2202 self.repass();
2203 }
2204 if self.wp.ctx.focus != id {
2205 self.m.edit.nb_id = 0;
2206 }
2207 }
2208 let mut out = (ev & !EV_ACT) | deferred; if *v != entry {
2210 out |= EV_ACT;
2211 }
2212 self.tb_sem_set(*v, rg.max);
2213 out
2214 }
2215
2216 fn nb_core(&mut self, id: u32, flags: u32, view: bool) -> u32 {
2218 self.m.edit.tb_natw = NB_NAT_W;
2219 let mut b = core::mem::take(if view { &mut self.m.edit.nb_view } else { &mut self.m.edit.nb_buf });
2220 let ev = self.tb_core(id, None, &mut b, flags, 0);
2221 *(if view { &mut self.m.edit.nb_view } else { &mut self.m.edit.nb_buf }) = b;
2222 ev
2223 }
2224
2225 fn nb_step_keys(&mut self) -> i32 {
2228 self.m.edit.ed_self = true;
2229 let mut n = self.key(VK_UP) as i32;
2230 n -= self.key(VK_DOWN) as i32;
2231 n += self.key(VK_PRIOR) as i32 * 10;
2232 n -= self.key(VK_NEXT) as i32 * 10;
2233 self.m.edit.ed_self = false;
2234 n
2235 }
2236
2237 #[cfg_attr(jfgui_dev, track_caller)]
2239 pub fn number_box_f(&mut self, id: u32, v: &mut f64, rf: &RangeF) -> u32 {
2240 let _src = self.src_scope();
2241 self.nb_pend_store(id, None, Some(&mut *v), None, Some(rf));
2242 let entry = v.to_bits();
2243 let mut deferred = 0;
2244 let mut flags = NB_CAP | TB_DIGITS | TB_DECIMAL;
2245 if 0.0 > rf.min {
2246 flags |= TB_SIGNED;
2247 }
2248 if self.m.edit.nb_pend == id {
2249 self.m.edit.nb_pend = 0;
2250 deferred = self.m.edit.nb_pend_ev;
2251 }
2252 let view;
2253 if self.wp.ctx.focus == id && self.uiw_cur == self.m.win.active {
2254 if self.m.edit.nb_id != id {
2256 self.nb_release();
2257 let t = self.nbf_format(*v, rf.dec);
2258 let e = &mut self.m.edit;
2259 e.nb_id = id;
2260 e.nb_kind = 1;
2261 e.nb_buf = t;
2262 self.nb_sync(v.to_bits()); }
2264 self.m.edit.nb_rf = *rf;
2265 self.m.edit.nb_seen = self.frame;
2266 self.nb_follow(v.to_bits(), rf.dec); if self.m.edit.ed_id == id {
2268 let n = self.nb_step_keys();
2269 if n != 0 {
2270 let x = self.nbf_parse(*v);
2271 let mut step = rf.step;
2272 if !(step > 0.0) {
2273 step = (rf.max - rf.min) * 0.01;
2274 }
2275 let d = n as f64 * step;
2276 self.nbf_store(x + d, v);
2277 self.repass();
2278 }
2279 }
2280 view = false;
2281 } else if self.m.edit.nb_id == id {
2282 self.m.edit.nb_rf = *rf;
2283 view = false; } else {
2285 let t = self.nbf_format(*v, rf.dec);
2286 self.m.edit.nb_view = t;
2287 view = true;
2288 }
2289 let ev = self.nb_core(id, flags, view);
2290 if ev & EV_COMMIT != 0 && self.m.edit.nb_id == id {
2291 if self.nb_edited() {
2292 let x = self.nbf_parse(*v);
2294 self.nbf_store(x, v);
2295 self.repass();
2296 }
2297 if self.wp.ctx.focus != id {
2298 self.m.edit.nb_id = 0;
2299 }
2300 }
2301 let mut out = (ev & !EV_ACT) | deferred;
2302 if v.to_bits() != entry {
2303 out |= EV_ACT;
2304 }
2305 let sv = rf_scaled(rf, *v);
2306 let mx = rf_scaled(rf, rf.max);
2307 self.tb_sem_set(sv, mx);
2308 out
2309 }
2310
2311 #[cfg_attr(jfgui_dev, track_caller)]
2315 pub fn nb_live(&mut self, id: u32, v: &mut i32) -> bool {
2316 let _src = self.src_scope();
2317 let e = &self.m.edit;
2318 if id == 0 || e.nb_id != id || e.ed_id != id || e.nb_kind != 0 {
2319 return false;
2320 }
2321 let x = rg_clamp(nb_parse_text(&e.nb_buf, *v), &e.nb_rg); if x == *v {
2323 return false;
2324 }
2325 *v = x;
2326 true
2327 }
2328
2329 #[cfg_attr(jfgui_dev, track_caller)]
2331 pub fn nb_live_f(&mut self, id: u32, v: &mut f64) -> bool {
2332 let _src = self.src_scope();
2333 let e = &self.m.edit;
2334 if id == 0 || e.nb_id != id || e.ed_id != id || e.nb_kind != 1 {
2335 return false;
2336 }
2337 let Some(x) = parse_f64_sep(&e.nb_buf, self.m.value.decimal_sep) else { return false };
2338 let x = rf_fix(&e.nb_rf, x);
2339 if x.to_bits() == v.to_bits() {
2340 return false;
2341 }
2342 *v = x;
2343 true
2344 }
2345
2346 fn nbf_format(&self, v: f64, dec: u32) -> Vec<u8> {
2348 fmt_f64_sep(v, dec, self.m.value.decimal_sep).into_bytes()
2349 }
2350
2351 fn nbf_parse(&self, stored: f64) -> f64 {
2353 parse_f64_sep(&self.m.edit.nb_buf, self.m.value.decimal_sep).unwrap_or(stored)
2354 }
2355
2356 fn nbf_store(&mut self, x: f64, v: &mut f64) {
2358 let rf = self.m.edit.nb_rf;
2359 *v = rf_fix(&rf, x);
2360 let t = self.nbf_format(*v, rf.dec);
2361 self.m.edit.nb_buf = t;
2362 self.nb_sync(v.to_bits()); self.nb_caret_end();
2364 }
2365
2366 fn nb_store(&mut self, x: i64, v: &mut i32) {
2368 *v = rg_clamp(x, &self.m.edit.nb_rg);
2369 self.m.edit.nb_buf = nb_format(*v);
2370 self.nb_sync(*v as u32 as u64); self.nb_caret_end();
2372 }
2373
2374 fn nb_caret_end(&mut self) {
2376 let e = &mut self.m.edit;
2377 if e.nb_id != 0 && e.ed_id == e.nb_id {
2378 e.ed_caret = e.nb_buf.len();
2379 e.ed_anchor = e.nb_buf.len();
2380 }
2381 }
2382
2383 fn nb_sync(&mut self, shown: u64) {
2386 let e = &mut self.m.edit;
2387 e.nb_orig.clear();
2388 e.nb_orig.extend_from_slice(&e.nb_buf[..e.nb_buf.len().min(NB_CAP as usize - 1)]);
2389 e.nb_shown = shown;
2390 }
2391
2392 fn nb_edited(&self) -> bool {
2394 self.m.edit.nb_buf != self.m.edit.nb_orig
2395 }
2396
2397 fn nb_follow(&mut self, cur: u64, dec: u32) {
2402 if cur == self.m.edit.nb_shown || self.nb_edited() {
2403 return; }
2405 let e = &self.m.edit;
2406 let old = e.nb_buf.len();
2407 let (lo, hi) = if e.ed_caret >= e.ed_anchor { (e.ed_anchor, e.ed_caret) } else { (e.ed_caret, e.ed_anchor) };
2408 let all = lo == 0 && hi == old && old != 0;
2409 let t = if e.nb_kind == 0 { nb_format(cur as u32 as i32) } else { self.nbf_format(f64::from_bits(cur), dec) };
2410 self.m.edit.nb_buf = t;
2411 self.nb_sync(cur);
2412 let e = &mut self.m.edit;
2413 if e.ed_id == e.nb_id {
2414 e.ed_caret = e.nb_buf.len();
2415 e.ed_anchor = e.nb_buf.len();
2416 if all {
2417 e.ed_anchor = 0;
2418 }
2419 }
2420 }
2421
2422 fn nb_release(&mut self) {
2425 let old = self.m.edit.nb_id;
2426 if old == 0 {
2427 return;
2428 }
2429 self.m.edit.nb_id = 0;
2430 if self.frame.wrapping_sub(self.m.edit.nb_seen) > 2 {
2431 return; }
2433 if self.nb_edited() {
2434 let e = &mut self.m.edit;
2436 e.pend_text = Some((old, e.nb_kind, e.nb_buf.clone()));
2437 self.repass();
2438 }
2439 self.m.edit.nb_pend = old;
2440 self.m.edit.nb_pend_ev = EV_COMMIT;
2441 }
2442
2443 fn nb_pend_store(&mut self, id: u32, vi: Option<&mut i32>, vf: Option<&mut f64>, rg: Option<&Range>, rf: Option<&RangeF>) {
2446 match &self.m.edit.pend_text {
2447 Some((pid, _, _)) if *pid == id => {}
2448 _ => return,
2449 }
2450 let Some((_, kind, t)) = self.m.edit.pend_text.take() else { return };
2451 let changed = match (kind, vi, vf, rg, rf) {
2452 (0, Some(v), _, Some(rg), _) => {
2453 let old = *v;
2454 *v = rg_clamp(nb_parse_text(&t, *v), rg);
2455 *v != old
2456 }
2457 (1, _, Some(v), _, Some(rf)) => {
2458 let old = v.to_bits();
2459 let x = parse_f64_sep(&t, self.m.value.decimal_sep).unwrap_or(*v);
2460 *v = rf_fix(rf, x);
2461 v.to_bits() != old
2462 }
2463 _ => false,
2464 };
2465 if changed && self.m.edit.nb_pend == id {
2466 self.m.edit.nb_pend_ev |= EV_ACT;
2467 }
2468 }
2469}
2470
2471impl Ui {
2475 pub(crate) fn ed_hold(&mut self, id: u32, anchor: usize, caret: usize) {
2480 let e = &mut self.m.edit;
2481 if e.ed_id != id {
2482 if e.ed_id != 0 {
2483 e.ed_blur = e.ed_id;
2484 }
2485 e.ed_id = id;
2486 e.ed_drag = false;
2487 self.ud_reset();
2488 }
2489 let frame = self.frame;
2490 let e = &mut self.m.edit;
2491 if e.ed_blur == id {
2492 e.ed_blur = 0;
2493 }
2494 e.ed_anchor = anchor;
2495 e.ed_caret = caret;
2496 e.ed_seen = frame;
2497 e.ed_multi = true;
2498 e.ed_prefx = -1;
2499 }
2500
2501 pub(crate) fn ed_drop(&mut self, id: u32) {
2504 if self.m.edit.ed_id == id {
2505 self.m.edit.ed_id = 0;
2506 self.ime_off();
2507 }
2508 }
2509}