Skip to content

Commit e45d368

Browse files
committed
feat: add support to change the volume scrolling on the volume icon
1 parent 95669fc commit e45d368

File tree

3 files changed

+118
-8
lines changed

3 files changed

+118
-8
lines changed

src/clients/volume/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub enum Event {
2828
AddInput(SinkInput),
2929
UpdateInput(SinkInput),
3030
RemoveInput(u32),
31+
VolumeUp(f64),
32+
VolumeDown(f64),
3133
}
3234

3335
#[derive(Debug)]
@@ -308,4 +310,18 @@ pub fn percent_to_volume(target_percent: f64) -> u32 {
308310
}
309311
}
310312

313+
pub fn scroll_to_volume(current_value: u32, scroll_value: f64) -> u32 {
314+
let mut val: i32 = current_value as i32;
315+
let scroll_delta = scroll_value * -1000.0;
316+
trace!("base+delta: {val} + {scroll_delta} orig: scroll_value");
317+
val += scroll_delta as i32;
318+
if val > Volume::NORMAL.0 as i32 {
319+
Volume::NORMAL.0 as u32
320+
} else if val < Volume::MUTED.0 as i32 {
321+
Volume::MUTED.0 as u32
322+
} else {
323+
val as u32
324+
}
325+
}
326+
311327
register_client!(Client, volume);

src/clients/volume/sink.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use super::{ArcMutVec, Client, ConnectionState, Event, percent_to_volume, volume_to_percent};
1+
use super::{
2+
ArcMutVec, Client, ConnectionState, Event, percent_to_volume, scroll_to_volume,
3+
volume_to_percent,
4+
};
25
use crate::{lock, send};
36
use libpulse_binding::callbacks::ListResult;
47
use libpulse_binding::context::Context;
@@ -53,6 +56,40 @@ impl Client {
5356
}
5457
}
5558

59+
#[instrument(level = "trace")]
60+
pub fn set_default_volume(&self, value: f64) {
61+
trace!("raceived volume change: {value:?}");
62+
let mut active = None;
63+
for sync in &*lock!(self.data.sinks) {
64+
if sync.active {
65+
active = Some(sync.name.to_string());
66+
break;
67+
}
68+
}
69+
if let Some(name) = active {
70+
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {
71+
let (tx, rx) = mpsc::channel();
72+
73+
introspector.get_sink_info_by_name(&name, move |info| {
74+
let ListResult::Item(info) = info else {
75+
return;
76+
};
77+
send!(tx, info.volume);
78+
});
79+
80+
let mut volume = rx.recv().expect("to receive info");
81+
for v in volume.get_mut() {
82+
let dval = v.0;
83+
let val = scroll_to_volume(v.0, value);
84+
trace!("changing value from: {dval:?} to: {val}");
85+
v.0 = val;
86+
}
87+
88+
introspector.set_sink_volume_by_name(&name, &volume, None);
89+
}
90+
}
91+
}
92+
5693
#[instrument(level = "trace")]
5794
pub fn set_sink_volume(&self, name: &str, volume_percent: f64) {
5895
if let ConnectionState::Connected { introspector, .. } = &mut *lock!(self.connection) {

src/modules/volume.rs

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ use crate::modules::{
66
};
77
use crate::{glib_recv, lock, module_impl, send_async, spawn, try_send};
88
use glib::Propagation;
9+
use gtk::gdk::{EventMask, ScrollDirection};
910
use gtk::pango::EllipsizeMode;
1011
use gtk::prelude::*;
11-
use gtk::{Button, CellRendererText, ComboBoxText, Label, Orientation, Scale, ToggleButton};
12+
use gtk::{
13+
Button, CellRendererText, ComboBoxText, EventBox, Label, Orientation, Scale, ToggleButton,
14+
};
1215
use serde::Deserialize;
1316
use std::collections::HashMap;
1417
use tokio::sync::mpsc;
@@ -128,9 +131,11 @@ pub enum Update {
128131

129132
InputVolume(u32, f64),
130133
InputMute(u32, bool),
134+
InputVolumeUp(f64),
135+
InputVolumeDown(f64),
131136
}
132137

133-
impl Module<Button> for VolumeModule {
138+
impl Module<EventBox> for VolumeModule {
134139
type SendMessage = Event;
135140
type ReceiveMessage = Update;
136141

@@ -143,7 +148,7 @@ impl Module<Button> for VolumeModule {
143148
mut rx: mpsc::Receiver<Self::ReceiveMessage>,
144149
) -> color_eyre::Result<()>
145150
where
146-
<Self as Module<Button>>::SendMessage: Clone,
151+
<Self as Module<EventBox>>::SendMessage: Clone,
147152
{
148153
let client = context.client::<volume::Client>();
149154

@@ -197,6 +202,8 @@ impl Module<Button> for VolumeModule {
197202
Update::SinkVolume(name, volume) => client.set_sink_volume(&name, volume),
198203
Update::SinkMute(name, muted) => client.set_sink_muted(&name, muted),
199204
Update::InputVolume(index, volume) => client.set_input_volume(index, volume),
205+
Update::InputVolumeUp(val) => client.set_default_volume(val),
206+
Update::InputVolumeDown(val) => client.set_default_volume(val),
200207
Update::InputMute(index, muted) => client.set_input_muted(index, muted),
201208
}
202209
}
@@ -209,17 +216,17 @@ impl Module<Button> for VolumeModule {
209216
self,
210217
context: WidgetContext<Self::SendMessage, Self::ReceiveMessage>,
211218
info: &ModuleInfo,
212-
) -> color_eyre::Result<ModuleParts<Button>>
219+
) -> color_eyre::Result<ModuleParts<EventBox>>
213220
where
214-
<Self as Module<Button>>::SendMessage: Clone,
221+
<Self as Module<EventBox>>::SendMessage: Clone,
215222
{
216223
let button_label = Label::builder()
217224
.use_markup(true)
218225
.angle(self.layout.angle(info))
219226
.justify(self.layout.justify.into())
220227
.build();
221228

222-
let button = Button::new();
229+
let button = Button::builder().build();
223230
button.add(&button_label);
224231

225232
{
@@ -251,6 +258,49 @@ impl Module<Button> for VolumeModule {
251258
});
252259
}
253260

261+
let event_box = EventBox::builder()
262+
.events(
263+
EventMask::SCROLL_MASK
264+
| EventMask::SMOOTH_SCROLL_MASK
265+
| EventMask::BUTTON_MOTION_MASK,
266+
)
267+
.child(&button)
268+
.build();
269+
270+
{
271+
let tx = context.tx.clone();
272+
event_box.connect_scroll_event(move |_button, scroll| {
273+
match scroll.direction() {
274+
ScrollDirection::Up => {
275+
try_send!(
276+
tx,
277+
ModuleUpdateEvent::Update(Event::VolumeUp(scroll.delta().1))
278+
);
279+
}
280+
ScrollDirection::Down => {
281+
try_send!(
282+
tx,
283+
ModuleUpdateEvent::Update(Event::VolumeDown(scroll.delta().1))
284+
);
285+
}
286+
ScrollDirection::Smooth => {
287+
if scroll.scroll_deltas().unwrap_or_default().1 > 0.0 {
288+
try_send!(
289+
tx,
290+
ModuleUpdateEvent::Update(Event::VolumeUp(scroll.delta().1))
291+
);
292+
} else {
293+
try_send!(
294+
tx,
295+
ModuleUpdateEvent::Update(Event::VolumeDown(scroll.delta().1))
296+
);
297+
}
298+
}
299+
_ => {}
300+
}
301+
Propagation::Stop
302+
});
303+
}
254304
let popup = self
255305
.into_popup(
256306
context.controller_tx.clone(),
@@ -260,7 +310,7 @@ impl Module<Button> for VolumeModule {
260310
)
261311
.into_popup_parts(vec![&button]);
262312

263-
Ok(ModuleParts::new(button, popup))
313+
Ok(ModuleParts::new(event_box, popup))
264314
}
265315

266316
fn into_popup(
@@ -460,6 +510,13 @@ impl Module<Button> for VolumeModule {
460510
input_container.remove(&ui.container);
461511
}
462512
}
513+
Event::VolumeUp(val) => {
514+
try_send!(tx, Update::InputVolumeUp(val));
515+
}
516+
517+
Event::VolumeDown(val) => {
518+
try_send!(tx, Update::InputVolumeDown(val));
519+
}
463520
}
464521
});
465522
}

0 commit comments

Comments
 (0)