gled 2.28.4

gled is an application for creating animations and effects on artnet or dmx installations
use crate::storage::asset::scene::grid::GridLocation;
use egui::{
    Button, Color32, Frame, InnerResponse, KeyboardShortcut, Response, Stroke, Ui, UiBuilder,
};

pub mod action;
pub mod asset;
pub mod asset_tree;
pub mod effect;
pub mod gled_slider;
pub mod input;
pub mod logo;
pub mod pills;
pub mod scene_effect_editor;
pub mod scene_instance;
pub mod temperature;
#[cfg(not(debug_assertions))]
pub mod update_check;
pub mod window_common;
pub mod windows;

pub static FRAME_STROKE: Stroke = Stroke {
    width: 0.3,
    color: Color32::WHITE,
};

pub trait ChangeButton {
    fn change_button(&mut self, ui: &mut Ui) -> bool;
}

impl ChangeButton for f32 {
    fn change_button(&mut self, ui: &mut Ui) -> bool {
        let mut changed = false;

        let response = ui.add(egui::Slider::new(self, 0.0..=1.0));
        if response.changed() {
            changed = true;
        }

        changed
    }
}

pub trait OverwriteChangeButton: Default + ChangeButton {
    const NAME: &'static str;
}

// for overwrites
impl<T: OverwriteChangeButton> ChangeButton for Option<T> {
    fn change_button(&mut self, ui: &mut Ui) -> bool {
        let mut changed = false;

        let mut overwrite = self.is_some();
        ui.checkbox(&mut overwrite, format!("Overwrite {}", T::NAME));
        if self.is_none() && overwrite {
            *self = Some(T::default());
            changed = true;
        } else if self.is_some() && !overwrite {
            *self = None;
            changed = true;
        }
        if let Some(asset_id) = self {
            ui.vertical_centered_justified(|ui| {
                changed |= asset_id.change_button(ui);
            });
        }

        changed
    }
}

pub fn scoped_frame<T>(
    ui: &mut Ui,
    ui_builder: UiBuilder,
    frame: Frame,
    add_contents: impl FnOnce(&mut Ui) -> T,
) -> InnerResponse<T> {
    ui.scope_builder(ui_builder, |ui| {
        frame
            .show(ui, |ui| {
                ui.take_available_space();
                add_contents(ui)
            })
            .inner
    })
}

pub struct ContextMenuAction {
    pub keyboard_shortcut: KeyboardShortcut,
    pub description: String,
    pub ctx_action: fn(GridLocation),
    pub key_action: fn(),
}

#[derive(Default)]
pub struct ContextMenuBuilder {
    actions: Vec<ContextMenuAction>,
}

impl ContextMenuBuilder {
    pub fn add_action(mut self, action: ContextMenuAction) -> Self {
        self.actions.push(action);
        self
    }
    pub fn show(self, response: &mut Response, location: GridLocation) {
        response.context_menu(|ctx_menu_ui| {
            for action in &self.actions {
                let context_button = Button::new(action.description.to_string())
                    .shortcut_text(ctx_menu_ui.ctx().format_shortcut(&action.keyboard_shortcut));
                if ctx_menu_ui.add(context_button).clicked() {
                    (action.ctx_action)(location);
                    return;
                }
            }
        });
        for action in &self.actions {
            if !response.ctx.egui_wants_keyboard_input()
                && response
                    .ctx
                    .input_mut(|i| i.consume_shortcut(&action.keyboard_shortcut))
            {
                (action.key_action)();
            }
        }
    }
}