bladvak 1.3.28

Base egui/eframe app - bladvak means 'pickaxe' in Gobbledegook
Documentation
//! Settings component

use eframe::egui::{self, Checkbox, Context, Frame, Id, Margin, Modal, RichText, ThemePreference};
use serde::{Deserialize, Serialize};

use crate::{
    ErrorManager,
    app::{Bladvak, BladvakApp, PanelOpen},
};

/// Selected Setting
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)]
pub(crate) enum SelectedSetting {
    /// General setting
    General,
    /// Panel setting
    Panel,
    /// Custom setting
    String(String),
}

/// Settings object
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub(crate) struct Settings {
    /// Is setting modal open
    pub open: bool,

    /// Debug and inspection toggle
    pub show_inspection: bool,

    /// Selected Panel
    pub selected_setting: SelectedSetting,
}

impl Default for Settings {
    fn default() -> Self {
        Self {
            show_inspection: false,
            open: false,
            selected_setting: SelectedSetting::General,
        }
    }
}

impl<M> Bladvak<M>
where
    M: for<'a> BladvakApp<'a> + Serialize + for<'a> Deserialize<'a> + 'static,
{
    /// Show the error manager ui
    pub fn show_error_manager(&mut self, ctx: &Context) {
        if !self.error_manager.was_open && !self.error_manager.errors.is_empty() {
            self.error_manager.is_open = true;
        }
        egui::Window::new("Errors")
            .id("bladvak_errors_windows".into())
            .open(&mut self.error_manager.is_open)
            .vscroll(true)
            .show(ctx, |ui| {
                for error in &self.error_manager.errors {
                    ui.label(error.message.clone());
                }
            });
        if !self.error_manager.is_open {
            self.error_manager.errors.clear();
        }
        self.error_manager.was_open = self.error_manager.is_open;
    }

    /// show setting popop bottom
    #[inline]
    fn show_settings_modal_bottom(ui: &mut egui::Ui) {
        egui::Panel::bottom("bottom_settings")
            .frame(
                Frame::new()
                    .inner_margin(Margin {
                        left: 2,
                        right: 2,
                        top: 8,
                        bottom: 2,
                    })
                    .fill(ui.style().visuals.panel_fill),
            )
            .show(ui, |ui| {
                egui::Sides::new().show(
                    ui,
                    |modal_ui_left| {
                        modal_ui_left.horizontal_wrapped(|ui| {
                            ui.spacing_mut().item_spacing.x = 0.0;
                            let repo_url = M::repo_url();
                            if repo_url.is_empty() {
                                ui.label(format!("{}@{}", M::name(), M::version()));
                            } else {
                                ui.add(
                                    egui::Hyperlink::from_label_and_url(
                                        format!("{}@{}", M::name(), M::version()),
                                        M::repo_url(),
                                    )
                                    .open_in_new_tab(true),
                                );
                            }
                        });
                    },
                    |modal_ui_right| {
                        if modal_ui_right.button("Close").clicked() {
                            modal_ui_right.close();
                        }
                    },
                );
            });
    }

    /// show setting popup
    fn show_settings_modal(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
        let value = self.internal.settings.selected_setting.clone();
        Self::show_settings_modal_bottom(ui);
        egui::Panel::left("left_panel_setting")
            .resizable(true)
            .frame(
                egui::Frame::central_panel(&ui.ctx().global_style())
                    .inner_margin(0)
                    .outer_margin(5.0),
            )
            .show(ui, |ui| {
                ui.vertical_centered(|ui| {
                    ui.heading("Settings");
                });
                ui.separator();
                egui::ScrollArea::vertical().show(ui, |ui| {
                    ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
                        ui.selectable_value(
                            &mut self.internal.settings.selected_setting,
                            SelectedSetting::General,
                            "General",
                        );
                        if !self.app.panel_options_as_menu() {
                            ui.selectable_value(
                                &mut self.internal.settings.selected_setting,
                                SelectedSetting::Panel,
                                "Panels",
                            );
                        }
                        for one_panel in &self.panel_list {
                            if one_panel.has_settings() {
                                let one_setting_name = one_panel.name();
                                ui.selectable_value(
                                    &mut self.internal.settings.selected_setting,
                                    SelectedSetting::String(one_setting_name.to_string()),
                                    one_setting_name,
                                );
                            }
                        }
                    });
                });
            });
        egui::CentralPanel::default().show(ui, |ui| {
            egui::ScrollArea::vertical().show(ui, |ui| {
                ui.set_min_width(ui.available_width());
                match value {
                    SelectedSetting::General => {
                        self.show_general_setting(ui);
                    }
                    SelectedSetting::Panel => {
                        self.show_panel_setting(ui);
                    }
                    SelectedSetting::String(value) => {
                        for one_panel in &self.panel_list {
                            let panel_name = one_panel.name();
                            if panel_name == value {
                                ui.heading(format!("{panel_name} settings"));
                                ui.separator();
                                one_panel.ui_settings(&mut self.app, ui, &mut self.error_manager);
                            }
                        }
                    }
                }
            });
        });
    }

    /// Show settings Ui
    pub fn show_setting(&mut self, ctx: &Context, frame: &mut eframe::Frame) {
        let mut show_inspection = self.internal.settings.show_inspection;
        egui::Window::new("Inspection")
            .id("bladvak_inspection_windows".into())
            .open(&mut show_inspection)
            .vscroll(true)
            .show(ctx, |ui| {
                ctx.inspection_ui(ui);
                ui.separator();
                self.show_debug_setting(ui);
            });
        self.internal.settings.show_inspection = show_inspection;
        if self.internal.settings.open {
            let modal = Modal::new(Id::new("Modal settings")).show(ctx, |ui| {
                self.show_settings_modal(ui, frame);
            });
            if modal.should_close() {
                self.internal.settings.open = false;
            }
        }
    }

    /// Show setting of selected
    pub(crate) fn show_panel_setting(&mut self, ui: &mut egui::Ui) {
        ui.heading("Panels");
        for one_panel in &self.panel_list {
            if one_panel.has_ui() {
                let panel_name = one_panel.name();
                if let Some(state) = self.internal.panel_state.get_mut(panel_name) {
                    let is_side_panel = self.app.is_side_panel();
                    ui.horizontal(|ui| {
                        ui.label(panel_name);
                        if is_side_panel {
                            ui.selectable_value(&mut state.open, PanelOpen::AsSideBar, "Sidebar");
                        } else if state.open == PanelOpen::AsSideBar {
                            // set the default to None (hidden)
                            state.open = PanelOpen::None;
                        }
                        ui.selectable_value(&mut state.open, PanelOpen::AsWindows, "Windows");
                        ui.selectable_value(&mut state.open, PanelOpen::None, "None");
                    });
                }
            }
        }
    }

    /// Show setting of selected
    pub(crate) fn show_general_setting(&mut self, ui: &mut egui::Ui) {
        ui.heading(format!("{} settings", M::name()));
        ui.separator();
        ui.horizontal(|ui| {
            ui.label(format!("Clean storage of {}", M::name()));
            ui.add(Checkbox::without_text(&mut self.ignore_saved_state));
            if self.ignore_saved_state {
                ui.label(
                    RichText::new("⚠ You should restart app ⚠").color(ui.visuals().warn_fg_color),
                );
            }
        });
        ui.horizontal(|ui| {
            ui.label(format!("Reset {}", ErrorManager::title()));
            ui.button("").clicked().then(|| {
                self.error_manager = ErrorManager::default();
            });
        });
        ui.checkbox(&mut self.error_manager.is_open, "Show Error panel");
        ui.checkbox(
            &mut self.internal.settings.show_inspection,
            "Show Debug panel",
        );
        if ui.button("Organize windows").clicked() {
            ui.memory_mut(eframe::egui::Memory::reset_areas);
        }
        ui.separator();
        ui.heading("Theme");
        ui.horizontal(|ui| {
            let mut theme_preference = ui.ctx().options(|opt| opt.theme_preference);
            ui.selectable_value(&mut theme_preference, ThemePreference::Light, "☀ Light");
            ui.selectable_value(&mut theme_preference, ThemePreference::Dark, "🌙 Dark");
            ui.selectable_value(&mut theme_preference, ThemePreference::System, "💻 System");
            ui.ctx().set_theme(theme_preference);
        });
        ui.separator();
        ui.heading("About");
        ui.horizontal_wrapped(|ui| {
            ui.spacing_mut().item_spacing.x = 0.0;
            ui.label("Version: ");
            ui.label(M::version());
        });
        ui.horizontal_wrapped(|ui| {
            ui.spacing_mut().item_spacing.x = 0.0;
            let repo_url = M::repo_url();
            if !repo_url.is_empty() {
                ui.label("Link to ");
                ui.add(
                    egui::Hyperlink::from_label_and_url(
                        format!("{} repository", M::name()),
                        M::repo_url(),
                    )
                    .open_in_new_tab(true),
                );
            }
        });
        ui.horizontal_wrapped(|ui| {
            ui.spacing_mut().item_spacing.x = 0.0;
            ui.label("Using ");
            ui.add(
                egui::Hyperlink::from_label_and_url(
                    concat!("bladvak@", env!("CARGO_PKG_VERSION")),
                    "https://github.com/Its-Just-Nans/bladvak",
                )
                .open_in_new_tab(true),
            );
        });
    }

    /// Show debug information
    fn show_debug_setting(&mut self, ui: &mut egui::Ui) {
        ui.collapsing("App state", |ui| {
            ui.add_enabled_ui(false, |ui| {
                if let Ok(serialized) = serde_json::to_string_pretty(&self.app) {
                    let mut mut_serial = serialized;
                    ui.text_edit_multiline(&mut mut_serial);
                } else {
                    ui.label("Failed to serialize");
                }
            });
        });
        ui.collapsing("Internal state (bladvak)", |ui| {
            ui.add_enabled_ui(false, |ui| {
                if let Ok(serialized) = serde_json::to_string_pretty(&self.internal) {
                    let mut mut_serial = serialized;
                    ui.text_edit_multiline(&mut mut_serial);
                } else {
                    ui.label("Failed to serialize");
                }
            });
        });
    }
}