mirror of
https://github.com/arabianq/pipewire-soundpad.git
synced 2026-04-27 22:11:22 +00:00
feat: add master volume slider
This commit is contained in:
+49
-13
@@ -18,6 +18,18 @@ enum TrackAction {
|
||||
}
|
||||
|
||||
impl SoundpadGui {
|
||||
fn get_volume_icon(volume: f32) -> &'static str {
|
||||
if volume > 0.7 {
|
||||
icons::ICON_VOLUME_UP
|
||||
} else if volume <= 0.0 {
|
||||
icons::ICON_VOLUME_OFF
|
||||
} else if volume < 0.3 {
|
||||
icons::ICON_VOLUME_MUTE
|
||||
} else {
|
||||
icons::ICON_VOLUME_DOWN
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_waiting_for_daemon(&mut self, ui: &mut Ui) {
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(
|
||||
@@ -215,17 +227,10 @@ impl SoundpadGui {
|
||||
// --------------------------------
|
||||
|
||||
// ---------- Volume Icon ----------
|
||||
let volume_icon = if track.volume > 0.7 {
|
||||
icons::ICON_VOLUME_UP
|
||||
} else if track.volume == 0.0 {
|
||||
icons::ICON_VOLUME_OFF
|
||||
} else if track.volume < 0.3 {
|
||||
icons::ICON_VOLUME_MUTE
|
||||
} else {
|
||||
icons::ICON_VOLUME_DOWN
|
||||
};
|
||||
let volume_icon = Label::new(RichText::new(volume_icon).size(18.0));
|
||||
ui.add_sized([30.0, 30.0], volume_icon);
|
||||
let volume_icon = Self::get_volume_icon(track.volume);
|
||||
let volume_label = Label::new(RichText::new(volume_icon).size(18.0));
|
||||
ui.add_sized([30.0, 30.0], volume_label)
|
||||
.on_hover_text(format!("Volume: {:.0}%", track.volume * 100.0));
|
||||
// --------------------------------
|
||||
|
||||
// ---------- Volume Slider ----------
|
||||
@@ -396,7 +401,7 @@ impl SoundpadGui {
|
||||
|
||||
fn draw_footer(&mut self, ui: &mut Ui) {
|
||||
ui.add_space(5.0);
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// ---------- Microphone selection ----------
|
||||
let mut mics: Vec<(&String, &String)> =
|
||||
self.audio_player_state.all_inputs.iter().collect();
|
||||
@@ -405,6 +410,7 @@ impl SoundpadGui {
|
||||
let mut selected_input = self.audio_player_state.current_input.to_owned();
|
||||
let prev_input = selected_input.to_owned();
|
||||
ComboBox::from_label("Choose microphone")
|
||||
.height(30.0)
|
||||
.selected_text(
|
||||
self.audio_player_state
|
||||
.all_inputs
|
||||
@@ -422,10 +428,40 @@ impl SoundpadGui {
|
||||
}
|
||||
// --------------------------------
|
||||
|
||||
// ---------- Master Volume Slider ----------
|
||||
let volume_icon = Self::get_volume_icon(self.audio_player_state.volume);
|
||||
let volume_label = Label::new(RichText::new(volume_icon).size(18.0));
|
||||
ui.add_sized([18.0, 18.0], volume_label)
|
||||
.on_hover_text(format!(
|
||||
"Master Volume: {:.0}%",
|
||||
self.audio_player_state.volume * 100.0
|
||||
));
|
||||
|
||||
let should_update_volume = !self.app_state.volume_dragged
|
||||
&& self
|
||||
.app_state
|
||||
.ignore_volume_update_until
|
||||
.map(|t| Instant::now() > t)
|
||||
.unwrap_or(true);
|
||||
|
||||
if should_update_volume {
|
||||
self.app_state.volume_slider_value = self.audio_player_state.volume;
|
||||
}
|
||||
|
||||
let volume_slider = Slider::new(&mut self.app_state.volume_slider_value, 0.0..=1.0)
|
||||
.show_value(false)
|
||||
.step_by(0.01);
|
||||
let volume_slider_response = ui.add_sized([150.0, 18.0], volume_slider);
|
||||
if volume_slider_response.drag_stopped() {
|
||||
self.app_state.volume_dragged = true;
|
||||
}
|
||||
// ------------------------------------------
|
||||
|
||||
ui.add_space(ui.available_width() - 18.0 - ui.spacing().item_spacing.x);
|
||||
|
||||
// ---------- Settings button ----------
|
||||
let settings_button = Button::new(icons::ICON_SETTINGS).frame(false);
|
||||
let settings_button =
|
||||
Button::new(icons::ICON_SETTINGS.atom_size(Vec2::new(18.0, 18.0))).frame(false);
|
||||
let settings_button_response = ui.add_sized([18.0, 18.0], settings_button);
|
||||
if settings_button_response.clicked() {
|
||||
self.app_state.show_settings = true;
|
||||
|
||||
+1
-1
@@ -17,9 +17,9 @@ use pwsp::{
|
||||
},
|
||||
};
|
||||
use rfd::FileDialog;
|
||||
use std::path::PathBuf;
|
||||
use std::{
|
||||
error::Error,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
|
||||
+22
-1
@@ -3,7 +3,10 @@ use eframe::{App, Frame as EFrame};
|
||||
use egui::{CentralPanel, Context};
|
||||
use pwsp::{
|
||||
types::socket::Request,
|
||||
utils::{daemon::is_daemon_running, gui::make_request_sync},
|
||||
utils::{
|
||||
daemon::{get_daemon_config, is_daemon_running},
|
||||
gui::make_request_sync,
|
||||
},
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -40,6 +43,24 @@ impl App for SoundpadGui {
|
||||
}
|
||||
}
|
||||
|
||||
if self.app_state.volume_dragged {
|
||||
make_request_sync(Request::set_volume(
|
||||
self.app_state.volume_slider_value,
|
||||
None,
|
||||
))
|
||||
.ok();
|
||||
|
||||
self.app_state.volume_dragged = false;
|
||||
self.app_state.ignore_volume_update_until =
|
||||
Some(Instant::now() + Duration::from_millis(200));
|
||||
|
||||
if self.config.save_volume {
|
||||
let mut daemon_config = get_daemon_config();
|
||||
daemon_config.default_volume = Some(self.app_state.volume_slider_value);
|
||||
daemon_config.save_to_file().ok();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let guard = self.audio_player_state_shared.lock().unwrap();
|
||||
self.audio_player_state = guard.clone();
|
||||
|
||||
@@ -12,8 +12,10 @@ use std::{
|
||||
pub struct TrackUiState {
|
||||
pub position_slider_value: f32,
|
||||
pub volume_slider_value: f32,
|
||||
|
||||
pub position_dragged: bool,
|
||||
pub volume_dragged: bool,
|
||||
|
||||
pub ignore_position_update_until: Option<Instant>,
|
||||
pub ignore_volume_update_until: Option<Instant>,
|
||||
}
|
||||
@@ -25,6 +27,11 @@ pub struct AppState {
|
||||
pub track_ui_states: HashMap<u32, TrackUiState>,
|
||||
|
||||
pub show_settings: bool,
|
||||
pub volume_dragged: bool,
|
||||
|
||||
pub volume_slider_value: f32,
|
||||
|
||||
pub ignore_volume_update_until: Option<Instant>,
|
||||
|
||||
pub current_dir: Option<PathBuf>,
|
||||
pub dirs: HashSet<PathBuf>,
|
||||
@@ -43,6 +50,8 @@ pub struct AudioPlayerState {
|
||||
|
||||
pub tracks: Vec<TrackInfo>,
|
||||
|
||||
pub volume: f32, // Master volume
|
||||
|
||||
pub current_input: String,
|
||||
pub all_inputs: HashMap<String, String>,
|
||||
}
|
||||
|
||||
+10
-2
@@ -50,18 +50,21 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
|
||||
|
||||
let state_req = Request::get_state();
|
||||
let tracks_req = Request::get_tracks();
|
||||
let volume_req = Request::get_volume();
|
||||
let current_input_req = Request::get_input();
|
||||
let all_inputs_req = Request::get_inputs();
|
||||
|
||||
let (state_res, tracks_res, current_input_res, all_inputs_res) = tokio::join!(
|
||||
let (state_res, tracks_res, volume_res, current_input_res, all_inputs_res) = tokio::join!(
|
||||
make_request(state_req),
|
||||
make_request(tracks_req),
|
||||
make_request(volume_req),
|
||||
make_request(current_input_req),
|
||||
make_request(all_inputs_req),
|
||||
);
|
||||
|
||||
let state_res = state_res.unwrap_or_default();
|
||||
let tracks_res = tracks_res.unwrap_or_default();
|
||||
let volume_res = volume_res.unwrap_or_default();
|
||||
let current_input_res = current_input_res.unwrap_or_default();
|
||||
let all_inputs_res = all_inputs_res.unwrap_or_default();
|
||||
|
||||
@@ -77,6 +80,11 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
|
||||
false => vec![],
|
||||
};
|
||||
|
||||
let volume = match volume_res.status {
|
||||
true => volume_res.message.parse::<f32>().unwrap(),
|
||||
false => 0.0,
|
||||
};
|
||||
|
||||
let current_input = match current_input_res.status {
|
||||
true => current_input_res
|
||||
.message
|
||||
@@ -117,7 +125,7 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
|
||||
None => state,
|
||||
};
|
||||
guard.tracks = tracks.clone();
|
||||
|
||||
guard.volume = volume;
|
||||
guard.current_input = current_input;
|
||||
guard.all_inputs = all_inputs;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user