Skip to main content

grafix_toolbox/gui/elements/
slidernum.rs

1use super::*;
2
3#[derive(Debug)]
4pub struct SliderNum {
5	slider: Slider,
6	num: LineEdit,
7	inc: Button,
8	dec: Button,
9	pub value: f32,
10	last_value: f32,
11	step: Mod,
12}
13impl Default for SliderNum {
14	fn default() -> Self {
15		let (last_value, step) = (f32::NAN, Mod::empty());
16		let (slider, num, inc, dec, value) = Def();
17		Self { slider, num, inc, dec, value, last_value, step }
18	}
19}
20impl SliderNum {
21	pub fn draw<'s: 'l, 'l>(&'s mut self, r: &mut RenderLock<'l>, t: &'l Theme, layout @ Surf { size, .. }: Surf, (min, max): Vec2) -> f32 {
22		ASSERT!(min < max, "SliderNum range expects min < max");
23		let side = size.min_comp();
24		let (format, range, half_side) = (|v: &f32| format(*v, min < 0.), max - min, side * 0.5);
25
26		let Self { slider, num, inc, dec, value, last_value, step } = self;
27
28		let ss = layout.w_sub(half_side);
29		if size.x() < size.y() {
30			let v = slider.draw(r, t, ss, side) * range + min;
31			*value = v;
32			return v;
33		}
34
35		let adv_step = move |step: &Mod| {
36			1.0.or_val(range > 2., || {
37				let s = half_side / range;
38				let mag = 10f32.powf(s.abs().log10().floor());
39				(s / mag).round() * mag
40			})
41			.or_map(|_| !step.ctrl(), |s| s * 0.1.or_val(range < 1., || 20.))
42			.or_map(|_| !step.shift(), |s| s * 0.01.or_val(range < 100., || 200.))
43		};
44
45		let sl = ss.size((3., 0.9).mul(side));
46		let sb = ss.x_self(1).size((half_side, half_side));
47
48		if dec.draw(r, t, sb, "-") {
49			*value -= adv_step(step);
50		}
51		if inc.draw(r, t, sb.y(half_side), "+") {
52			*value += adv_step(step);
53		}
54
55		*value = value.clamp(min, max);
56
57		let (editing, edited) = num.edited(r);
58
59		if value != last_value {
60			num.text = format(value).into();
61		} else if editing {
62			num.text = "".into();
63		} else if edited {
64			*value = num.text.trim_start().parse::<f32>().map(|n| n.clamp(min, max)).unwrap_or(*value);
65			num.text = format(value).into();
66		}
67		slider.pip_pos = slider.pip_pos.or_val(value == last_value, || (*value - min) / range);
68
69		*last_value = *value;
70
71		let v = slider.draw(r, t, ss, side) * range + min;
72
73		*value = value.or_val(value.eps_eq(v), || v);
74
75		num.draw(r, t, sl, NumericOnly().pipe(Some));
76
77		r.logic(
78			layout,
79			move |e, _, _| {
80				if let &MouseButton { m, .. } = e
81					&& m.pressed()
82				{
83					*step = m
84				}
85
86				Pass
87			},
88			0,
89		);
90		*value
91	}
92}
93
94impl<'s: 'l, 'l> Lock::SliderNum<'s, 'l, '_> {
95	pub fn draw<O>(self, g: impl Into<Surf>, l: O) -> f32
96	where
97		Vec2: Cast<O>,
98	{
99		let Self { s, r, t } = self;
100		s.draw(r, t, g.into(), Vec2(l))
101	}
102}
103
104fn format(n: f32, signed: bool) -> String {
105	let max_len = 6;
106
107	let slots = max_len - u32(signed);
108	let max_len = usize(max_len);
109
110	let n = (|| {
111		if n == 0. {
112			return "0".into();
113		}
114
115		let min_frac = 10_f32.powi(-i32(slots - 2));
116		if n >= f32(10_u32.pow(slots)) || n.abs() <= min_frac {
117			let n = format!("{n:+.*e}", usize(slots - 4));
118			return n.or_map(|_| signed, |n| n[1..].into());
119		}
120
121		if n.fract().abs() < min_frac {
122			return format!("{n:.0}");
123		}
124
125		let (mag, slots) = iVec2((n.abs().log10(), slots - 2));
126		let slots = (slots - mag).clamp(0, slots);
127		let n = format!("{n:.*}", usize(slots));
128		n.or_map(|_| slots < 1, |n| n.trim_end_matches('0').into())
129	})();
130
131	let l = n.len();
132	n.or_map(|_| l >= max_len, |n| [n, " ".repeat(max_len - l)].concat())
133}