use eframe::egui::{self, Checkbox, Context, Frame, Id, Margin, Modal, RichText, ThemePreference};
use serde::{Deserialize, Serialize};
use crate::{
ErrorManager,
app::{Bladvak, BladvakApp, PanelOpen},
};
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, PartialEq)]
pub(crate) enum SelectedSetting {
General,
Panel,
String(String),
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub(crate) struct Settings {
pub open: bool,
pub show_inspection: bool,
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,
{
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;
}
#[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();
}
},
);
});
}
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);
}
}
}
}
});
});
}
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;
}
}
}
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 {
state.open = PanelOpen::None;
}
ui.selectable_value(&mut state.open, PanelOpen::AsWindows, "Windows");
ui.selectable_value(&mut state.open, PanelOpen::None, "None");
});
}
}
}
}
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),
);
});
}
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");
}
});
});
}
}