From 7d2a5416fac200b29750157157b12fdeeb221352 Mon Sep 17 00:00:00 2001 From: Yeicor <4929005+Yeicor@users.noreply.github.com> Date: Sat, 2 Jul 2022 18:13:19 +0200 Subject: [PATCH] Better GUI and increased resiliency of the program --- src/app/cli.rs | 48 ++++++++++++++++++++++++++++++++++-------------- src/app/mod.rs | 5 +++-- src/run.rs | 8 ++++++++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/app/cli.rs b/src/app/cli.rs index b091cc1..e601006 100644 --- a/src/app/cli.rs +++ b/src/app/cli.rs @@ -1,12 +1,13 @@ use clap::Parser; use eframe::egui; -use eframe::egui::Context; +use eframe::egui::{Context, TextEdit}; use eframe::egui::Widget; use ehttp::Request; use klask::app_state::AppState; use crate::app::SDFViewerApp; use crate::sdf::demo::SDFDemo; +use crate::sdf::SDFSurface; use crate::sdf::wasm::load_sdf_wasm_send_sync; #[derive(clap::Parser, Debug, Clone, PartialEq, Eq, Default)] @@ -71,6 +72,10 @@ impl CliApp { #[cfg(target_arch = "wasm32")] { tracing::error!("Failed to load SDF from URL: {:?}", err); + sender.send(unsafe { + // FIXME: Extremely unsafe code (forcing SDFDemo Send+Sync), but only used for this error path + std::mem::transmute(Box::new(SDFDemo::default()) as Box) + }); } #[cfg(not(target_arch = "wasm32"))] { @@ -88,6 +93,10 @@ impl CliApp { } Err(err2) => { tracing::error!("Failed to load SDF from URL ({:?}) or file ({:?})", err, err2); + sender.send(unsafe { + // FIXME: Extremely unsafe code (forcing SDFDemo Send+Sync), but only used for this error path + std::mem::transmute(Box::new(SDFDemo::default()) as Box) + }); } } } @@ -133,16 +142,14 @@ impl SDFViewerAppSettings { } fn editing_to_instance(editing: &AppState) -> CliApp { - let args = editing.get_cmd_args(vec!["app".to_string()]) - .or_else(|err| { - tracing::error!("Failed to parse CLI arguments (from settings GUI): {:?}", err); - Ok::<_, ()>(vec![]) - }).unwrap(); - ::try_parse_from(&args) - .or_else(|err| { - tracing::error!("Failed to parse CLI arguments (from settings GUI): {:?} --> {:?}", args, err); - Ok::<_, ()>(CliApp::default()) - }).unwrap() + let args = editing.get_cmd_args(vec!["app".to_string()]).unwrap_or_else(|err| { + tracing::error!("Failed to parse CLI arguments (from settings GUI): {:?}", err); + vec![] + }); + ::try_parse_from(&args).unwrap_or_else(|err| { + tracing::error!("Failed to parse CLI arguments (from settings GUI): {:?} --> {:?}", args, err); + CliApp::default() + }) } pub fn show(app: &mut SDFViewerApp, ctx: &Context) { @@ -161,7 +168,7 @@ impl SDFViewerAppSettings { <&mut AppState>::ui(editing.as_mut().unwrap(), ui); // # The cancel and apply buttons - ui.columns(2, |ui| { + let action = ui.columns(2, |ui| { if ui[0].button("Cancel").clicked() { return Some(SDFViewerAppSettings::Configured { settings: CliApp::clone(previous.as_ref().unwrap()) }); } @@ -170,9 +177,21 @@ impl SDFViewerAppSettings { return Some(SDFViewerAppSettings::Configured { settings: new_settings }); } None - }) + }); + + // The command line arguments that would generate this config (for copying) + let cmd_args = editing.as_mut().unwrap() + .get_cmd_args(vec![env!("CARGO_PKG_NAME").to_string()]) + .unwrap_or_else(|err| vec![format!("Invalid configuration: {}", err)]); + ui.horizontal_wrapped(|ui| { + ui.label("CLI: "); + let mut cmd_args_str = cmd_args.join(" "); // TODO: Proper escaping + TextEdit::singleline(&mut cmd_args_str) // Not editable, but copyable + .desired_width(ui.available_width()) + .ui(ui) + }); - // TODO: The command line arguments that would generate this config (for copying) + action }).and_then(|r| r.inner.flatten()); if prev_open && !open { // Closing the window is the same as cancelling change_state = Some(SDFViewerAppSettings::Configured { settings: CliApp::clone(previous.as_ref().unwrap()) }); @@ -180,6 +199,7 @@ impl SDFViewerAppSettings { if let Some(new_state) = change_state { if app.settings_values.previous() != &new_state.current() { + // TODO: Apply only the modified settings (know which settings were left unchanged / as default values) new_state.current().apply(app) // TODO: Auto-refresh the whole SDF. } diff --git a/src/app/mod.rs b/src/app/mod.rs index 431ec4e..0578601 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -193,11 +193,12 @@ impl SDFViewerApp { if settings_button.response.clicked() { use clap::CommandFactory; static LOC_SETTINGS: OnceCell = OnceCell::new(); + let loc_settings = LOC_SETTINGS.get_or_init(LocalizationSettings::default); self.settings_values = SDFViewerAppSettings::Configuring { previous: values.clone(), + // TODO(klask): load initial values from the previous settings editing: klask::app_state::AppState::new( - &CliApp::command(), - LOC_SETTINGS.get_or_init(|| LocalizationSettings::default())), + &CliApp::command(), loc_settings), }; } }); diff --git a/src/run.rs b/src/run.rs index ded857f..742ee11 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,4 +1,5 @@ use eframe::AppCreator; +use klask::Settings; use tracing::info; use crate::app::SDFViewerApp; @@ -38,6 +39,13 @@ pub async fn native_main() { // Setup logging tracing::subscriber::set_global_default(tracing_subscriber::FmtSubscriber::default()) .expect("Failed to set global default subscriber"); + // If no arguments are provided, run a GUI interface for configuring the CLI arguments ;) + if std::env::args().nth(1).is_none() { + #[cfg(feature = "klask")] + klask::run_derived::(Settings::default(), |_app| { + // Ignore me: this block will be skipped the second time (as arguments will be set) + }); + } // Run app let native_options = eframe::NativeOptions::default(); if let Some(app_creator) = setup_app().await {