fix(audio): accumuler les rafales scroll Pixels au lieu de signum()
Some checks failed
Continuous Integration / formatting (push) Has been cancelled
Continuous Integration / linting (push) Has been cancelled

Avec Wayland axis_v120 (scroll haute-résolution sur souris HID modernes),
un cran physique génère 5–8 events ScrollDelta::Pixels (~15–20px chacun).
L'ancien code passait chaque sub-event par .signum() puis -1/+1 à sink_volume,
donc un seul cran physique faisait varier le volume de 5 à 40% — résultat :
scroll up sur l'icône audio panel / dock coupait le son si le volume était
déjà bas.

Fix : thread_local accumulator des deltas Pixels, émission seulement
au passage du seuil de 15px par cran logique. Lines (souris classique
sans axis_v120) reste proportionnel y * WHEEL_STEP. round() au lieu de
truncation finale pour ne pas perdre les fractions de pourcent.

Leyoda 2026 - GPLv3
This commit is contained in:
Votre Nom 2026-04-26 14:51:33 +02:00
parent 936147ecf6
commit 4f5825b0b2

View file

@ -487,11 +487,31 @@ impl cosmic::Application for Audio {
.icon_button(self.output_icon_name()) .icon_button(self.output_icon_name())
.on_press_down(Message::TogglePopup); .on_press_down(Message::TogglePopup);
const WHEEL_STEP: f32 = 5.0; // 5% per wheel event const WHEEL_STEP: f32 = 5.0; // 5% par cran logique
// Wayland axis_v120 envoie un cran physique en rafale de plusieurs
// ScrollDelta::Pixels (58 events ~1520px), pour ~120px par cran. On
// accumule ces sub-events dans un thread_local et on n'émet qu'au
// passage d'un seuil — sinon `signum()` faisait croire que chaque
// sub-event = un cran, et un seul cran physique faisait chuter le
// volume jusqu'à 0 ("coupe le son").
const PIXEL_THRESHOLD: f32 = 15.0; // px par cran logique
std::thread_local! {
static PIXEL_ACC: std::cell::Cell<f32> = const { std::cell::Cell::new(0.0) };
}
let btn = crate::mouse_area::MouseArea::new(btn).on_mouse_wheel(|delta| { let btn = crate::mouse_area::MouseArea::new(btn).on_mouse_wheel(|delta| {
let scroll_vector = match delta { let scroll_vector = match delta {
iced::mouse::ScrollDelta::Lines { y, .. } => y.signum() * WHEEL_STEP, // -1/0/1 iced::mouse::ScrollDelta::Lines { y, .. } => {
iced::mouse::ScrollDelta::Pixels { y, .. } => y.signum(), // -1/0/1 PIXEL_ACC.with(|a| a.set(0.0));
y * WHEEL_STEP
}
iced::mouse::ScrollDelta::Pixels { y, .. } => {
PIXEL_ACC.with(|acc_cell| {
let acc = acc_cell.get() + y;
let steps = (acc / PIXEL_THRESHOLD).trunc();
acc_cell.set(acc - steps * PIXEL_THRESHOLD);
steps * WHEEL_STEP
})
}
}; };
if scroll_vector == 0.0 { if scroll_vector == 0.0 {
return Message::Ignore; return Message::Ignore;
@ -499,7 +519,7 @@ impl cosmic::Application for Audio {
let new_volume = (self.model.sink_volume as f64 + (scroll_vector as f64)) let new_volume = (self.model.sink_volume as f64 + (scroll_vector as f64))
.clamp(0.0, self.max_sink_volume as f64); .clamp(0.0, self.max_sink_volume as f64);
Message::SetSinkVolume(new_volume as u32) Message::SetSinkVolume(new_volume.round() as u32)
}); });
let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| { let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| {