use std::{
fs::{self, File},
path::Path,
};
use egui::{Popup, Response, RichText, Ui, Visuals, Widget};
use rvimage_domain::to_rv;
use crate::{
cfg::{
self, AzureBlobCfgPrj, AzureBlobCfgUsr, Cache, Cfg, Connection, get_cfg_tmppath,
write_cfg_str,
},
file_util::get_prj_name,
menu::ui_util::{slider, text_edit_singleline},
result::trace_ok_err,
};
fn azure_cfg_menu(
ui: &mut Ui,
azure_cfg_prj: &mut AzureBlobCfgPrj,
azure_cfg_usr: &mut AzureBlobCfgUsr,
curprjpath: Option<&Path>,
are_tools_active: &mut bool,
) {
egui::Grid::new("azure-cfg-menu")
.num_columns(2)
.show(ui, |ui| {
ui.label("Connection str");
text_edit_singleline(ui, &mut azure_cfg_usr.connection_string, are_tools_active)
.on_hover_text(azure_cfg_usr.connection_string.clone());
ui.end_row();
ui.label("Connection str path");
text_edit_singleline(
ui,
&mut azure_cfg_prj.connection_string_path,
are_tools_active,
)
.on_hover_text(azure_cfg_prj.connection_string_path.clone());
if ui.button("browse").clicked() {
let csf = rfd::FileDialog::new().pick_file();
if let Some(csf) = csf {
let relpath = curprjpath
.and_then(|cpp| csf.strip_prefix(cpp).map_err(to_rv).ok())
.and_then(|rp| rp.to_str());
if let Some(relpath) = relpath {
azure_cfg_prj.connection_string_path = relpath.to_string();
} else if let Some(csf_s) = csf.to_str() {
azure_cfg_prj.connection_string_path = csf_s.to_string();
}
}
}
ui.end_row();
ui.label("Blob container name");
text_edit_singleline(ui, &mut azure_cfg_prj.container_name, are_tools_active)
.on_hover_text(azure_cfg_prj.container_name.clone());
ui.end_row();
ui.label("Prefix/folder");
text_edit_singleline(ui, &mut azure_cfg_prj.prefix, are_tools_active)
.on_hover_text(azure_cfg_prj.prefix.clone());
ui.end_row();
ui.label("");
slider(
ui,
are_tools_active,
&mut azure_cfg_prj.blob_list_timeout_s,
1u64..=3600u64,
"timout in s",
)
.on_hover_text(azure_cfg_prj.prefix.clone());
});
}
enum Close {
Yes(bool),
No,
}
fn settings_popup(
ui: &mut Ui,
cfg: &mut Cfg,
are_tools_active: &mut bool,
toggle_cache_clear_on_close: &mut bool,
) -> Close {
let mut close = Close::No;
ui.horizontal(|ui| {
if ui.button("Open in Editor").clicked() {
let tmppath = get_cfg_tmppath(cfg);
tmppath
.parent()
.and_then(|p| fs::create_dir_all(p).ok())
.or_else(|| {
tracing::error!("could not create directory for tmp file");
Some(())
});
trace_ok_err(File::create(&tmppath));
let log_tmp = false;
if let Err(e) =
toml::to_string_pretty(&cfg).map(|s| write_cfg_str(&s, &tmppath, log_tmp))
{
tracing::error!("could not write config,\n{e:#?}");
tracing::error!("{:?}", cfg);
}
tracing::info!("opening {tmppath:?}");
if let Err(e) = edit::edit_file(&tmppath) {
tracing::error!("{e:?}");
tracing::error!("could not open editor. {:?}", edit::get_editor());
}
if let Some(cfg_) = trace_ok_err(cfg::read_cfg_gen::<Cfg>(&tmppath)) {
tracing::info!("config updated with new settings");
*cfg = cfg_;
}
if let Err(e) = cfg.write() {
tracing::error!("could not save cfg {e:?}");
}
}
if ui.button("OK").clicked() {
close = Close::Yes(true);
}
if ui.button("Cancel").clicked() {
close = Close::Yes(false);
}
});
ui.horizontal(|ui| {
let name = get_prj_name(cfg.current_prj_path(), None);
ui.label("Project Name");
ui.label(RichText::from(name).text_style(egui::TextStyle::Monospace))
.on_hover_text(
cfg.current_prj_path()
.to_str()
.unwrap_or_default()
.to_string(),
);
});
ui.separator();
ui.horizontal(|ui| {
ui.label("Style");
if ui.visuals().dark_mode {
if ui.button("Light").clicked() {
cfg.usr.darkmode = Some(false);
ui.ctx().set_visuals(Visuals::light());
}
} else if ui.button("Dark").clicked() {
cfg.usr.darkmode = Some(true);
ui.ctx().set_visuals(Visuals::dark());
}
});
ui.separator();
ui.checkbox(&mut cfg.usr.hide_thumbs, "Hide Thumbnails");
ui.checkbox(&mut cfg.usr.thumb_attrs_view, "Show Image Attribute List");
ui.separator();
if ui
.checkbox(
&mut cfg.usr.file_cache_args.clear_on_close,
"Clear cache on close",
)
.changed()
{
*toggle_cache_clear_on_close = true;
}
ui.separator();
ui.horizontal(|ui| {
let mut autosave = cfg.usr.n_autosaves.unwrap_or(0);
ui.label("Autosave versions");
ui.add(egui::Slider::new(&mut autosave, 0..=10));
if autosave > 0 {
cfg.usr.n_autosaves = Some(autosave);
} else {
cfg.usr.n_autosaves = None;
}
});
ui.separator();
ui.label("Connection");
ui.radio_value(&mut cfg.prj.connection, Connection::Local, "Local");
ui.radio_value(&mut cfg.prj.connection, Connection::Ssh, "Ssh");
ui.radio_value(
&mut cfg.prj.connection,
Connection::PyHttp,
"Http served by 'python -m http.server'",
);
#[cfg(feature = "azure_blob")]
ui.radio_value(
&mut cfg.prj.connection,
Connection::AzureBlob,
"Azure blob storage",
);
#[cfg(feature = "azure_blob")]
if cfg.prj.connection == Connection::AzureBlob {
let curprjpath = cfg.current_prj_path().parent().map(|cpp| cpp.to_path_buf());
let azure_cfg_prj = match &mut cfg.prj.azure_blob {
Some(cfg) => cfg,
None => {
cfg.prj.azure_blob = Some(AzureBlobCfgPrj::default());
cfg.prj.azure_blob.as_mut().unwrap()
}
};
let azure_cfg_usr = match &mut cfg.usr.azure_blob {
Some(cfg) => cfg,
None => {
cfg.usr.azure_blob = Some(AzureBlobCfgUsr::default());
cfg.usr.azure_blob.as_mut().unwrap()
}
};
azure_cfg_menu(
ui,
azure_cfg_prj,
azure_cfg_usr,
curprjpath.as_deref(),
are_tools_active,
);
}
ui.separator();
ui.horizontal(|ui| {
ui.label("Cache");
ui.radio_value(&mut cfg.usr.cache, Cache::FileCache, "File Cache");
ui.radio_value(&mut cfg.usr.cache, Cache::NoCache, "No Cache");
});
ui.separator();
ui.horizontal(|ui| {
if ui.button("OK").clicked() {
close = Close::Yes(true);
}
if ui.button("Cancel").clicked() {
close = Close::Yes(false);
}
});
close
}
pub struct CfgMenu<'a> {
cfg: &'a mut Cfg,
cfg_orig: Cfg,
are_tools_active: &'a mut bool,
toggle_clear_cache_on_close: &'a mut bool,
}
impl<'a> CfgMenu<'a> {
pub fn new(
cfg: &'a mut Cfg,
are_tools_active: &'a mut bool,
reload: &'a mut bool,
) -> CfgMenu<'a> {
let cfg_orig = cfg.clone();
Self {
cfg,
cfg_orig,
are_tools_active,
toggle_clear_cache_on_close: reload,
}
}
}
impl Widget for CfgMenu<'_> {
fn ui(self, ui: &mut Ui) -> Response {
let edit_cfg_btn_resp = ui.button("Settings");
Popup::menu(&edit_cfg_btn_resp)
.close_behavior(egui::PopupCloseBehavior::CloseOnClickOutside)
.show(|ui| {
let close = settings_popup(
ui,
self.cfg,
self.are_tools_active,
self.toggle_clear_cache_on_close,
);
if let Close::Yes(save) = close {
if save {
if let Err(e) = self.cfg.write() {
tracing::error!("could not write config,\n{e:#?}");
tracing::error!("{:?}", self.cfg);
}
} else {
*self.cfg = self.cfg_orig.clone();
}
ui.close();
}
});
edit_cfg_btn_resp
}
}