Change up the ui quite a bit, but make it cleaner

This commit is contained in:
David Holland 2023-09-20 14:30:02 +02:00
parent 2acb594c28
commit 5e3e30c30e
Signed by: DustVoice
GPG Key ID: 47068995A14EDCA9
1 changed files with 178 additions and 211 deletions

389
src/ui.rs
View File

@ -2,7 +2,8 @@
use eframe::egui;
use egui::{plot::{Legend, Line, Plot}, Align, Layout, ProgressBar, Modifiers};
use egui::{plot::{Legend, Line, Plot}, Align, Layout, ProgressBar, Modifiers, Checkbox, collapsing_header, SelectableLabel};
use log::debug;
use rfd::{AsyncFileDialog, AsyncMessageDialog};
use crate::{
@ -27,7 +28,7 @@ struct LoadingData {
config: Config,
database: Database,
brew_checkboxes: HashMap<String, bool>,
selected_bean: String,
bean_brew_marked: HashMap<String, bool>,
plot_entries: HashMap<String, PlotEntry>,
}
@ -47,11 +48,8 @@ pub struct Ui {
config: Config,
database: Database,
selected_bean: String,
brew_checkboxes: HashMap<String, bool>,
select_all: bool,
clear_all: bool,
bean_brew_marked: HashMap<String, bool>,
plot_entries: HashMap<String, PlotEntry>,
@ -80,6 +78,12 @@ impl Ui {
pub fn reset(&mut self) {
*self = Self::default();
}
pub fn clear_selection(&mut self) {
for checkbox in &mut self.brew_checkboxes {
*checkbox.1 = false;
}
}
}
impl eframe::App for Ui {
@ -96,21 +100,123 @@ impl eframe::App for Ui {
egui::TopBottomPanel::top("dark_light").show(ctx, |ui| {
ui.vertical(|ui| {
egui::menu::bar(ui, |ui| {
let reset_button = ui.button("Reset");
let load_button = ui.button(
if !self.data_transfered {
"Load Files"
} else {
"Reload Files"
});
if reset_button.clicked() {
self.reset();
if load_button.hovered() {
if self.show_loading_screen {
egui::show_tooltip(ctx, egui::Id::new("reload_tooltip"), |ui| {
ui.label("Loading is still in progress.\nTo reload, please wait until previous loading has finished!");
});
}
}
// if reload_button.hovered() {
// if let Some(loader_thread) = &self.loader_thread {
// if !loader_thread.is_finished() {
// egui::show_tooltip(ctx, egui::Id::new("reload_tooltip"), |ui| {
// ui.label("Loading is still in progress.\nTo reload, please wait until previous loading has finished!");
// });
// }
// }
// }
if load_button.clicked() {
self.reset();
let config_file_arc = self.config_file.clone();
let data_archive_arc = self.data_archive.clone();
let files_provided_arc = self.files_provided.clone();
let file_dialog = async move {
let config_message = AsyncMessageDialog::new()
.set_title("Select config file")
.set_description("Please select the config (.toml) file in the next dialog.\n(Default \"config.toml\")")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
if !config_message { return }
let config_file = AsyncFileDialog::new()
.add_filter("toml", &["toml"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}.unwrap_or_default()
)
.set_title("Config file [.toml]")
.set_file_name("config.toml")
.pick_file().await;
if config_file.is_none() { return }
let config_raw = config_file.unwrap().read().await;
let archive_message = AsyncMessageDialog::new()
.set_title("Select data archive")
.set_description("Please select the data archive (.zip) in the next dialog.\n(Default \"config.toml\")\n\nWihin the zip archive there should be the main json file (e.g. \"Beanconqueror.json\"), as well as a \"brews\" folder, with exact paths specified in the previously selected config file.")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
if !archive_message { return }
let archive_file = AsyncFileDialog::new()
.add_filter("zip", &["zip"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}.unwrap_or_default()
)
.set_title("Data archive [.zip]")
.set_file_name("beanconqueror.zip")
.pick_file().await;
if archive_file.is_none() { return; }
let archive_raw = archive_file.unwrap().read().await;
let config_content = String::from_utf8(config_raw)
.expect("Can't convert provided config file to UTF-8");
let mut config_file_lock = config_file_arc.lock().unwrap();
*config_file_lock = Some(config_content);
let mut data_archive: Option<HashMap<String, String>> = None;
if let Ok(mut zip) = zip::ZipArchive::new(Cursor::new(archive_raw)) {
let mut unzip: HashMap<String, String> = HashMap::new();
for i in 0..zip.len() {
if let Ok(mut file) = zip.by_index(i) {
if file.is_file() {
let mut content: String = String::new();
if file.read_to_string(&mut content).is_ok() {
if let Some(enclosed_name) = &file.enclosed_name() {
if let Some(filename) = enclosed_name.to_str() {
unzip.insert(filename.to_string(), content);
}
}
}
}
}
}
data_archive = Some(unzip);
}
let mut data_archive_lock = data_archive_arc.lock().unwrap();
*data_archive_lock = data_archive;
let mut files_provided_lock = files_provided_arc.lock().unwrap();
*files_provided_lock = true;
};
#[cfg(not(target_arch = "wasm32"))]
task::spawn(file_dialog);
#[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(file_dialog);
}
if ui.button("Clear Selection").clicked() {
self.clear_selection();
}
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
egui::widgets::global_dark_light_mode_buttons(ui);
@ -170,117 +276,70 @@ impl eframe::App for Ui {
.resizable(true)
.show_animated(ctx, self.side_panel_expanded, |ui| {
ui.vertical_centered_justified(|ui| {
let total_height = ui.available_height();
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if self.data_transfered {
ui.heading("Beans");
ui.heading("Beans with Brews");
egui::ScrollArea::vertical()
.id_source("beans_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
for bean in &self.database.beans {
ui.horizontal(|ui| {
if ui
.radio_value(
&mut self.selected_bean,
bean.config.uuid.to_owned(),
bean.name_with_date(),
)
.clicked_by(egui::PointerButton::Primary)
{
if ctx.input(|i| i.modifiers.command) {
self.select_all = true;
}
}
});
}
});
}
});
if let Some(brew_marked) = self.bean_brew_marked.get_mut(&bean.config.uuid) {
let id = bean.name_with_date();
ui.vertical(|ui| {
if self.data_transfered {
if let Some(bean) =
&self.database.bean_for_uuid(&self.selected_bean)
{
let brews = self.database.brews_for_bean(&bean);
ui.heading("Brews");
ui.collapsing(id, |ui| {
ui.vertical(|ui| {
let mut marked: Option<bool> = None;
let mut flip_all: Option<bool> = None;
egui::ScrollArea::vertical()
.id_source("brews_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
for brew in &brews {
ui.horizontal(|ui| {
if let Some(checked) = self
.brew_checkboxes
.get_mut(&brew.config.uuid)
{
if self.select_all {
*checked = !*checked;
}
let brews = self.database.brews_for_bean(&bean);
for brew in &brews {
ui.horizontal(|ui| {
if let Some(checked) = self
.brew_checkboxes
.get_mut(&brew.config.uuid)
{
let response = ui.toggle_value(
checked,
brew.date_time(&self.database),
);
if response.clicked_by(egui::PointerButton::Primary) {
if ctx.input(|i| i.modifiers.command) {
flip_all = Some(*checked);
*checked = !*checked;
}
}
ui.checkbox(
checked,
brew.date_time(&self.database),
);
}
});
}
if *checked {
marked = Some(true);
} else if marked.is_none() {
marked = Some(false);
}
}
});
}
self.select_all = false;
});
}
}
});
});
if let Some(value) = flip_all {
self.brew_checkboxes.iter_mut().filter(|brew_checkbox| {
brews.iter().any(|&brew| &brew.config.uuid == brew_checkbox.0)
}).for_each(|brew_checkbox| {
*brew_checkbox.1 = value;
});
ui.horizontal_centered(|ui| {
ui.vertical(|ui| {
if self.data_transfered {
ui.heading("Selected Brews");
marked = Some(value);
}
egui::ScrollArea::vertical()
.id_source("selected_brews_scroll")
.max_height(total_height / 2.0)
.show(ui, |ui| {
if self.clear_all {
self.brew_checkboxes.iter_mut().for_each(
|brew_checkbox| {
*brew_checkbox.1 = false;
},
);
self.clear_all = false;
} else {
for brew in &self.database.brews {
if let Some(checked) =
self.brew_checkboxes.get_mut(&brew.config.uuid)
{
if *checked {
ui.horizontal(|ui| {
if ui
.checkbox(
checked,
brew.date_time_with_bean(
&self.database,
),
)
.clicked_by(
egui::PointerButton::Primary,
)
{
if ctx.input(|i| i.modifiers.matches(Modifiers::CTRL)) {
self.clear_all = true;
}
match marked {
Some(marked) => *brew_marked = marked,
None => *brew_marked = false,
}
});
});
}
}
}
});
}
});
}
@ -320,6 +379,7 @@ impl eframe::App for Ui {
self.continuous_mode = true;
self.modal = true;
self.show_loading_screen = true;
self.data_transfered = false;
let config_file_arc = self.config_file.clone();
let data_archive_arc = self.data_archive.clone();
@ -347,14 +407,14 @@ impl eframe::App for Ui {
loading_data.brew_checkboxes = checkboxes;
if !&loading_data.database.beans.is_empty() {
loading_data.selected_bean =
loading_data.database.beans[0]
.config
.uuid
.to_owned();
let mut brew_marked = HashMap::new();
for bean in &loading_data.database.beans {
brew_marked.insert(bean.config.uuid.to_owned(), false);
}
loading_data.bean_brew_marked = brew_marked;
loading_data.plot_entries = plot::database_plot_entries(
loading_data
.database
@ -392,7 +452,7 @@ impl eframe::App for Ui {
self.config = loading_data.config;
self.database = loading_data.database;
self.brew_checkboxes = loading_data.brew_checkboxes;
self.selected_bean = loading_data.selected_bean;
self.bean_brew_marked = loading_data.bean_brew_marked;
self.plot_entries = loading_data.plot_entries;
self.data_transfered = true;
@ -425,99 +485,6 @@ impl eframe::App for Ui {
});
}
}
} else {
ui.label("Select the required files to start");
if ui.button("Load files").clicked() {
let config_file_arc = self.config_file.clone();
let data_archive_arc = self.data_archive.clone();
let files_provided_arc = self.files_provided.clone();
let file_dialog = async move {
let _config_message = AsyncMessageDialog::new()
.set_title("Select config file")
.set_description("Please select the config (.toml) file in the next dialog.\n(Default \"config.toml\")")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
let config_file = AsyncFileDialog::new()
.add_filter("toml", &["toml"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}.unwrap_or_default()
)
.set_title("Config file [.toml]")
.set_file_name("config.toml")
.pick_file().await;
let config_raw = config_file.unwrap().read().await;
let _archive_message = AsyncMessageDialog::new()
.set_title("Select data archive")
.set_description("Please select the data archive (.zip) in the next dialog.\n(Default \"config.toml\")\n\nWihin the zip archive there should be the main json file (e.g. \"Beanconqueror.json\"), as well as a \"brews\" folder, with exact paths specified in the previously selected config file.")
.set_buttons(rfd::MessageButtons::Ok)
.set_level(rfd::MessageLevel::Info)
.show().await;
let archive_file = AsyncFileDialog::new()
.add_filter("zip", &["zip"])
.set_directory(
match &env::current_dir() {
Ok(path) => path.to_str(),
Err(_) => None,
}.unwrap_or_default()
)
.set_title("Data archive [.zip]")
.set_file_name("beanconqueror.zip")
.pick_file().await;
let archive_raw = archive_file.unwrap().read().await;
let config_content = String::from_utf8(config_raw)
.expect("Can't convert provided config file to UTF-8");
let mut config_file_lock = config_file_arc.lock().unwrap();
*config_file_lock = Some(config_content);
let mut data_archive: Option<HashMap<String, String>> = None;
if let Ok(mut zip) = zip::ZipArchive::new(Cursor::new(archive_raw)) {
let mut unzip: HashMap<String, String> = HashMap::new();
for i in 0..zip.len() {
if let Ok(mut file) = zip.by_index(i) {
if file.is_file() {
let mut content: String = String::new();
if file.read_to_string(&mut content).is_ok() {
if let Some(enclosed_name) = &file.enclosed_name() {
if let Some(filename) = enclosed_name.to_str() {
unzip.insert(filename.to_string(), content);
}
}
}
}
}
}
data_archive = Some(unzip);
}
let mut data_archive_lock = data_archive_arc.lock().unwrap();
*data_archive_lock = data_archive;
let mut files_provided_lock = files_provided_arc.lock().unwrap();
*files_provided_lock = true;
};
#[cfg(not(target_arch = "wasm32"))]
task::spawn(file_dialog);
#[cfg(target_arch = "wasm32")]
wasm_bindgen_futures::spawn_local(file_dialog);
}
}
},
);