Skip to content

Commit 3fd64eb

Browse files
authored
feat: add mouse scroll to increase/decrease volume (#931)
Adds mouse scroll support to increase/decrease volume. Resolves #446. In `app.toml`: ``` enable_mouse_scroll_volume = true # enable mouse scrolling to change volume volume_scroll_step = 5 # percent to change per scroll (default) ```
1 parent 8009a68 commit 3fd64eb

3 files changed

Lines changed: 51 additions & 20 deletions

File tree

docs/config.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ A sample `app.toml` is available at [examples/app.toml](../examples/app.toml).
6464
| `cover_img_pixels` | Pixels per side for cover image (requires `pixelate` feature). | `16` |
6565
| `seek_duration_secs` | Seek duration in seconds for seek commands. | `5` |
6666
| `sort_artist_albums_by_type` | Sort albums by type on artist pages. | `false` |
67+
| `volume_scroll_step` | Volume change step when using mouse scroll. | `5` |
68+
| `enable_mouse_scroll_volume` | Enable volume control via mouse scroll. | `true` |
6769
| `device` | Device configuration (see below). | See below |
6870

6971
### Notes

spotify_player/src/config/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ pub struct AppConfig {
127127
pub seek_duration_secs: u16,
128128

129129
pub sort_artist_albums_by_type: bool,
130+
131+
pub volume_scroll_step: u8,
132+
pub enable_mouse_scroll_volume: bool,
130133
}
131134

132135
#[derive(Debug, Deserialize, Serialize, Clone)]
@@ -372,6 +375,9 @@ impl Default for AppConfig {
372375
seek_duration_secs: 5,
373376

374377
sort_artist_albums_by_type: false,
378+
379+
volume_scroll_step: 5,
380+
enable_mouse_scroll_volume: true,
375381
}
376382
}
377383
}

spotify_player/src/event/mod.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,28 +65,51 @@ fn handle_mouse_event(
6565
client_pub: &flume::Sender<ClientRequest>,
6666
state: &SharedState,
6767
) -> Result<()> {
68-
// a left click event
69-
if let crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left) = event.kind
70-
{
71-
tracing::debug!("Handling mouse event: {event:?}");
72-
let rect = state.ui.lock().playback_progress_bar_rect;
73-
if event.row == rect.y {
74-
// calculate the seek position (in ms) based on the mouse click position,
75-
// the progress bar's width and the track's duration (in ms)
76-
let player = state.player.read();
77-
let duration = match player.currently_playing() {
78-
Some(rspotify::model::PlayableItem::Track(track)) => Some(track.duration),
79-
Some(rspotify::model::PlayableItem::Episode(episode)) => Some(episode.duration),
80-
Some(rspotify::model::PlayableItem::Unknown(_)) | None => None,
81-
};
82-
if let Some(duration) = duration {
83-
let position_ms =
84-
(duration.num_milliseconds()) * i64::from(event.column) / i64::from(rect.width);
85-
client_pub.send(ClientRequest::Player(PlayerRequest::SeekTrack(
86-
chrono::Duration::try_milliseconds(position_ms).unwrap(),
87-
)))?;
68+
tracing::debug!("Handling mouse event: {event:?}");
69+
70+
let enable_scroll = config::get_config().app_config.enable_mouse_scroll_volume;
71+
72+
match event.kind {
73+
crossterm::event::MouseEventKind::ScrollUp if enable_scroll => {
74+
let step = config::get_config().app_config.volume_scroll_step;
75+
if let Some(ref playback) = state.player.read().buffered_playback {
76+
if let Some(volume) = playback.volume {
77+
let new_volume = std::cmp::min(volume as u8 + step, 100);
78+
client_pub.send(ClientRequest::Player(PlayerRequest::Volume(new_volume)))?;
79+
}
80+
}
81+
}
82+
crossterm::event::MouseEventKind::ScrollDown if enable_scroll => {
83+
let step = config::get_config().app_config.volume_scroll_step;
84+
if let Some(ref playback) = state.player.read().buffered_playback {
85+
if let Some(volume) = playback.volume {
86+
let new_volume = (volume as u8).saturating_sub(step);
87+
client_pub.send(ClientRequest::Player(PlayerRequest::Volume(new_volume)))?;
88+
}
89+
}
90+
}
91+
// a left click event
92+
crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left) => {
93+
let rect = state.ui.lock().playback_progress_bar_rect;
94+
if event.row == rect.y {
95+
// calculate the seek position (in ms) based on the mouse click position,
96+
// the progress bar's width and the track's duration (in ms)
97+
let player = state.player.read();
98+
let duration = match player.currently_playing() {
99+
Some(rspotify::model::PlayableItem::Track(track)) => Some(track.duration),
100+
Some(rspotify::model::PlayableItem::Episode(episode)) => Some(episode.duration),
101+
Some(rspotify::model::PlayableItem::Unknown(_)) | None => None,
102+
};
103+
if let Some(duration) = duration {
104+
let position_ms = (duration.num_milliseconds()) * i64::from(event.column)
105+
/ i64::from(rect.width);
106+
client_pub.send(ClientRequest::Player(PlayerRequest::SeekTrack(
107+
chrono::Duration::try_milliseconds(position_ms).unwrap(),
108+
)))?;
109+
}
88110
}
89111
}
112+
_ => {}
90113
}
91114
Ok(())
92115
}

0 commit comments

Comments
 (0)