gled 2.28.5

gled is an application for creating animations and effects on artnet or dmx installations
use egui::{Color32, Label, Rect, Vec2, Widget};
use epaint::RectShape;

use crate::ui::FRAME_STROKE;

pub struct GledSlider<'a> {
    value: &'a mut f32,
    preview_value: Option<f32>,
    max_value: f32,
    size: f32,
    show_label: bool,
    horizontal: bool,
}

impl<'a> GledSlider<'a> {
    pub fn new(value: &'a mut f32, max_value: f32) -> Self {
        Self {
            value,
            preview_value: None,
            max_value,
            size: 50.0,
            show_label: false,
            horizontal: false,
        }
    }

    pub fn preview_value(mut self, preview_value: f32) -> Self {
        self.preview_value = Some(preview_value);
        self
    }

    pub fn size(mut self, size: f32) -> Self {
        self.size = size;
        self
    }
    pub fn show_label(mut self) -> Self {
        self.show_label = true;
        self
    }
    pub fn horizontal(mut self) -> Self {
        self.horizontal = true;
        self
    }
}

impl Widget for GledSlider<'_> {
    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
        const TOP_COLOR: Color32 = Color32::from_rgb(53, 53, 49);
        const HANDLE_COLOR: Color32 = Color32::from_rgb(59, 255, 0);
        const SELECTED_VALUE_COLOR: Color32 = Color32::from_rgb(103, 103, 94);
        const ACTUAL_VALUE_COLOR: Color32 = Color32::from_rgb(255, 176, 100);

        let preview_value = self.preview_value.unwrap_or(*self.value);

        if self.horizontal {
            let height: f32 = self.size;
            let mut width = ui.available_width();
            if self.show_label {
                width -= 20.0;
            }

            ui.horizontal(|ui| {
                let corner_radius = 5;
                let (rect, mut response) = ui
                    .allocate_exact_size(egui::vec2(width, height), egui::Sense::click_and_drag());
                let (left, right) = rect.split_left_right_at_fraction(preview_value);
                let handle_rect = {
                    let rect = rect.shrink2(Vec2::new(corner_radius as f32, 0.0));
                    let (left, _right) = rect.split_left_right_at_fraction(*self.value);
                    Rect::from_min_max(
                        left.right_top() + Vec2::new(-(corner_radius as f32), 0.0),
                        left.right_bottom() + Vec2::new(corner_radius as f32, 0.0),
                    )
                };
                let (selected_left, _) = rect.split_left_right_at_fraction(*self.value);

                ui.painter_at(right)
                    .rect_filled(rect, corner_radius, TOP_COLOR);
                ui.painter_at(selected_left)
                    .rect_filled(rect, corner_radius, SELECTED_VALUE_COLOR);
                ui.painter_at(left)
                    .rect_filled(rect, corner_radius, ACTUAL_VALUE_COLOR);
                ui.painter()
                    .rect_filled(handle_rect, corner_radius, HANDLE_COLOR);
                ui.painter().rect_stroke(
                    rect,
                    corner_radius,
                    FRAME_STROKE,
                    egui::StrokeKind::Inside,
                );

                if self.show_label {
                    let mut label_rect = ui.available_rect_before_wrap();
                    label_rect.set_height(height);
                    ui.put(
                        label_rect,
                        Label::new(format!("{:.0}%", *self.value * self.max_value)),
                    );
                }

                if let Some(pointer_position_2d) = response.interact_pointer_pos() {
                    let new_value =
                        ((pointer_position_2d.x - rect.left()) / rect.width()).clamp(0.0, 1.0);
                    *self.value = new_value;
                    response.mark_changed();
                }
                response
            })
            .inner
        } else {
            let width = self.size;
            let mut height = ui.available_height();
            if self.show_label {
                height -= 20.0;
            }

            ui.vertical(|ui| {
                let corner_radius = 5;
                let (rect, mut response) = ui
                    .allocate_exact_size(egui::vec2(width, height), egui::Sense::click_and_drag());
                let (top, bottom) = rect.split_top_bottom_at_fraction(1.0 - preview_value);
                let handle_rect = {
                    let rect = rect.shrink2(Vec2::new(0.0, corner_radius as f32));
                    let (top, _bottom) = rect.split_top_bottom_at_fraction(1.0 - *self.value);
                    Rect::from_min_max(
                        top.left_bottom() + Vec2::new(0.0, -(corner_radius as f32)),
                        top.right_bottom() + Vec2::new(0.0, corner_radius as f32),
                    )
                };
                let (_, selected_bottom) = rect.split_top_bottom_at_fraction(1.0 - *self.value);

                ui.painter().add(
                    RectShape::filled(
                        rect.expand(2.0),
                        corner_radius,
                        Color32::from_black_alpha(60),
                    )
                    .with_blur_width(6.0),
                );
                ui.painter_at(top)
                    .rect_filled(rect, corner_radius, TOP_COLOR);
                ui.painter_at(selected_bottom).rect_filled(
                    rect,
                    corner_radius,
                    SELECTED_VALUE_COLOR,
                );
                ui.painter_at(bottom)
                    .rect_filled(rect, corner_radius, ACTUAL_VALUE_COLOR);
                ui.painter()
                    .rect_filled(handle_rect, corner_radius, HANDLE_COLOR);
                ui.painter().rect_stroke(
                    rect,
                    corner_radius,
                    FRAME_STROKE,
                    egui::StrokeKind::Inside,
                );

                if self.show_label {
                    let mut label_rect = ui.available_rect_before_wrap();
                    label_rect.set_width(width);
                    ui.put(
                        label_rect,
                        Label::new(format!("{:.0}%", *self.value * 100.0)),
                    );
                }

                if let Some(pointer_position_2d) = response.interact_pointer_pos() {
                    let new_value = 1.0
                        - ((pointer_position_2d.y - rect.top()) / rect.height()).clamp(0.0, 1.0);
                    *self.value = new_value;
                    response.mark_changed();
                }
                response
            })
            .inner
        }
    }
}