1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Li/Mikewolfli/Wei Li(mikewolfli@163.com)
// SPDX-License-Identifier: MIT
use super::{ConfigLoadError, ConfigPersistence, MenuConfig, PerformanceLevel};
use std::io;
/// Menu configuration dialog for user preferences.
pub struct MenuConfigDialog {
config: MenuConfig,
persistence: ConfigPersistence,
}
impl MenuConfigDialog {
/// Creates a new configuration dialog.
pub fn new(config: MenuConfig) -> Self {
Self { config, persistence: ConfigPersistence::new() }
}
/// Creates a dialog with custom persistence.
pub fn with_persistence(config: MenuConfig, persistence: ConfigPersistence) -> Self {
Self { config, persistence }
}
/// Gets the current configuration.
pub fn config(&self) -> &MenuConfig {
&self.config
}
/// Gets mutable configuration.
pub fn config_mut(&mut self) -> &mut MenuConfig {
&mut self.config
}
/// Toggles animations on/off.
pub fn toggle_animations(&mut self) {
let new_value = !self.config.animations_enabled();
self.config.set_animations_enabled(new_value);
}
/// Toggles transparency on/off.
pub fn toggle_transparency(&mut self) {
let new_value = !self.config.transparency_enabled();
self.config.set_transparency_enabled(new_value);
}
/// Toggles shadows on/off.
pub fn toggle_shadows(&mut self) {
let new_value = !self.config.shadows_enabled();
self.config.set_shadows_enabled(new_value);
}
/// Toggles blur on/off.
pub fn toggle_blur(&mut self) {
let new_value = !self.config.blur_enabled();
self.config.set_blur_enabled(new_value);
}
/// Increases animation speed.
pub fn increase_animation_speed(&mut self) {
let current = self.config.animation_speed();
let new_speed = (current + 0.1).min(3.0);
self.config.set_animation_speed(new_speed);
}
/// Decreases animation speed.
pub fn decrease_animation_speed(&mut self) {
let current = self.config.animation_speed();
let new_speed = (current - 0.1).max(0.1);
self.config.set_animation_speed(new_speed);
}
/// Increases max visible items, saturating at the type's maximum rather than
/// wrapping back to a small value.
pub fn increase_max_items(&mut self) {
let current = self.config.max_visible_items();
self.config.set_max_visible_items(current.saturating_add(5));
}
/// Decreases max visible items.
pub fn decrease_max_items(&mut self) {
let current = self.config.max_visible_items();
if current > 5 {
self.config.set_max_visible_items(current - 5);
}
}
/// Resets all settings to hardware defaults.
pub fn reset_to_defaults(&mut self) {
self.config.reset_to_defaults();
}
/// Saves the current configuration.
pub fn save(&self) -> io::Result<()> {
self.persistence.save(&self.config)
}
/// Loads configuration from disk, restoring the hardware baseline first so a
/// field removed from the file falls back to its default rather than keeping a
/// stale reversed preference.
///
/// A rejected load leaves `self.config` untouched, so a parse failure cannot
/// corrupt the previous configuration.
pub fn load(&mut self) -> Result<(), ConfigLoadError> {
let overrides = self.persistence.load()?;
self.config.reload_overrides(overrides);
Ok(())
}
/// Returns a summary of current settings for display.
pub fn settings_summary(&self) -> String {
self.config.settings_summary()
}
/// Returns true if any settings have been overridden.
pub fn has_overrides(&self) -> bool {
self.config.has_user_overrides()
}
/// Returns the hardware-detected performance level.
pub fn hardware_performance_level(&self) -> PerformanceLevel {
self.config.hardware_caps().performance_level
}
/// Returns a description of the detected GPU.
///
/// The memory figure is only printed when it was actually measured. This crate
/// cannot query VRAM portably (wgpu exposes no size, the platform backends report
/// system RAM), so on most hosts the honest description is the GPU type alone —
/// the previous wording printed a defaulted `512` MB as if it had been detected.
pub fn gpu_description(&self) -> String {
let caps = self.config.hardware_caps();
match (caps.gpu_memory_mb, caps.gpu_memory_is_measured) {
(Some(mb), true) => format!("{} ({mb} MB)", caps.gpu_type.description()),
_ => format!("{} (memory not reportable)", caps.gpu_type.description()),
}
}
/// Returns a description of the detected system memory.
///
/// The same rule [`Self::gpu_description`] follows: a figure is printed only when a
/// backend actually measured it. When nothing could report physical memory the
/// description says so, and names the conservative assumption the *scoring* model
/// uses instead — clearly labelled as an assumption, so it cannot be read as a probe
/// result.
pub fn system_memory_description(&self) -> String {
match self.config.hardware_caps().system_ram_mb {
Some(mb) => format!("{mb} MB"),
None => format!(
"not reportable on this backend (scoring assumes {} MB)",
MenuConfig::assumed_system_memory()
),
}
}
}