use egui::{Response, Slider};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum AlphaSliderOrientation {
#[default]
Horizontal,
Vertical,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AlphaSlider {
value: u8,
pub orientation: AlphaSliderOrientation,
}
impl Default for AlphaSlider {
fn default() -> Self {
Self {
value: 255,
orientation: AlphaSliderOrientation::default(),
}
}
}
impl AlphaSlider {
pub fn new(value: u8) -> Self {
Self {
value,
orientation: AlphaSliderOrientation::default(),
}
}
pub fn from_alpha(alpha: f32) -> Self {
let mut s = Self::default();
s.set_alpha(alpha);
s
}
pub fn with_orientation(mut self, orientation: AlphaSliderOrientation) -> Self {
self.orientation = orientation;
self
}
pub fn value(&self) -> u8 {
self.value
}
pub fn set_value(&mut self, value: u8) {
self.value = value;
}
pub fn alpha(&self) -> f32 {
alpha_from_u8(self.value)
}
pub fn set_alpha(&mut self, alpha: f32) {
self.value = u8_from_alpha(alpha);
}
pub fn ui(&mut self, ui: &mut egui::Ui) -> Response {
match self.orientation {
AlphaSliderOrientation::Horizontal => {
ui.add(Slider::new(&mut self.value, 0..=255).text("alpha"))
}
AlphaSliderOrientation::Vertical => {
ui.add(Slider::new(&mut self.value, 0..=255).vertical())
}
}
}
}
fn alpha_from_u8(value: u8) -> f32 {
value as f32 / 255.0
}
fn u8_from_alpha(alpha: f32) -> u8 {
let clamped = alpha.clamp(0.0, 1.0);
(clamped * 255.0).round() as u8
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alpha_from_u8_at_boundaries() {
assert_eq!(alpha_from_u8(0), 0.0);
assert_eq!(alpha_from_u8(255), 1.0);
assert!((alpha_from_u8(128) - 128.0 / 255.0).abs() < 1e-7);
}
#[test]
fn u8_from_alpha_at_boundaries() {
assert_eq!(u8_from_alpha(0.0), 0);
assert_eq!(u8_from_alpha(1.0), 255);
assert_eq!(u8_from_alpha(0.5), 128);
}
#[test]
fn u8_from_alpha_clamps_out_of_range() {
assert_eq!(u8_from_alpha(-0.5), 0);
assert_eq!(u8_from_alpha(2.0), 255);
assert_eq!(u8_from_alpha(f32::NAN), 0);
}
#[test]
fn u8_from_alpha_rounds_to_nearest() {
assert_eq!(u8_from_alpha(0.498), 127);
assert_eq!(u8_from_alpha(0.502), 128);
}
#[test]
fn default_is_fully_opaque() {
let s = AlphaSlider::default();
assert_eq!(s.value(), 255);
assert_eq!(s.alpha(), 1.0);
assert_eq!(s.orientation, AlphaSliderOrientation::Horizontal);
}
#[test]
fn new_sets_integer_value() {
let s = AlphaSlider::new(0);
assert_eq!(s.value(), 0);
assert_eq!(s.alpha(), 0.0);
}
#[test]
fn from_alpha_rounds_to_integer() {
let s = AlphaSlider::from_alpha(0.5);
assert_eq!(s.value(), 128);
assert_eq!(AlphaSlider::from_alpha(2.0).value(), 255);
assert_eq!(AlphaSlider::from_alpha(-1.0).value(), 0);
}
#[test]
fn set_value_and_set_alpha_round_trip_at_boundaries() {
let mut s = AlphaSlider::new(0);
s.set_value(255);
assert_eq!(s.value(), 255);
assert_eq!(s.alpha(), 1.0);
s.set_value(128);
assert_eq!(s.value(), 128);
assert!((s.alpha() - 128.0 / 255.0).abs() < 1e-7);
s.set_alpha(0.0);
assert_eq!(s.value(), 0);
s.set_alpha(1.0);
assert_eq!(s.value(), 255);
}
#[test]
fn with_orientation_sets_vertical() {
let s = AlphaSlider::default().with_orientation(AlphaSliderOrientation::Vertical);
assert_eq!(s.orientation, AlphaSliderOrientation::Vertical);
}
}