1use std::{fmt, path::PathBuf};
2
3use thedes_settings::{AudioSinkType, Settings};
4
5pub use thedes_settings::SaveError;
6use thedes_tui::{
7 cancellability::Cancellable,
8 core::{
9 App,
10 audio::{self, Volume, device::SetVolumeError},
11 },
12 menu::{self, Menu},
13 slidebar::{self, Slidebar},
14};
15use thiserror::Error;
16
17#[derive(Debug, Error)]
18pub enum InitError {
19 #[error("Failed to create main settings menu")]
20 MainSettingsMenu(#[source] menu::Error),
21 #[error("Failed to create audio settings menu")]
22 AudioSettingsMenu(#[source] menu::Error),
23}
24
25#[derive(Debug, Error)]
26pub enum LoadError {
27 #[error(transparent)]
28 LoadSettings(#[from] thedes_settings::LoadError),
29 #[error("Failed to initialize component")]
30 Init(#[from] InitError),
31}
32
33#[derive(Debug, Error)]
34pub enum Error {
35 #[error("Failed to run main settings menu")]
36 MainSettingsMenu(#[source] menu::Error),
37 #[error("Failed to run audio settings menu")]
38 AudioSettingsMenu(#[source] menu::Error),
39 #[error("Failed to set audio output volume")]
40 AudioSetVolume(#[from] SetVolumeError),
41 #[error("Failed to manipulate slidebar")]
42 Slidebar(#[from] slidebar::Error),
43 #[error("Failed to save settings")]
44 Save(#[from] SaveError),
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
48enum MainSettingsItem {
49 Audio,
50}
51
52impl fmt::Display for MainSettingsItem {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 f.write_str(match self {
55 Self::Audio => "Audio Settings",
56 })
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
61enum AudioSettingsItem {
62 Music,
63 Fx,
64}
65
66impl fmt::Display for AudioSettingsItem {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.write_str(match self {
69 Self::Music => "Set Music Volume",
70 Self::Fx => "Set FX Volume",
71 })
72 }
73}
74
75#[derive(Debug, Clone)]
76pub struct Component {
77 main_settings_menu: Menu<MainSettingsItem, Cancellable>,
78 audio_settings_menu: Menu<AudioSettingsItem, Cancellable>,
79 audio_music_slidebar: Slidebar,
80 audio_fx_slidebar: Slidebar,
81 settings: Settings,
82 path: PathBuf,
83}
84
85impl Component {
86 pub fn new(path: PathBuf, settings: Settings) -> Result<Self, InitError> {
87 let main_settings_menu = Menu::from_cancellation(
88 "% Settings %",
89 [MainSettingsItem::Audio],
90 Cancellable::new(false),
91 )
92 .map_err(InitError::MainSettingsMenu)?;
93
94 let audio_settings_menu = Menu::from_cancellation(
95 "((|> Audio Settings <|))",
96 [AudioSettingsItem::Music, AudioSettingsItem::Fx],
97 Cancellable::new(false),
98 )
99 .map_err(InitError::AudioSettingsMenu)?;
100
101 let audio_music_slidebar = Slidebar::new(
102 "Set Music Volume",
103 slidebar::Config {
104 ui_size: 17,
105 logical_size: 256,
106 logical_current: settings
107 .audio()
108 .volume(AudioSinkType::Music)
109 .into(),
110 },
111 );
112
113 let audio_fx_slidebar = Slidebar::new(
114 "Set FX Volume",
115 slidebar::Config {
116 ui_size: 17,
117 logical_size: 256,
118 logical_current: settings
119 .audio()
120 .volume(AudioSinkType::Fx)
121 .into(),
122 },
123 );
124
125 Ok(Self {
126 path,
127 settings,
128 main_settings_menu,
129 audio_settings_menu,
130 audio_music_slidebar,
131 audio_fx_slidebar,
132 })
133 }
134
135 pub async fn load(path: PathBuf) -> Result<Self, LoadError> {
136 let settings = Settings::load_or_default(&path).await;
137 Ok(Self::new(path, settings)?)
138 }
139
140 pub async fn save(&self) -> Result<(), SaveError> {
141 self.settings.save(&self.path).await?;
142 Ok(())
143 }
144
145 pub fn values(&self) -> &Settings {
146 &self.settings
147 }
148
149 pub fn values_mut(&mut self) -> &mut Settings {
150 &mut self.settings
151 }
152
153 pub async fn run(&mut self, app: &mut App) -> Result<(), Error> {
154 loop {
155 self.main_settings_menu
156 .run(app)
157 .await
158 .map_err(Error::MainSettingsMenu)?;
159 match self.main_settings_menu.output() {
160 Some(MainSettingsItem::Audio) => loop {
161 self.audio_settings_menu
162 .run(app)
163 .await
164 .map_err(Error::AudioSettingsMenu)?;
165
166 match self.audio_settings_menu.output() {
167 Some(AudioSettingsItem::Music) => {
168 self.audio_music_slidebar
169 .run(app, |app, current| {
170 let level = current as Volume;
171 self.settings.audio_mut().set_volume(
172 AudioSinkType::Music,
173 level,
174 );
175 app.audio_controller.queue([
176 audio::Command::new_set_volume(
177 AudioSinkType::Music,
178 level,
179 ),
180 ]);
181 _ = app.audio_controller.flush();
182 })
183 .await?;
184 },
185
186 Some(AudioSettingsItem::Fx) => {
187 self.audio_fx_slidebar
188 .run(app, |app, current| {
189 let level = current as Volume;
190 self.settings
191 .audio_mut()
192 .set_volume(AudioSinkType::Fx, level);
193 app.audio_controller.queue([
194 audio::Command::new_set_volume(
195 AudioSinkType::Fx,
196 level,
197 ),
198 ]);
199 _ = app.audio_controller.flush();
200 })
201 .await?;
202 },
203
204 None => break,
205 }
206 },
207 None => break,
208 }
209 }
210
211 self.save().await?;
212
213 Ok(())
214 }
215}