desktop: Persist volume to disk
This commit is contained in:
parent
e18055d351
commit
6971a1aa5e
|
@ -87,9 +87,9 @@ pub struct Opt {
|
|||
#[clap(long, short, default_value = "show-all")]
|
||||
pub scale: StageScaleMode,
|
||||
|
||||
/// Audio volume as a number between 0 (muted) and 1 (full volume)
|
||||
#[clap(long, short, default_value = "1.0")]
|
||||
pub volume: f32,
|
||||
/// Audio volume as a number between 0 (muted) and 1 (full volume). Default is 1.
|
||||
#[clap(long, short)]
|
||||
pub volume: Option<f32>,
|
||||
|
||||
/// Prevent movies from changing the stage scale mode.
|
||||
#[clap(long, action)]
|
||||
|
|
|
@ -106,7 +106,7 @@ impl RuffleGui {
|
|||
Self {
|
||||
is_about_visible: false,
|
||||
is_volume_visible: false,
|
||||
volume_controls: VolumeControls::new(false, default_player_options.volume * 100.0),
|
||||
volume_controls: VolumeControls::new(&preferences),
|
||||
is_open_dialog_visible: false,
|
||||
was_suspended_before_debug: false,
|
||||
|
||||
|
@ -481,6 +481,19 @@ impl RuffleGui {
|
|||
if let Some(player) = player {
|
||||
player.set_volume(self.volume_controls.get_volume());
|
||||
}
|
||||
// Don't update persisted volume if the CLI set it
|
||||
if self.preferences.cli.volume.is_none() {
|
||||
if let Err(e) = self.preferences.write_preferences(|writer| {
|
||||
if changed_checkbox {
|
||||
writer.set_mute(self.volume_controls.is_muted);
|
||||
}
|
||||
if changed_slider {
|
||||
writer.set_volume(self.volume_controls.volume / 100.0);
|
||||
}
|
||||
}) {
|
||||
tracing::warn!("Couldn't update volume preferences: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -568,8 +581,11 @@ pub struct VolumeControls {
|
|||
}
|
||||
|
||||
impl VolumeControls {
|
||||
fn new(is_muted: bool, volume: f32) -> Self {
|
||||
Self { is_muted, volume }
|
||||
fn new(preferences: &GlobalPreferences) -> Self {
|
||||
Self {
|
||||
is_muted: preferences.mute(),
|
||||
volume: preferences.preferred_volume() * 100.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the volume between 0 and 1 (calculated out of the
|
||||
|
|
|
@ -39,7 +39,6 @@ pub struct PlayerOptions {
|
|||
pub align: StageAlign,
|
||||
pub force_align: bool,
|
||||
pub scale: StageScaleMode,
|
||||
pub volume: f32,
|
||||
pub force_scale: bool,
|
||||
pub proxy: Option<Url>,
|
||||
pub socket_allowed: HashSet<String>,
|
||||
|
@ -68,7 +67,6 @@ impl From<&GlobalPreferences> for PlayerOptions {
|
|||
align: value.cli.align.unwrap_or_default(),
|
||||
force_align: value.cli.force_align,
|
||||
scale: value.cli.scale,
|
||||
volume: value.cli.volume,
|
||||
force_scale: value.cli.force_scale,
|
||||
proxy: value.cli.proxy.clone(),
|
||||
upgrade_to_https: value.cli.upgrade_to_https,
|
||||
|
|
|
@ -62,23 +62,23 @@ impl GlobalPreferences {
|
|||
}
|
||||
|
||||
pub fn graphics_backends(&self) -> GraphicsBackend {
|
||||
self.cli.graphics.unwrap_or(
|
||||
self.cli.graphics.unwrap_or_else(|| {
|
||||
self.preferences
|
||||
.lock()
|
||||
.expect("Preferences is not reentrant")
|
||||
.values
|
||||
.graphics_backend,
|
||||
)
|
||||
.graphics_backend
|
||||
})
|
||||
}
|
||||
|
||||
pub fn graphics_power_preference(&self) -> PowerPreference {
|
||||
self.cli.power.unwrap_or(
|
||||
self.cli.power.unwrap_or_else(|| {
|
||||
self.preferences
|
||||
.lock()
|
||||
.expect("Preferences is not reentrant")
|
||||
.values
|
||||
.graphics_power_preference,
|
||||
)
|
||||
.graphics_power_preference
|
||||
})
|
||||
}
|
||||
|
||||
pub fn language(&self) -> LanguageIdentifier {
|
||||
|
@ -99,6 +99,24 @@ impl GlobalPreferences {
|
|||
.clone()
|
||||
}
|
||||
|
||||
pub fn mute(&self) -> bool {
|
||||
self.preferences
|
||||
.lock()
|
||||
.expect("Preferences is not reentrant")
|
||||
.values
|
||||
.mute
|
||||
}
|
||||
|
||||
pub fn preferred_volume(&self) -> f32 {
|
||||
self.cli.volume.unwrap_or_else(|| {
|
||||
self.preferences
|
||||
.lock()
|
||||
.expect("Preferences is not reentrant")
|
||||
.values
|
||||
.volume
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_preferences(&self, fun: impl FnOnce(&mut PreferencesWriter)) -> Result<(), Error> {
|
||||
let mut preferences = self
|
||||
.preferences
|
||||
|
@ -138,6 +156,8 @@ pub struct SavedGlobalPreferences {
|
|||
pub graphics_power_preference: PowerPreference,
|
||||
pub language: LanguageIdentifier,
|
||||
pub output_device: Option<String>,
|
||||
pub mute: bool,
|
||||
pub volume: f32,
|
||||
}
|
||||
|
||||
impl Default for SavedGlobalPreferences {
|
||||
|
@ -151,6 +171,8 @@ impl Default for SavedGlobalPreferences {
|
|||
graphics_power_preference: Default::default(),
|
||||
language: locale,
|
||||
output_device: None,
|
||||
mute: false,
|
||||
volume: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,18 @@ pub fn read_preferences(input: &str) -> (ParseResult, Document) {
|
|||
Err(e) => result.add_warning(format!("Invalid output_device: {e}")),
|
||||
};
|
||||
|
||||
match parse_item_from_float(document.get("volume")) {
|
||||
Ok(Some(value)) => result.result.volume = value.clamp(0.0, 1.0) as f32,
|
||||
Ok(None) => {}
|
||||
Err(e) => result.add_warning(format!("Invalid volume: {e}")),
|
||||
};
|
||||
|
||||
match parse_item_from_bool(document.get("mute")) {
|
||||
Ok(Some(value)) => result.result.mute = value,
|
||||
Ok(None) => {}
|
||||
Err(e) => result.add_warning(format!("Invalid mute: {e}")),
|
||||
};
|
||||
|
||||
(result, document)
|
||||
}
|
||||
|
||||
|
@ -79,6 +91,30 @@ fn parse_item_from_str<T: FromStr + Default>(item: Option<&Item>) -> Result<Opti
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_item_from_float(item: Option<&Item>) -> Result<Option<f64>, String> {
|
||||
if let Some(item) = item {
|
||||
if let Some(value) = item.as_float() {
|
||||
Ok(Some(value))
|
||||
} else {
|
||||
Err(format!("expected float but found {}", item.type_name()))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_item_from_bool(item: Option<&Item>) -> Result<Option<bool>, String> {
|
||||
if let Some(item) = item {
|
||||
if let Some(value) = item.as_bool() {
|
||||
Ok(Some(value))
|
||||
} else {
|
||||
Err(format!("expected boolean but found {}", item.type_name()))
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -262,4 +298,76 @@ mod tests {
|
|||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mute() {
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
mute: false,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec!["Invalid mute: expected boolean but found string".to_string()]
|
||||
},
|
||||
read_preferences("mute = \"false\"").0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
mute: true,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec![]
|
||||
},
|
||||
read_preferences("mute = true").0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
mute: false,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec![]
|
||||
},
|
||||
read_preferences("").0
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn volume() {
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
volume: 1.0,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec!["Invalid volume: expected float but found string".to_string()]
|
||||
},
|
||||
read_preferences("volume = \"0.5\"").0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
volume: 0.5,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec![]
|
||||
},
|
||||
read_preferences("volume = 0.5").0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ParseResult {
|
||||
result: SavedGlobalPreferences {
|
||||
volume: 0.0,
|
||||
..Default::default()
|
||||
},
|
||||
warnings: vec![]
|
||||
},
|
||||
read_preferences("volume = -1.0").0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,16 @@ impl<'a> PreferencesWriter<'a> {
|
|||
}
|
||||
self.0.values.output_device = name;
|
||||
}
|
||||
|
||||
pub fn set_mute(&mut self, mute: bool) {
|
||||
self.0.toml_document["mute"] = value(mute);
|
||||
self.0.values.mute = mute;
|
||||
}
|
||||
|
||||
pub fn set_volume(&mut self, volume: f32) {
|
||||
self.0.toml_document["volume"] = value(volume as f64);
|
||||
self.0.values.volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -124,4 +134,19 @@ mod tests {
|
|||
"",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_volume() {
|
||||
test("", |writer| writer.set_volume(0.5), "volume = 0.5\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_mute() {
|
||||
test("", |writer| writer.set_mute(true), "mute = true\n");
|
||||
test(
|
||||
"mute = true",
|
||||
|writer| writer.set_mute(false),
|
||||
"mute = false\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue