1use alloc::rc::Rc;
31use alloc::string::String;
32use alloc::vec::Vec;
33
34use super::cmd::DrawRef;
35use super::consts::*;
36use super::keys::*;
37use super::value::part_id;
38use super::{Sp, Ui};
39use gfx::Surface;
40
41const ELL_MAX: usize = 1024; const WRAP_MAX: usize = 1 << 16; #[derive(Clone, Copy)]
47struct Wl {
48 start: usize,
49 len: usize,
50 w: i32,
51 hy: bool,
52}
53
54pub(crate) fn cstr(s: &str) -> &str {
56 match s.find('\0') {
57 Some(i) => &s[..i],
58 None => s,
59 }
60}
61
62pub(crate) fn next_char(s: &[u8], i: usize) -> usize {
64 if i >= s.len() || s[i] == 0 {
65 return i;
66 }
67 let (_, n) = base::utf8_decode(s, i);
68 n
69}
70
71impl Ui {
72 pub fn hair(&self) -> i32 {
74 (((self.wp.rt.dpi as u32).wrapping_add(24)) / 96).max(1) as i32
75 }
76
77 pub(crate) fn text_color(&self, style: u32) -> u32 {
79 let mut t = T_TEXT;
80 if style & TX_SECONDARY != 0 {
81 t = T_TEXT2;
82 }
83 if style & TX_TERTIARY != 0 {
84 t = T_TEXT3;
85 }
86 if style & TX_ACCENT != 0 {
87 t = T_ACCENT_TEXT;
88 }
89 if style & TX_ON_ACCENT != 0 {
90 t = T_ON_ACCENT;
91 }
92 if style & TX_DISABLED != 0 || self.wp.ctx.disabled_depth != 0 {
93 t = T_TEXT_DISABLED;
94 }
95 self.color(t)
96 }
97
98 pub fn focus_ring_draw(&mut self, ev: u32) {
101 if self.focus_ring(ev) {
102 let st = self.style;
103 let r = self.wp.ctx.last_rect;
104 st.focus_ring(self, r, 4); }
106 }
107
108 pub(crate) fn sp_placed(&self, sp: &mut Sp, ev: u32) {
111 sp.place(self.wp.ctx.last_rect);
112 sp.ev = ev;
113 sp.state = if self.wp.ctx.last_state == SS_DISABLED as u32 {
114 3
115 } else if ev & EV_HOVER == 0 {
116 0
117 } else if ev & EV_HELD == 0 {
118 1
119 } else {
120 2
121 };
122 }
123
124 #[cfg_attr(jfgui_dev, track_caller)]
128 pub fn text(&mut self, style: u32, s: &str) {
129 let _src = self.src_scope();
130 let s = cstr(s);
131 if style & TX_WRAP != 0 {
132 self.text_wrap(style, s); return;
134 }
135 let base = self.ts_base(style & 7); let (font, size) = self.text.get_font();
137 let mut w = self.text.width(s);
138 let line = self.ts_line(style & 7);
139 let r = self.place(w, line);
140 let mut cut = 0;
142 let mut drawn: Option<String> = None;
143 if style & TX_ELLIPSIS != 0 && w > r[2] {
144 let (e, ew) = self.ellipsize(s.as_bytes(), r[2]);
145 drawn = Some(e);
146 w = ew;
147 cut = 1;
148 }
149 let c = self.text_color(style);
150 let mut x = r[0];
151 let spare = r[2].wrapping_sub(w);
152 if style & TX_CENTER != 0 {
153 x = x.wrapping_add(spare >> 1);
154 } else if style & TX_RIGHT != 0 {
155 x = x.wrapping_add(spare);
156 }
157 let y = r[1].wrapping_add((r[3].wrapping_sub(line)) >> 1).wrapping_add(base);
158 let t = drawn.as_deref().unwrap_or(s);
159 self.cmd_text(x, y, t.as_bytes(), font, size, c, r[2]); self.wp.ctx.last_state = 0;
161 self.sem(K_TEXT, 0, Some(s), cut);
162 }
163
164 #[cfg_attr(jfgui_dev, track_caller)]
168 pub fn heading(&mut self, level: u32, style: u32, s: &str) {
169 let _src = self.src_scope();
170 let n = self.sems.len();
171 self.text(style, s);
172 if let Some(r) = self.sems.get_mut(n) {
173 r.kind = K_HEADING;
174 r.value = level.clamp(1, 9) as i32;
175 }
176 }
177
178 fn text_wrap(&mut self, style: u32, s: &str) {
185 let b = s.as_bytes();
186 let hy = self.hyph_take(style & TX_HYPHENATE != 0); let hy = hy.as_deref();
188 let base = self.ts_base(style & 7); let (font, size) = self.text.get_font();
190 let line = self.ts_line(style & 7);
191 let mut uw = 0;
193 let mut p = 0;
194 loop {
195 let w = self.text.width(&s[p..]);
196 if w > uw {
197 uw = w;
198 }
199 match b[p..].iter().position(|&c| c == b'\n') {
200 Some(i) => p += i + 1,
201 None => break,
202 }
203 }
204 let (aw, fixed) = self.avail_width(uw);
206 let mut ww = if aw > 0 { aw } else { 0x7FFF_FFFF };
207 let mut lines = Vec::new();
208 let mut widest = self.wrap_lines(b, ww, &mut lines, hy);
209 let natw = if fixed != 0 { uw } else { widest };
211 let r = self.place(natw, (lines.len() as i32).wrapping_mul(line));
212 let pw = r[2];
214 if fixed != 0 && pw > 0 && ww != 0x7FFF_FFFF && pw != ww {
215 ww = pw;
216 widest = self.wrap_lines(b, ww, &mut lines, hy);
217 }
218 let _ = widest;
219 let c = self.text_color(style);
220 let nlines = lines.len() as i32;
223 let mut ndraw = nlines;
224 let mut cut = false;
225 let spare = r[3].wrapping_sub(nlines.wrapping_mul(line));
226 let y0 = if spare >= 0 {
227 r[1].wrapping_add(spare >> 1)
228 } else {
229 if style & TX_ELLIPSIS != 0 {
230 let fit = if line != 0 { r[3] / line } else { 0 }.max(1);
231 if fit < ndraw {
232 ndraw = fit;
233 cut = true;
234 }
235 }
236 r[1]
237 };
238 for i in 0..ndraw {
239 let l = lines[i as usize];
240 let mut t: Vec<u8> = b[l.start..l.start + l.len].to_vec();
241 if l.hy {
242 t.push(b'-'); }
244 let mut tw = l.w;
245 if cut && i + 1 == ndraw {
247 let rest = &b[l.start..];
248 let mut k = 0;
249 while k < ELL_MAX - 8 && k < rest.len() && rest[k] != 0 && rest[k] != b'\n' {
250 k += 1;
251 }
252 let (e, ew) = self.ellipsize(&rest[..k], pw);
254 t = e.into_bytes();
255 tw = ew;
256 }
257 let mut x = r[0];
259 let sp = pw.wrapping_sub(tw);
260 if sp > 0 {
261 if style & TX_CENTER != 0 {
262 x = x.wrapping_add(sp >> 1);
263 } else if style & TX_RIGHT != 0 {
264 x = x.wrapping_add(sp);
265 }
266 }
267 let y = i.wrapping_mul(line).wrapping_add(y0).wrapping_add(base);
268 self.cmd_text(x, y, &t, font, size, c, pw); }
270 self.wp.ctx.last_state = 0;
271 self.sem(K_TEXT, 0, Some(s), nlines);
272 }
273
274 fn wrap_lines(&mut self, s: &[u8], width: i32, out: &mut Vec<Wl>, hyph: Option<&spell::Hyphenator>) -> i32 {
281 out.clear();
282 let mut widest = 0;
283 let mut pos = 0;
284 'para: loop {
285 let pe = pos + s[pos..].iter().position(|&c| c == b'\n' || c == 0).unwrap_or(s.len() - pos);
287 loop {
288 if out.len() >= WRAP_MAX {
289 self.limit_hit(super::ctx::LIM_WRAP);
290 break 'para;
291 }
292 let (end, hy) = self.wrap_break(s, pos, pe, width, hyph);
293 let mut v = end;
295 while v > pos && (s[v - 1] == b' ' || s[..v].ends_with("\u{3000}".as_bytes())) {
296 v -= if s[v - 1] == b' ' { 1 } else { 3 };
297 }
298 let w = self.text.width_n(&s[pos..], v - pos) + if hy { self.text.width("-") } else { 0 };
299 out.push(Wl { start: pos, len: v - pos, w, hy });
300 if w > widest {
301 widest = w;
302 }
303 pos = end;
304 if pos >= pe {
305 break; }
307 }
308 if pe >= s.len() || s[pe] != b'\n' {
309 break;
310 }
311 pos = pe + 1; }
313 widest
314 }
315
316 fn wrap_break(&mut self, s: &[u8], pos: usize, pe: usize, width: i32, hyph: Option<&spell::Hyphenator>) -> (usize, bool) {
322 let hw = self.text.width("-");
323 let t = &mut self.text;
324 match core::str::from_utf8(s) {
325 Ok(st) => {
326 let mut fits = |a: usize, b: usize, h: bool| t.width_n(&s[a..], b - a) + if h { hw } else { 0 } <= width;
327 match hyph {
328 Some(hy) => text::segment::wrap_end_hyph(st, pos, pe, &mut fits, Some(&mut |w: &str, out: &mut Vec<usize>| *out = hy.hyphenate(w))),
329 None => text::segment::wrap_end_hyph(st, pos, pe, &mut fits, None),
330 }
331 }
332 Err(_) => {
333 let mut best = next_char(s, pos);
335 while best < pe {
336 let n = next_char(s, best);
337 if t.width_n(&s[pos..], n - pos) > width {
338 break;
339 }
340 best = n;
341 }
342 (best.min(pe), false)
343 }
344 }
345 }
346
347 pub(crate) fn ellipsize(&mut self, s: &[u8], maxw: i32) -> (String, i32) {
350 let avail = maxw.wrapping_sub(self.text.width("…"));
351 let mut n = 0;
352 if avail > 0 {
353 loop {
354 if n >= s.len() || s[n] == 0 {
355 break;
356 }
357 let m = next_char(s, n); if m > ELL_MAX - 4 {
359 break;
360 }
361 if self.text.width_n(s, m) > avail {
362 break;
363 }
364 n = m;
365 }
366 }
367 while n > 0 && s[n - 1] == b' ' {
368 n -= 1;
369 }
370 let mut out = String::from_utf8_lossy(&s[..n]).into_owned();
371 out.push('…'); let w = self.text.width(&out);
373 (out, w)
374 }
375
376 fn btn_core(&mut self, id: u32, label: Option<&str>, flags: u32) -> u32 {
381 let label = label.map(cstr);
382 let flags = flags | self.take_flags();
383 let icon = self.take_icon();
384 let st = self.style;
385 let mut sp = Sp { flags, icon, label, ..Sp::default() };
386 let (w, h) = st.button_size(self, &mut sp);
387 let square = icon != 0 && (flags & ST_ICON_ONLY != 0 || label.is_none_or(str::is_empty));
388 let (w, h) = self.ctrl_size(w, h, flags, square); self.place(w, h);
390 let mut hb = if flags & ST_NOFOCUS != 0 { 0 } else { HB_FOCUS };
391 if flags & ST_DISABLED != 0 {
392 hb |= HB_DISABLED;
393 }
394 if flags & ST_CHECKED != 0 {
395 hb |= HB_CURRENT; }
397 let ev = self.behave(id, hb);
398 self.sp_placed(&mut sp, ev);
399 st.button(self, &sp);
400 self.focus_ring_draw(ev);
401 if flags & ST_CHECKED != 0 {
402 self.wp.ctx.last_state |= SS_CHECKED as u32;
403 }
404 self.sem(K_BUTTON, id, label, 0);
405 if flags & ST_ICON_ONLY != 0 {
407 if let Some(l) = label {
408 self.tooltip(l);
409 }
410 }
411 ev
412 }
413
414 #[cfg_attr(jfgui_dev, track_caller)]
416 pub fn button(&mut self, id: u32, label: &str) -> u32 {
417 let _src = self.src_scope();
418 self.btn_core(id, Some(label), 0)
419 }
420
421 #[cfg_attr(jfgui_dev, track_caller)]
425 pub fn label_button(&mut self, id: u32, label: &str, typography: u32, inset: i32) -> u32 {
426 let _src = self.src_scope();
427 let label = cstr(label);
428 let flags = self.take_flags();
429 self.take_icon();
430 let inset = self.dp(inset.max(0));
431 self.ts_font(typography & 7);
432 let width = self.text.width(label) + inset * 2;
433 let line = self.ts_line(typography & 7);
434 let r = self.place(width, line);
435 let hb = if flags & ST_NOFOCUS != 0 { 0 } else { HB_FOCUS }
436 | if flags & ST_DISABLED != 0 { HB_DISABLED } else { 0 };
437 let ev = self.behave(id, hb);
438 let mut sp = Sp { flags, ..Sp::default() };
439 self.sp_placed(&mut sp, ev);
440 self.style.button(self, &sp);
441 let base = self.ts_base(typography & 7);
442 let available = (r[2] - inset * 2).max(0);
443 let width = self.text.width(label);
444 let (display, width) = if width > available {
445 self.ellipsize(label.as_bytes(), available)
446 } else { (String::from(label), width) };
447 let x = r[0] + inset + if typography & TX_CENTER != 0 {
448 (available - width) / 2
449 } else if typography & TX_RIGHT != 0 { available - width } else { 0 };
450 let color = if flags & (ST_ACCENT | ST_ON_ACCENT) != 0 {
451 self.color(if sp.state == 3 { T_ON_ACCENT_DIS } else { T_ON_ACCENT })
452 } else if sp.state == 3 { self.color(T_TEXT_DISABLED) }
453 else { self.text_color(typography) };
454 let (font, size) = self.text.get_font();
455 self.draw_clip(r[0], r[1], r[2], r[3]);
456 self.cmd_text(x, r[1] + (r[3] - line) / 2 + base, display.as_bytes(), font, size, color, available);
457 self.draw_unclip();
458 self.focus_ring_draw(ev);
459 if flags & ST_CHECKED != 0 {
460 self.wp.ctx.last_state |= SS_CHECKED as u32;
461 }
462 self.sem(K_BUTTON, id, Some(label), 0);
463 ev
464 }
465
466 #[cfg_attr(jfgui_dev, track_caller)]
470 pub fn link(&mut self, id: u32, label: &str, typography: u32) -> u32 {
471 let _src = self.src_scope();
472 let label = cstr(label);
473 let flags = self.take_flags();
474 self.take_icon();
475 self.ts_font(typography & 7);
476 let line = self.ts_line(typography & 7);
477 let natural = self.text.width(label);
478 let r = self.place(natural, line);
479 let hb = HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT | if flags & ST_DISABLED != 0 { HB_DISABLED } else { 0 };
480 let ev = self.behave(id, hb);
481 let (display, width) = if natural > r[2] {
482 self.ellipsize(label.as_bytes(), r[2])
483 } else { (String::from(label), natural) };
484 let color = if flags & ST_DISABLED != 0 {
485 self.color(T_TEXT_DISABLED)
486 } else if ev & EV_HELD != 0 { self.color(T_TEXT2) } else { self.color(T_ACCENT_TEXT) };
487 let base = self.ts_base(typography & 7);
488 let y = r[1] + (r[3] - line) / 2;
489 let (font, size) = self.text.get_font();
490 self.draw_clip(r[0], r[1], r[2], r[3]);
491 self.cmd_text(r[0], y + base, display.as_bytes(), font, size, color, r[2]);
492 if ev & EV_HOVER != 0 && flags & ST_DISABLED == 0 {
493 self.draw_color(color);
494 self.draw_rect(r[0], y + base + self.dp(2), width, self.dp(1).max(1));
495 }
496 self.draw_unclip();
497 self.focus_ring_draw(ev);
498 self.sem(K_LINK, id, Some(label), 0);
499 ev
500 }
501
502 #[cfg_attr(jfgui_dev, track_caller)]
504 pub fn accent_button(&mut self, id: u32, label: &str) -> u32 {
505 let _src = self.src_scope();
506 self.btn_core(id, Some(label), ST_ACCENT)
507 }
508
509 #[cfg_attr(jfgui_dev, track_caller)]
511 pub fn icon_button(&mut self, id: u32, icon: u32, label: &str) -> u32 {
512 let _src = self.src_scope();
513 self.next_icon(icon);
514 self.btn_icon(id, Some(label), ST_SUBTLE | ST_ICON_ONLY)
515 }
516
517 fn btn_icon(&mut self, id: u32, label: Option<&str>, flags: u32) -> u32 {
519 let f = self.take_flags();
520 let mut flags = flags;
521 if f & ST_LARGE != 0 {
522 flags &= !ST_ICON_ONLY;
523 }
524 self.btn_core(id, label, flags | f)
525 }
526
527 #[cfg_attr(jfgui_dev, track_caller)]
529 pub fn toggle_button(&mut self, id: u32, label: &str, v: &mut i32) -> u32 {
530 let _src = self.src_scope();
531 let ev = self.btn_core(id, Some(label), if *v != 0 { ST_CHECKED } else { 0 });
532 if ev & EV_ACT != 0 {
533 *v = (*v ^ 1) & 1;
534 self.repass();
535 }
536 ev
537 }
538
539 #[cfg_attr(jfgui_dev, track_caller)]
542 pub fn tool_button(&mut self, id: u32, icon: u32, label: &str, sel: &mut i32, mine: i32) -> u32 {
543 let _src = self.src_scope();
544 self.kb_auto_group(&*sel as *const i32 as usize, mine);
545 self.next_icon(icon);
546 let mut f = ST_SUBTLE | ST_ICON_ONLY;
547 if *sel == mine {
548 f |= ST_CHECKED;
549 }
550 let ev = self.btn_icon(id, Some(label), f);
551 if ev & EV_ACT != 0 {
552 *sel = mine;
553 self.repass();
554 }
555 ev
556 }
557
558 #[cfg_attr(jfgui_dev, track_caller)]
563 pub fn checkbox(&mut self, id: u32, label: &str, v: &mut i32) -> u32 {
564 let _src = self.src_scope();
565 let label = cstr(label);
566 let f = self.take_flags();
567 let st = self.style;
568 let mut sp = Sp { flags: f, label: Some(label), ..Sp::default() };
569 let (w, h) = st.checkbox_size(self, &mut sp);
570 self.place(w, h);
571 let hb = HB_FOCUS | HB_SPACE | if f & ST_DISABLED != 0 { HB_DISABLED } else { 0 };
572 let mut ev = self.behave(id, hb);
573 if f & ST_DISABLED == 0 && core::mem::take(&mut self.m.kbnav.toggle) {
574 ev |= EV_ACT; }
576 if ev & EV_ACT != 0 {
577 *v = if *v == 1 { 0 } else { 1 };
578 self.repass();
579 }
580 self.sp_placed(&mut sp, ev);
581 sp.value = *v;
582 st.checkbox(self, &sp);
583 self.focus_ring_draw(ev);
584 if *v != 0 {
585 self.wp.ctx.last_state |= SS_CHECKED as u32;
586 }
587 self.sem(K_CHECKBOX, id, Some(label), *v);
588 ev
589 }
590
591 #[cfg_attr(jfgui_dev, track_caller)]
595 pub fn checkbox_wrap(&mut self, id: u32, label: &str, v: &mut i32) -> u32 {
596 let _src = self.src_scope();
597 self.box_begin(0, BOX_H, 6);
598 let mut ev = self.checkbox(id, "", v);
599 self.sem_label_last(label);
600 if self.wrap_label(id, label) {
601 *v = if *v == 1 { 0 } else { 1 };
602 ev |= EV_ACT;
603 self.repass();
604 }
605 self.box_end();
606 ev
607 }
608
609 #[cfg_attr(jfgui_dev, track_caller)]
612 pub fn radio_wrap(&mut self, id: u32, label: &str, v: &mut i32, mine: i32) -> u32 {
613 let _src = self.src_scope();
614 self.box_begin(0, BOX_H, 6);
615 let mut ev = self.radio(id, "", v, mine);
616 self.sem_label_last(label);
617 if self.wrap_label(id, label) && *v != mine {
618 *v = mine;
619 ev |= EV_ACT;
620 self.repass();
621 }
622 self.box_end();
623 ev
624 }
625
626 fn sem_label_last(&mut self, label: &str) {
628 let label = cstr(label);
629 if let Some(s) = self.sems.last_mut() {
630 s.label = Some(String::from(label));
631 }
632 }
633
634 fn wrap_label(&mut self, id: u32, label: &str) -> bool {
636 let n = self.sems.len();
637 self.next_size(-1, 0);
638 self.text(TS_BODY | TX_WRAP, label);
639 self.sems.truncate(n);
640 let clicked = self.behave(part_id(id, 1), 0) & EV_ACT != 0;
641 if clicked {
642 self.focus_quiet(id);
643 }
644 clicked
645 }
646
647 #[cfg_attr(jfgui_dev, track_caller)]
650 pub fn radio(&mut self, id: u32, label: &str, v: &mut i32, mine: i32) -> u32 {
651 let _src = self.src_scope();
652 let label = cstr(label);
653 let f = self.take_flags();
654 let st = self.style;
655 let mut sp = Sp { flags: f, label: Some(label), ..Sp::default() };
656 let (w, h) = st.radio_size(self, &mut sp);
657 self.place(w, h);
658 let mut hb = HB_FOCUS | HB_SPACE | if f & ST_DISABLED != 0 { HB_DISABLED } else { 0 };
659 if *v == mine {
660 hb |= HB_CURRENT;
661 }
662 self.kb_auto_group(&*v as *const i32 as usize, mine);
663 let mut ev = self.behave(id, hb);
664 if ev & EV_FOCUS != 0 && self.kb_moved(id) && *v != mine {
665 ev |= EV_ACT; }
667 if ev & EV_ACT != 0 {
668 *v = mine;
669 self.repass();
670 }
671 self.sp_placed(&mut sp, ev);
672 sp.value = (*v == mine) as i32;
673 st.radio(self, &sp);
674 self.focus_ring_draw(ev);
675 if *v == mine {
676 self.wp.ctx.last_state |= SS_CHECKED as u32;
677 }
678 self.sem(K_RADIO, id, Some(label), mine);
679 ev
680 }
681
682 #[cfg_attr(jfgui_dev, track_caller)]
684 pub fn switch(&mut self, id: u32, label: &str, v: &mut i32) -> u32 {
685 let _src = self.src_scope();
686 let label = cstr(label);
687 let f = self.take_flags();
688 let st = self.style;
689 let mut sp = Sp { flags: f, label: Some(label), ..Sp::default() };
690 let (w, h) = st.switch_size(self, &mut sp);
691 self.place(w, h);
692 let hb = HB_FOCUS | HB_SPACE | if f & ST_DISABLED != 0 { HB_DISABLED } else { 0 };
693 let mut ev = self.behave(id, hb);
694 if f & ST_DISABLED == 0 && core::mem::take(&mut self.m.kbnav.toggle) {
695 ev |= EV_ACT; }
697 if ev & EV_ACT != 0 {
698 *v = (*v ^ 1) & 1;
699 self.repass();
700 }
701 let a = self.anim(id, if *v != 0 { 256 } else { 0 }, 167);
703 self.sp_placed(&mut sp, ev);
704 sp.value = *v;
705 sp.value2 = a as i32;
706 st.switch(self, &sp);
707 self.focus_ring_draw(ev);
708 if *v != 0 {
709 self.wp.ctx.last_state |= SS_CHECKED as u32;
710 }
711 self.sem(K_SWITCH, id, Some(label), *v);
712 ev
713 }
714
715 #[cfg_attr(jfgui_dev, track_caller)]
720 pub fn slider(&mut self, id: u32, v: &mut i32, rg: &Range) -> u32 {
721 let _src = self.src_scope();
722 let old = *v;
723 let f = self.take_flags();
724 let vert = (rg.flags & RG_VERT) as i32;
725 let st = self.style;
726 let mut sp = Sp { flags: f, value: vert, ..Sp::default() };
728 let (w, h) = st.slider_size(self, &mut sp);
729 let thumb = sp.s[0];
730 let r = self.place(w, h);
731 let mut hb = HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT | if vert != 0 { HB_GRAB_V } else { HB_GRAB_H };
732 if f & ST_DISABLED != 0 {
733 hb |= HB_DISABLED;
734 }
735 let mut ev = self.behave(id, hb);
736 let len = (if vert != 0 { r[3] } else { r[2] }).wrapping_sub(thumb).max(1);
738 let snap = |x: i32| {
739 if rg.step <= 1 {
740 return x;
741 }
742 (x.wrapping_sub(rg.min).wrapping_add(rg.step >> 1) / rg.step).wrapping_mul(rg.step).wrapping_add(rg.min)
743 };
744 if ev & EV_HELD != 0 {
745 let (mx, my) = (self.wp.ctx.mx, self.wp.ctx.my);
747 let mut pos = if vert != 0 { r[1].wrapping_add(r[3]).wrapping_sub(my) } else { mx.wrapping_sub(r[0]) };
748 pos = pos.wrapping_sub(thumb >> 1); if pos < 0 {
750 pos = 0;
751 }
752 if pos > len {
753 pos = len;
754 }
755 *v = snap(rg.value_at(pos, len));
756 } else if ev & EV_FOCUS != 0 {
757 let step = if rg.step != 0 { rg.step } else { 1 };
758 let a = self.key(VK_RIGHT);
759 let b = self.key(VK_UP);
760 if a || b {
761 *v = v.wrapping_add(step);
762 self.wp.ctx.focus_visible = true;
763 }
764 let a = self.key(VK_LEFT);
765 let b = self.key(VK_DOWN);
766 if a || b {
767 *v = v.wrapping_sub(step);
768 self.wp.ctx.focus_visible = true;
769 }
770 if self.key(VK_HOME) {
771 *v = rg.min;
772 }
773 if self.key(VK_END) {
774 *v = rg.max;
775 }
776 }
777 if let Some(x) = self.a11y_value(id) {
779 *v = snap(x);
780 }
781 if *v < rg.min {
782 *v = rg.min;
783 }
784 if *v > rg.max {
785 *v = rg.max;
786 }
787 if *v != old {
788 ev |= EV_ACT;
789 self.repass();
790 }
791 let t = rg.position(*v, len);
793 self.sp_placed(&mut sp, ev);
794 sp.value = t; sp.value2 = len; st.slider(self, &sp);
797 self.focus_ring_draw(ev);
798 let r = self.sem(K_SLIDER, id, None, *v);
799 r.max = rg.max;
800 r.min = rg.min;
801 ev
802 }
803
804 #[cfg_attr(jfgui_dev, track_caller)]
808 pub fn progress(&mut self, id: u32, v: i32, max: i32) {
809 let _src = self.src_scope();
810 let st = self.style;
811 let mut sp = Sp::default();
812 let (w, h) = st.progress_size(self, &mut sp);
813 self.place(w, h);
814 self.sp_placed(&mut sp, 0);
815 sp.value = v;
816 sp.value2 = max;
817 st.progress(self, &sp);
818 self.wp.ctx.last_state = 0;
819 self.sem(K_PROGRESS, id, None, v).max = max;
820 }
821
822 #[cfg_attr(jfgui_dev, track_caller)]
824 pub fn separator(&mut self) {
825 let _src = self.src_scope();
826 let st = self.style;
827 let mut sp = Sp::default();
828 let t = st.separator_size(self, &mut sp); let horizontal = self.box_flags() & BOX_V != 0;
830 if horizontal {
831 self.next_size(-1, 0);
833 self.place(0, t);
834 } else {
835 self.next_size(0, -1);
836 self.place(t, 0);
837 }
838 self.sp_placed(&mut sp, 0);
839 sp.value = horizontal as i32; st.separator(self, &sp);
841 }
842
843 #[cfg_attr(jfgui_dev, track_caller)]
845 pub fn icon(&mut self, cp: u32, size: i32) {
846 let _src = self.src_scope();
847 let s = self.dp(size);
848 let r = self.place(s, s);
849 let c = self.color(if self.wp.ctx.disabled_depth != 0 { T_TEXT_DISABLED } else { T_TEXT });
850 self.draw_color(c);
851 let x = (r[2].wrapping_sub(s) >> 1).wrapping_add(r[0]);
852 let y = (r[3].wrapping_sub(s) >> 1).wrapping_add(r[1]);
853 self.draw_icon(cp, x, y, s << 8);
854 }
855
856 #[cfg_attr(jfgui_dev, track_caller)]
859 pub fn image(&mut self, id: u32, sf: &Rc<Surface>, ver: u32) -> u32 {
860 let _src = self.src_scope();
861 let r = self.place(sf.w, sf.h);
862 self.draw_image(sf, ver, r[0], r[1], r[2], r[3]);
863 if id == 0 {
864 return 0;
865 }
866 self.behave(id, 0)
867 }
868
869 #[cfg_attr(jfgui_dev, track_caller)]
875 pub fn canvas(&mut self, id: u32, draw: Option<DrawRef>, user: usize, version: u32) -> &UiInput {
876 let _src = self.src_scope();
877 let f = self.take_flags();
878 let r = self.place(0, 0);
879 let mut cur = f & (15 << HB_CURSOR_SHIFT);
880 if cur == 0 {
881 cur = CUR_CROSS << HB_CURSOR_SHIFT;
882 }
883 let ev = self.behave(id, cur | HB_MOVES | HB_GRAB_H | HB_GRAB_V); self.cmd_custom(r[0], r[1], r[2], r[3], draw, user, version);
886 let key = self.rtl_text_key(self.key_first()); let (mx, px) = (self.rtl_map(self.wp.ctx.mx, r[0], r[2]), self.rtl_map(self.wp.ctx.press_x, r[0], r[2]));
888 let c = &mut self.wp.ctx;
889 let inp = &mut self.uiinput;
890 inp.ev = ev;
891 inp.btn = c.mbtn;
892 let (p, rl) = if ev & (EV_HOVER | EV_COMMIT) != 0 || c.active == id { (c.pressed, c.released) } else { (0, 0) };
895 inp.pressed = p;
896 inp.released = rl;
897 if p != 0 && ev & EV_HOVER != 0 {
900 c.focus = id;
901 c.focus_visible = false;
902 }
903 inp.x = mx.wrapping_sub(r[0]);
904 inp.y = c.my.wrapping_sub(r[1]);
905 inp.press_x = px.wrapping_sub(r[0]);
906 inp.press_y = c.press_y.wrapping_sub(r[1]);
907 let (wx, wy) = if ev & EV_HOVER != 0 { (c.wheel_x, c.wheel_y) } else { (0, 0) };
908 inp.wheel_x = wx;
909 inp.wheel_y = wy;
910 inp.mods = self.mods;
911 inp.rect = r;
912 inp.dpi = self.wp.rt.dpi;
913 inp.key = key;
914 inp.pressure = self.mpress; inp.ptype = self.mptype;
916 inp.tilt_x = self.mtilt[0];
917 inp.tilt_y = self.mtilt[1];
918 let n = if ev & (EV_HOVER | EV_HELD) != 0 && self.uiw_cur == self.mpts_win { self.mpts.len().min(2048) } else { 0 };
920 inp.pts.clear();
921 inp.ppress.clear();
922 let (rtl, mirror) = (self.rtl, r[0].wrapping_add(r[0]).wrapping_add(r[2]).wrapping_sub(1));
923 for i in 0..n {
924 let p = self.mpts[i];
925 let x = if rtl { mirror.wrapping_sub(p[0]) } else { p[0] };
926 inp.pts.push([x.wrapping_sub(r[0]), p[1].wrapping_sub(r[1])]);
927 inp.ppress.push(self.mprs[i]);
928 }
929 self.dmg_canvas_seen(id); self.sem(K_CANVAS, id, None, 0);
931 &self.uiinput
932 }
933}