fix: guard preview updates behind blocking validation

Pause preview updates when the workspace config is not saveable, surface that state in the Live Preview panel, and treat incomplete threshold color setups as real blocking errors so MangoHud preview never sees the bad intermediate state.
This commit is contained in:
2026-03-31 20:01:35 -04:00
parent 7de8224e67
commit 86c4a11321
11 changed files with 392 additions and 83 deletions
+20 -10
View File
@@ -1412,6 +1412,8 @@ fn install_window_actions(
state.config = snapshot;
state.config.dirty = true;
state.dirty = true;
state.validation.clear();
state.auto_disabled_dependents.clear();
changed = true;
}
}
@@ -2516,10 +2518,9 @@ fn load_config_into_state(
if let Ok(mut guard) = state.lock() {
guard.config = parsed;
guard.saved_snapshot = guard.config.clone();
guard.redo_snapshot = None;
guard.config.dirty = false;
guard.dirty = false;
guard.validation.clear();
clear_workspace_session_state(&mut guard);
}
if let Some(settings) = settings {
let _ = settings.set_string("last-config-path", &path.display().to_string());
@@ -2651,8 +2652,7 @@ fn restore_latest_backup_into_state(state: &Arc<Mutex<AppState>>) -> anyhow::Res
.map_err(|_| anyhow::anyhow!("failed to lock app state"))?;
guard.config = parsed;
guard.dirty = true;
guard.validation.clear();
guard.redo_snapshot = None;
clear_workspace_session_state(&mut guard);
Ok(backup)
}
@@ -2667,8 +2667,7 @@ fn reset_config_to_defaults(
let path = guard.config.path.clone();
guard.config = default_config_for_path(path);
guard.dirty = true;
guard.validation.clear();
guard.redo_snapshot = None;
clear_workspace_session_state(&mut guard);
drop(guard);
reset_app_preferences_to_defaults(settings);
true
@@ -2783,6 +2782,12 @@ fn refresh_workspace_after_config_load(
);
}
fn clear_workspace_session_state(state: &mut AppState) {
state.validation.clear();
state.redo_snapshot = None;
state.auto_disabled_dependents.clear();
}
fn run_workspace_bool_action(
state: &Arc<Mutex<AppState>>,
save_button: &libadwaita::SplitButton,
@@ -2826,10 +2831,9 @@ fn reload_config_from_disk(
if let Ok(mut state) = state.lock() {
state.config = parsed;
state.saved_snapshot = state.config.clone();
state.redo_snapshot = None;
state.config.dirty = false;
state.dirty = false;
state.validation.clear();
clear_workspace_session_state(&mut state);
}
if let Some(settings) = settings {
let _ = settings.set_string("last-config-path", &path.display().to_string());
@@ -2891,6 +2895,7 @@ fn restore_saved_snapshot(state: &Arc<Mutex<AppState>>) -> bool {
state.config.dirty = false;
state.dirty = false;
state.validation.clear();
state.auto_disabled_dependents.clear();
true
}
@@ -3097,7 +3102,7 @@ mod tests {
use mangotune::config::normalize::normalize_legacy_option_values;
use mangotune::config::parser::Parser;
use mangotune::config::types::ConfigValue;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
@@ -3141,6 +3146,10 @@ mod tests {
let saved = Parser::parse_str("fps\n", None);
let mut current = Parser::parse_str("frametime\n", None);
current.dirty = true;
let auto_disabled_dependents = HashMap::from([(
"fps".to_string(),
HashSet::from(["fps_color_change".to_string()]),
)]);
let state = Arc::new(Mutex::new(AppState {
config: current.clone(),
@@ -3148,7 +3157,7 @@ mod tests {
dirty: true,
saved_snapshot: saved.clone(),
redo_snapshot: None,
auto_disabled_dependents: HashMap::new(),
auto_disabled_dependents,
}));
assert!(restore_saved_snapshot(&state));
@@ -3161,6 +3170,7 @@ mod tests {
guard.redo_snapshot.as_ref().map(|cfg| &cfg.options),
Some(&current.options)
);
assert!(guard.auto_disabled_dependents.is_empty());
}
#[test]