use alloc::rc::Rc;
use alloc::string::String;
use alloc::vec::Vec;
use super::cmd::{Draw, DrawRef, Gfx};
use super::consts::*;
use super::keys::*;
use super::{Sp, Ui};
use gfx::Surface;
use rt::fx::{cvtt, cvttd, cvttd64, maxsd, minsd};
pub use rt::fx::cvtd;
const TRK_POOL: usize = 64; const PROP_NB_W: i32 = 88; pub const NO_DEFAULT: i32 = i32::MIN;
static POW10: [f64; 19] = [
1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11, 1.0e12, 1.0e13,
1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18,
];
const RAD2DEG: f64 = 57.29577951308232;
const DEG2RAD: f64 = 0.017453292519943295;
#[derive(Clone, Copy, Default, Debug)]
pub struct RangeF {
pub min: f64,
pub max: f64,
pub step: f64,
pub dec: u32,
pub flags: u32,
}
impl RangeF {
pub const fn new(min: f64, max: f64, step: f64, dec: u32, flags: u32) -> RangeF {
RangeF { min, max, step, dec, flags }
}
}
#[derive(Clone, Default)]
pub struct Track {
pub flags: u32,
pub n: u32,
pub stops: [u32; TK_MAXN],
pub custom: Option<(DrawRef, usize, u32)>,
}
impl Track {
pub fn new(flags: u32, stops: &[u32]) -> Track {
let mut t = Track { flags, n: stops.len().min(TK_MAXN) as u32, ..Track::default() };
t.stops[..t.n as usize].copy_from_slice(&stops[..t.n as usize]);
t
}
}
pub(crate) struct S {
pub(crate) decimal_sep: u32,
trk: Vec<(u64, Rc<TrackDraw>)>,
trk_next: usize,
}
impl Default for S {
fn default() -> S {
S { decimal_sep: 44, trk: Vec::new(), trk_next: 0 }
}
}
pub fn part_id(id: u32, k: u32) -> u32 {
((id << 4) | (k & 15)) & 0x0FFF_FFFF | PART_BASE
}
pub(crate) fn vl_state(ev: u32, disabled: bool) -> u32 {
if disabled {
3
} else if ev & EV_HELD != 0 {
2
} else if ev & EV_HOVER != 0 {
1
} else {
0
}
}
pub(crate) fn f_round(x: f64) -> i64 {
if x >= 0.0 { cvttd64(x + 0.5) } else { cvttd64(x - 0.5) }
}
fn f_floor(x: f64) -> f64 {
let mut t = cvttd64(x);
if (t as f64) > x {
t = t.wrapping_sub(1);
}
t as f64
}
fn rf_dec(rf: &RangeF) -> usize {
rf.dec.min(6) as usize
}
pub(crate) fn rf_fix(rf: &RangeF, v: f64) -> f64 {
let mut x = if v.is_nan() { rf.min } else { v };
x = maxsd(x, rf.min);
x = minsd(x, rf.max);
if rf.step > 0.0 {
x = x - rf.min;
x = x / rf.step;
x = f_round(x) as f64;
x = x * rf.step;
x = x + rf.min;
x = minsd(x, rf.max);
}
let p = POW10[rf_dec(rf)];
x = x * p;
x = f_round(x) as f64;
x = x / p;
x = maxsd(x, rf.min);
x = minsd(x, rf.max);
x + 0.0
}
pub(crate) fn rf_scaled(rf: &RangeF, v: f64) -> i32 {
let r = f_round(v * POW10[rf_dec(rf)]);
r.clamp(i32::MIN as i64, i32::MAX as i64) as i32
}
pub(crate) fn rf_t16(rf: &RangeF, v: f64) -> i32 {
let x = v - rf.min;
let d = rf.max - rf.min;
if !(d > 0.0) {
return 0;
}
let r = f_round(x / d * 65536.0);
if r < 0 {
0
} else if r > 65536 {
65536
} else {
r as i32
}
}
fn rf_from_t16(rf: &RangeF, t: i32) -> f64 {
let mut x = t as f64 / 65536.0;
let d = rf.max - rf.min;
x = x * d;
x = x + rf.min;
rf_fix(rf, x)
}
pub fn deg_atan2(y: f64, x: f64) -> f64 {
rt::x87::atan2(y, x) * RAD2DEG
}
pub fn deg_sincos(d: f64) -> (f64, f64) {
rt::x87::sincos(d * DEG2RAD)
}
fn utf8_push(out: &mut Vec<u8>, c: u32) {
if c < 0x80 {
out.push(c as u8);
} else if c < 0x800 {
out.push(0xC0 | (c >> 6) as u8);
out.push(0x80 | (c & 63) as u8);
} else if c < 0x10000 {
out.push(0xE0 | (c >> 12) as u8);
out.push(0x80 | ((c >> 6) & 63) as u8);
out.push(0x80 | (c & 63) as u8);
} else {
out.push(0xF0 | ((c >> 18) & 7) as u8);
out.push(0x80 | ((c >> 12) & 63) as u8);
out.push(0x80 | ((c >> 6) & 63) as u8);
out.push(0x80 | (c & 63) as u8);
}
}
pub(crate) fn fmt_f64_sep(v: f64, decimals: u32, sep: u32) -> String {
let dec = decimals.min(6) as usize;
let x = if v.is_nan() { 0.0 } else { v };
let mut r = f_round(x * POW10[dec]) as u64;
let mut out = Vec::with_capacity(32);
if (r as i64) < 0 {
out.push(b'-');
r = (r as i64).wrapping_neg() as u64;
}
let mut dig = [0u8; 32];
let mut n = 0;
loop {
dig[n] = b'0' + (r % 10) as u8;
r /= 10;
n += 1;
if n >= 24 {
break;
}
if r != 0 || n <= dec {
continue;
}
break;
}
let mut i = n;
for _ in 0..n - dec {
i -= 1;
out.push(dig[i]);
}
if dec != 0 {
utf8_push(&mut out, sep);
while i > 0 {
i -= 1;
out.push(dig[i]);
}
}
String::from_utf8_lossy(&out).into_owned()
}
pub(crate) fn parse_f64_sep(s: &[u8], sep: u32) -> Option<f64> {
let at = |i: usize| if i < s.len() { s[i] } else { 0 };
let mut i = 0;
while at(i) == b' ' {
i += 1;
}
let mut neg = false;
if at(i) == b'-' {
neg = true;
i += 1;
} else if at(i) == b'+' {
i += 1;
}
let mut m: u64 = 0; let mut digits = 0; let mut frac = 0usize; let mut dropped = 0usize; let mut seen_sep = false;
loop {
let c = at(i);
if c.is_ascii_digit() {
digits += 1;
i += 1;
if m >= 100_000_000_000_000_000 {
if !seen_sep {
dropped += 1; }
} else {
m = m * 10 + (c - b'0') as u64;
if seen_sep {
frac += 1;
}
}
} else if c == b',' || c == b'.' || c as u32 == sep {
if seen_sep {
break;
}
seen_sep = true;
i += 1;
} else {
break;
}
}
if digits == 0 {
return None;
}
let mut x = m as i64 as f64;
x = x / POW10[frac.min(18)];
x = x * POW10[dropped.min(18)];
if neg {
x = 0.0 - x;
}
Some(x)
}
pub fn grad_at(tk: &Track, t: i32) -> u32 {
let n = tk.n.min(TK_MAXN as u32);
if n == 0 {
return 0;
}
if n == 1 {
return tk.stops[0];
}
let t = if t < 0 { 0 } else if t as u32 > 65536 { 65536 } else { t };
let segs = n - 1;
let pos = (t as u32).wrapping_mul(segs); let mut seg = pos >> 16;
let mut f = (pos & 0xFFFF) as i32;
if seg >= segs {
seg = segs - 1; f = 0x10000;
}
let c0 = tk.stops[seg as usize];
let c1 = tk.stops[seg as usize + 1];
let mut r = 0u32;
for sh in [0, 8, 16, 24] {
let a = ((c0 >> sh) & 255) as i32;
let b = ((c1 >> sh) & 255) as i32;
let v = a + ((b - a).wrapping_mul(f) >> 16);
r |= (v as u32) << sh;
}
r
}
pub fn px_mix(dst: u32, src: u32, k: u32) -> u32 {
let ik = 255u32.wrapping_sub(k);
let mut r = 0;
for sh in [0, 8, 16] {
let mut v = ((dst >> sh) & 255).wrapping_mul(ik).wrapping_add(((src >> sh) & 255).wrapping_mul(k)).wrapping_add(128);
v = (v.wrapping_add(v >> 8)) >> 8;
r |= v << sh;
}
r
}
fn track_hash(tk: &Track) -> u32 {
let n = tk.n.min(TK_MAXN as u32) as usize;
let mut h: u64 = 0xCBF2_9CE4_8422_2325;
let mut eat = |v: u32| {
for b in v.to_le_bytes() {
h ^= b as u64;
h = h.wrapping_mul(0x0000_0100_0000_01B3);
}
};
eat(tk.flags);
eat(tk.n);
for &s in &tk.stops[..n] {
eat(s);
}
(h as u32) ^ (h >> 32) as u32
}
pub(crate) struct TrackDraw {
tk: Track,
cell: i32,
}
impl Draw for TrackDraw {
fn draw(&self, g: &mut Gfx, r: [i32; 4]) {
let tk = &self.tk;
let k = g.dc.clip;
let cx0 = k[0].max(r[0]);
let cy0 = k[1].max(r[1]);
let cx1 = k[2].min(r[0].wrapping_add(r[2]));
let cy1 = k[3].min(r[1].wrapping_add(r[3]));
let flags = tk.flags;
let vert = r[3] > r[2];
let (thick, len) = if vert { (r[2], r[3]) } else { (r[3], r[2]) };
let rad = thick as f32 * 0.5;
let inset = if flags & TK_RECT != 0 { 0 } else { ((thick as u32) >> 1) as i32 };
let travel = len.wrapping_sub(inset + inset).max(1);
let cell = self.cell as u32;
let stride = g.sf.stride as usize;
let mut y = cy0;
while y < cy1 {
let row = y as usize * stride;
let mut x = cx0;
while x < cx1 {
let (mut u, mut v) = (x.wrapping_sub(r[0]), y.wrapping_sub(r[1]));
if vert {
let uu = r[3].wrapping_sub(1).wrapping_sub(v);
v = u;
u = uu;
}
let t = ((((u.wrapping_sub(inset)) as i64) << 16) / travel as i64) as i32;
let c = grad_at(tk, t);
let mut cov: u32 = 255;
let mut skip = false;
if flags & TK_RECT == 0 {
let fu = u as f32 + 0.5;
let mut centre = rad;
let mut cap = !(fu >= rad); if !cap {
let e = len as f32 - rad;
if !(fu <= e) {
cap = true;
centre = e;
}
}
if cap {
let du = fu - centre;
let dv = v as f32 + 0.5 - rad;
let d = rt::fx::sqrt(du * du + dv * dv);
let kk = cvtt((rad + 0.5 - d) * 255.0);
if kk <= 0 {
skip = true;
} else {
cov = kk.min(255) as u32;
}
}
}
if !skip {
let a = c >> 24;
let (src, kk) = if flags & TK_CHECKER != 0 {
let odd = ((u as u32) / cell).wrapping_add((v as u32) / cell) & 1;
let bg = if odd != 0 { 0xCC_CCCC } else { 0xFF_FFFF };
(px_mix(bg, c, a), cov)
} else {
(c, (a * cov + 127) / 255)
};
let o = row + x as usize * 4;
let px = u32::from_le_bytes([g.sf.px[o], g.sf.px[o + 1], g.sf.px[o + 2], g.sf.px[o + 3]]);
let out = px_mix(px, src, kk);
g.sf.px[o..o + 4].copy_from_slice(&out.to_le_bytes());
}
x += 1;
}
y += 1;
}
}
}
impl Ui {
pub fn cmd_track(&mut self, x: i32, y: i32, w: i32, h: i32, tk: &Track) {
if tk.flags & TK_CUSTOM != 0 {
let (f, user, ver) = match &tk.custom {
Some((f, u, v)) => (Some(f.clone()), *u, *v),
None => (None, 0, 0),
};
self.cmd_custom(x, y, w, h, f, user, ver);
return;
}
let ver = track_hash(tk);
let f = self.track_drawing(tk);
self.cmd_custom(x, y, w, h, Some(f), 0, ver);
}
fn track_drawing(&mut self, tk: &Track) -> DrawRef {
let cell = self.dp(4).max(1);
let mut t = tk.clone();
t.custom = None;
let key = ((track_hash(&t) as u64) << 32) | cell as u32 as u64;
let s = &mut self.m.value;
let same = |d: &TrackDraw| d.cell == cell && d.tk.flags == t.flags && d.tk.n == t.n && d.tk.stops == t.stops;
if let Some((_, d)) = s.trk.iter().find(|(k, d)| *k == key && same(d)) {
return d.clone();
}
let d = Rc::new(TrackDraw { tk: t, cell });
if s.trk.len() < TRK_POOL {
s.trk.push((key, d.clone()));
} else {
let i = s.trk_next % TRK_POOL;
s.trk[i] = (key, d.clone());
s.trk_next = i + 1;
}
d
}
pub fn decimal_sep(&self) -> u32 {
self.m.value.decimal_sep
}
pub fn set_decimal_sep(&mut self, c: u32) {
self.m.value.decimal_sep = c;
}
pub fn fmt_f64(&self, v: f64, decimals: u32) -> String {
fmt_f64_sep(v, decimals, self.m.value.decimal_sep)
}
pub fn parse_f64(&self, s: &str) -> Option<f64> {
parse_f64_sep(s.as_bytes(), self.m.value.decimal_sep)
}
fn vl_disabled(&self) -> bool {
self.wp.ctx.last_state == SS_DISABLED as u32
}
pub(crate) fn sld_place(&mut self, rgflags: u32) {
let st = self.style;
let mut sp = Sp { flags: rgflags, ..Sp::default() };
let (w, h) = st.value_slider_size(self, &mut sp);
self.place(w, h);
}
pub(crate) fn sld_behave(&mut self, id: u32, stflags: u32) -> u32 {
let mut hb = HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT;
if stflags & ST_NOFOCUS != 0 {
hb &= !HB_FOCUS;
}
if stflags & ST_DISABLED != 0 {
hb |= HB_DISABLED;
}
self.behave(id, hb)
}
pub(crate) fn sld_ptr_t(&mut self, rgflags: u32) -> i32 {
let st = self.style;
let thumb = st.value_slider_thumb(self);
let half = ((thumb as u32) >> 1) as i32;
let r = self.wp.ctx.last_rect;
let (mx, my) = (self.wp.ctx.mx, self.wp.ctx.my);
let (mut p, len) = if rgflags & RG_VERT == 0 {
(mx.wrapping_sub(r[0]).wrapping_sub(half), r[2])
} else {
(r[1].wrapping_add(r[3]).wrapping_sub(half).wrapping_sub(my), r[3])
};
let len = len.wrapping_sub(thumb).max(1);
if p < 0 {
p = 0;
}
if p > len {
p = len;
}
(((p as i64) << 16) / len as i64) as i32
}
pub(crate) fn sld_keys(&mut self) -> (i32, u32) {
let mut steps = 0i32;
steps += self.key(VK_RIGHT) as i32;
steps += self.key(VK_UP) as i32;
steps -= self.key(VK_LEFT) as i32;
steps -= self.key(VK_DOWN) as i32;
steps += self.key(VK_PRIOR) as i32 * 10;
steps -= self.key(VK_NEXT) as i32 * 10;
let mut he = 0;
if self.key(VK_HOME) {
he = 1;
}
if self.key(VK_END) {
he = 2;
}
if steps != 0 || he != 0 {
self.wp.ctx.focus_visible = true;
}
(steps, he)
}
pub(crate) fn sld_draw(&mut self, t: i32, state: u32, rgflags: u32, track: Option<&Track>) {
let st = self.style;
let mut sp = Sp { value: t, state, flags: rgflags, track, ..Sp::default() };
sp.place(self.wp.ctx.last_rect);
st.value_slider(self, &sp);
}
#[cfg_attr(jfgui_dev, track_caller)]
pub fn slider_f(&mut self, id: u32, v: &mut f64, rf: &RangeF) -> u32 {
let _src = self.src_scope();
let old = v.to_bits();
let stflags = self.take_flags();
let rgf = rf.flags;
let mut keych = false;
self.sld_place(rgf);
let mut ev = self.sld_behave(id, stflags);
self.touch_grab(if rgf & RG_VERT != 0 { super::touch::AX_V } else { super::touch::AX_H });
if ev & EV_HELD != 0 {
let t = self.sld_ptr_t(rgf);
*v = rf_from_t16(rf, t);
} else if ev & EV_FOCUS != 0 {
let (steps, he) = self.sld_keys();
if he == 1 {
*v = rf.min;
keych = true;
} else if he == 2 {
*v = rf.max;
keych = true;
} else if steps != 0 {
keych = true;
let st = if rf.step > 0.0 { rf.step } else { (rf.max - rf.min) * 0.01 };
*v = steps as f64 * st + *v;
}
}
if let Some(x) = self.a11y_value(id) {
*v = x as f64 / POW10[rf_dec(rf)];
keych = true;
}
*v = rf_fix(rf, *v);
if v.to_bits() != old {
ev |= EV_ACT;
if keych {
ev |= EV_COMMIT;
}
self.repass();
}
let t = rf_t16(rf, *v);
let state = vl_state(ev, self.vl_disabled());
self.sld_draw(t, state, rgf, None);
self.focus_ring_draw(ev);
let val = rf_scaled(rf, *v);
let r = self.sem(K_SLIDER, id, None, val);
r.max = rf_scaled(rf, rf.max);
r.min = rf_scaled(rf, rf.min);
ev
}
#[cfg_attr(jfgui_dev, track_caller)]
pub fn track_slider(&mut self, id: u32, v: &mut i32, rg: &Range, track: Option<&Track>) -> u32 {
let _src = self.src_scope();
let old = *v;
let stflags = self.take_flags();
let rgf = rg.flags;
let mut keych = false;
self.sld_place(rgf);
let mut ev = self.sld_behave(id, stflags);
self.touch_grab(if rgf & RG_VERT != 0 { super::touch::AX_V } else { super::touch::AX_H });
if ev & EV_HELD != 0 {
let t = self.sld_ptr_t(rgf);
let span = rg.max.wrapping_sub(rg.min) as i64;
let mut x = ((t as i64).wrapping_mul(span).wrapping_add(32768) >> 16) as i32;
x = x.wrapping_add(rg.min);
if rg.step > 1 {
let half = ((rg.step as u32) >> 1) as i32;
x = (x.wrapping_sub(rg.min).wrapping_add(half) / rg.step).wrapping_mul(rg.step).wrapping_add(rg.min);
}
*v = x;
} else if ev & EV_FOCUS != 0 {
let (steps, he) = self.sld_keys();
if he == 1 {
*v = rg.min;
keych = true;
} else if he == 2 {
*v = rg.max;
keych = true;
} else if steps != 0 {
keych = true;
let st = rg.step.max(1);
*v = v.wrapping_add(steps.wrapping_mul(st));
}
}
if let Some(x) = self.a11y_value(id) {
*v = x;
keych = true;
}
let mut x = *v;
if x < rg.min {
x = rg.min;
}
if x > rg.max {
x = rg.max;
}
*v = x;
if x != old {
ev |= EV_ACT;
if keych {
ev |= EV_COMMIT;
}
self.repass();
}
let span = rg.max.wrapping_sub(rg.min);
let t: i32 = if span > 0 { ((((v.wrapping_sub(rg.min)) as i64) << 16) / span as i64) as i32 } else { 0 };
let state = vl_state(ev, self.vl_disabled());
self.sld_draw(t, state, rgf, track);
self.focus_ring_draw(ev);
let r = self.sem(K_SLIDER, id, None, *v);
r.max = rg.max;
r.min = rg.min;
ev
}
fn prop_begin(&mut self, label: &str) -> Option<String> {
self.box_begin(0, BOX_V, 4);
self.text(TS_BODY, label);
let l = self.sems.last().and_then(|s| s.label.clone());
self.box_begin(0, BOX_H | BOX_XCENTER, 8);
self.next_size(-1, 0);
l
}
fn set_last_label(&mut self, l: Option<String>) {
if let Some(s) = self.sems.last_mut() {
s.label = l;
}
}
fn prop_reset(&mut self, id: u32, same: bool) -> u32 {
if same {
self.next_flags(ST_DISABLED);
}
self.icon_button(part_id(id, 2), IC_UNDO, crate::i18n::tr("Zurücksetzen"))
}
#[cfg_attr(jfgui_dev, track_caller)]
pub fn prop_slider(&mut self, id: u32, label: &str, v: &mut i32, rg: &Range, default: i32) -> u32 {
let _src = self.src_scope();
let l = self.prop_begin(label);
let mut ev = self.slider(id, v, rg);
self.set_last_label(l);
self.next_size(PROP_NB_W, 0);
let nb = part_id(id, 1);
ev |= self.number_box(nb, v, rg);
if self.nb_live(nb, v) {
ev |= EV_ACT;
self.repass();
}
if default != NO_DEFAULT {
let same = *v == default;
if self.prop_reset(id, same) & EV_ACT != 0 {
*v = default;
ev |= EV_ACT | EV_COMMIT;
self.repass();
}
}
self.box_end();
self.box_end();
ev
}
#[cfg_attr(jfgui_dev, track_caller)]
pub fn prop_slider_f(&mut self, id: u32, label: &str, v: &mut f64, rf: &RangeF, default: Option<f64>) -> u32 {
let _src = self.src_scope();
let l = self.prop_begin(label);
let mut ev = self.slider_f(id, v, rf);
self.set_last_label(l);
self.next_size(PROP_NB_W, 0);
let nb = part_id(id, 1);
ev |= self.number_box_f(nb, v, rf);
if self.nb_live_f(nb, v) {
ev |= EV_ACT;
self.repass();
}
if let Some(d) = default {
let d = rf_fix(rf, d); let same = v.to_bits() == d.to_bits();
if self.prop_reset(id, same) & EV_ACT != 0 {
*v = d;
ev |= EV_ACT | EV_COMMIT;
self.repass();
}
}
self.box_end();
self.box_end();
ev
}
#[cfg_attr(jfgui_dev, track_caller)]
pub fn angle(&mut self, id: u32, deg: &mut f64, flags: u32) -> u32 {
let _src = self.src_scope();
let old = deg.to_bits();
let mut keych = false;
let stflags = self.take_flags();
let st = self.style;
let mut sp = Sp::default();
let (w, h) = st.angle_size(self, &mut sp);
self.place(w, h);
let mut ev = self.sld_behave(id, stflags);
self.touch_grab(super::touch::AX_ALL);
let r = self.wp.ctx.last_rect;
self.ltr_begin(r[0], r[2]);
let cx = (r[0] << 8).wrapping_add(r[2] << 7);
let cy = (r[1] << 8).wrapping_add(r[3] << 7);
if ev & EV_HELD != 0 {
let dx = (self.ltr_x(self.wp.ctx.mx) << 8).wrapping_sub(cx);
let dy = cy.wrapping_sub(self.wp.ctx.my << 8);
if dx | dy != 0 {
let mut a = deg_atan2(dy as f64, dx as f64);
if self.mods & MOD_SHIFT != 0 {
a = f_round(a / 15.0) as f64 * 15.0;
}
*deg = a;
}
} else if ev & EV_FOCUS != 0 {
let (steps, he) = self.sld_keys();
if he == 1 {
*deg = 0.0; keych = true;
} else if steps != 0 {
keych = true;
let x = if steps >= 10 || steps <= -10 {
((steps / 10).wrapping_mul(15)) as f64
} else if self.mods & MOD_SHIFT != 0 {
steps.wrapping_mul(15) as f64
} else {
steps as f64
};
*deg = x + *deg;
}
}
*deg = ang_norm(*deg, flags);
if deg.to_bits() != old {
ev |= EV_ACT;
if keych {
ev |= EV_COMMIT;
}
self.repass();
}
let state = vl_state(ev, self.vl_disabled());
sp.place(self.wp.ctx.last_rect);
sp.state = state;
sp.ev = ev;
sp.f = *deg;
sp.s[0] = self.focus_ring(ev) as i32;
st.angle(self, &sp);
self.ltr_end();
let v = f_round(*deg * 100.0) as i32;
self.sem(K_SLIDER, id, None, v).max = if flags & ANG_360 != 0 { 36000 } else { 18000 };
ev
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(jfgui_dev, track_caller)]
pub fn pan(&mut self, id: u32, x: &mut f64, y: &mut f64, thumb: Option<&Rc<Surface>>, ver: u32, flags: u32) -> u32 {
let _src = self.src_scope();
let (ox, oy) = (x.to_bits(), y.to_bits());
let mut keych = false;
let stflags = self.take_flags();
let (lo, span) = if flags & PAN_UNIT != 0 { (0.0, 1.0) } else { (-1.0, 2.0) };
let st = self.style;
let mut sp = Sp { img: thumb, img_ver: ver, ..Sp::default() };
let (w, h) = st.pan_size(self, &mut sp);
self.place(w, h);
let mut ev = self.sld_behave(id, stflags);
self.touch_grab(super::touch::AX_ALL);
let frac = |p: i32, len: i32| {
let len = len.max(1);
let mut f = p as f64 / len as f64;
f = maxsd(f, 0.0);
f = minsd(f, 1.0);
f = f * span;
f + lo
};
let r = self.wp.ctx.last_rect;
self.ltr_begin(r[0], r[2]); if ev & EV_HELD != 0 {
let r = self.wp.ctx.last_rect;
*x = frac(self.ltr_x(self.wp.ctx.mx).wrapping_sub(r[0]), r[2]);
*y = frac(self.wp.ctx.my.wrapping_sub(r[1]), r[3]);
} else if ev & EV_FOCUS != 0 {
let mut dx = 0i32;
let mut dy = 0i32;
let (right, left) = if self.rtl { (VK_LEFT, VK_RIGHT) } else { (VK_RIGHT, VK_LEFT) };
dx += self.key(right) as i32;
dx -= self.key(left) as i32;
dy += self.key(VK_DOWN) as i32;
dy -= self.key(VK_UP) as i32;
if self.key(VK_HOME) {
let c = span * 0.5 + lo;
*x = c;
*y = c;
keych = true;
self.wp.ctx.focus_visible = true;
} else if dx | dy != 0 {
keych = true;
self.wp.ctx.focus_visible = true;
let mut st = span * 0.01;
if self.mods & MOD_SHIFT != 0 {
st = st * 10.0;
}
*x = dx as f64 * st + *x;
*y = dy as f64 * st + *y;
}
}
let clamp = |v: f64| {
let mut v = if v.is_nan() { lo } else { v };
v = v * 10000.0;
v = f_round(v) as f64;
v = v / 10000.0;
v = maxsd(v, lo);
v = minsd(v, lo + span);
v + 0.0
};
*x = clamp(*x);
*y = clamp(*y);
if x.to_bits() != ox || y.to_bits() != oy {
ev |= EV_ACT;
if keych {
ev |= EV_COMMIT;
}
self.repass();
}
let state = vl_state(ev, self.vl_disabled());
let r = self.wp.ctx.last_rect;
let px = cvttd((*x - lo) / span * r[2] as f64).wrapping_add(r[0]);
let py = cvttd((*y - lo) / span * r[3] as f64).wrapping_add(r[1]);
sp.place(r);
sp.state = state;
sp.value = px;
sp.value2 = py;
st.pan(self, &sp);
self.focus_ring_draw(ev);
self.ltr_end();
let my = f_round(*y * 1000.0) as i32;
let mx = f_round(*x * 1000.0) as i32;
self.sem(K_SLIDER, id, None, mx).max = my;
ev
}
}
fn ang_norm(v: f64, flags: u32) -> f64 {
let x = if v.is_nan() { 0.0 } else { v };
let a360 = flags & ANG_360 != 0;
if !a360 && x >= -180.0 && x <= 180.0 {
return x + 0.0;
}
let lo = if a360 { 0.0 } else { -180.0 };
let mut k = x - lo;
k = k / 360.0;
k = f_floor(k);
k = k * 360.0;
let mut y = x - k; if !a360 && !(y > -180.0) {
y = 180.0; }
y + 0.0
}