Change up the ui quite a bit, but make it cleaner
This commit is contained in:
parent
2acb594c28
commit
5e3e30c30e
389
src/ui.rs
389
src/ui.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue