From 2093d1c9c7a8e205ca904c18465d10fb05eed965 Mon Sep 17 00:00:00 2001 From: Saman Shirdel Date: Thu, 30 Jul 2026 02:06:18 +0330 Subject: [PATCH] Show OSD and sync touchpad steps when scrolling bar widgets (audio, monitor) (#6421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Show OSD and sync touchpad steps when scrolling bar widgets Scrolling the volume or brightness icon in the bar changed the value directly without ever calling omarchy-osd, so only the keyboard media keys showed the popup. On top of that, a touchpad's stream of many small wheel events per finger-drag wasn't matched to a mouse's single ±120 notch per click, so touchpad scrolling felt uncoordinated and uneven next to the keyboard/mouse behavior. - shell/plugins/panels/audio/Panel.qml: accumulate raw wheel delta and only apply a step once it crosses a full mouse-notch's worth, so touchpad and mouse move the volume in identical 5% increments, and ping the OSD each time a step actually lands. - shell/plugins/panels/monitor/Panel.qml: same accumulator for brightness, with a 5% floor so scrolling down can never blank the screen. - shell/plugins/osd/Osd.qml: animate the progress bar's width instead of snapping, so successive steps glide smoothly. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix bar wheel OSD behavior * Normalize scaled wheel events --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: David Heinemeier Hansson --- shell/Commons/Util.qml | 11 ++++++++++ shell/plugins/osd/Osd.qml | 7 +++++++ shell/plugins/panels/audio/Panel.qml | 29 ++++++++++++++++++++------ shell/plugins/panels/monitor/Panel.qml | 18 +++++++++++++++- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/shell/Commons/Util.qml b/shell/Commons/Util.qml index 70ba0c58..3d5a1f8e 100644 --- a/shell/Commons/Util.qml +++ b/shell/Commons/Util.qml @@ -17,6 +17,17 @@ QtObject { return clamp(value, 0, 1) } + function wheelSteps(accumulator, delta) { + // Some mouse/compositor combinations scale a single notch well beyond + // Qt's conventional 120 units. Keep one event to one step while still + // accumulating the smaller deltas emitted by touchpads. + delta = Math.max(-120, Math.min(120, delta)) + if (accumulator * delta < 0) accumulator = 0 + var total = accumulator + delta + var steps = total < 0 ? Math.ceil(total / 120) : Math.floor(total / 120) + return { steps: steps, remainder: total - steps * 120 } + } + // Compose a base color with an opacity. Accepts a color object or a hex // string; null/undefined yields transparent black at the requested alpha. function alpha(c, opacity) { diff --git a/shell/plugins/osd/Osd.qml b/shell/plugins/osd/Osd.qml index 39f32e2f..abf53e22 100644 --- a/shell/plugins/osd/Osd.qml +++ b/shell/plugins/osd/Osd.qml @@ -56,6 +56,8 @@ Item { function show(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration) { var next = OsdModel.stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration) + // Update before opening so a fresh OSD starts at its new value; only + // subsequent updates while it remains open animate the progress bar. iconKey = next.iconKey maxValue = next.maxValue hasProgress = next.hasProgress @@ -176,6 +178,11 @@ Item { height: parent.height width: parent.width * (root.hasProgress ? root.value / root.maxValue : 0) color: Color.accent + + Behavior on width { + enabled: root.opened + NumberAnimation { duration: 140; easing.type: Easing.OutCubic } + } } } Text { diff --git a/shell/plugins/panels/audio/Panel.qml b/shell/plugins/panels/audio/Panel.qml index 8dcdb86a..1fd01d58 100644 --- a/shell/plugins/panels/audio/Panel.qml +++ b/shell/plugins/panels/audio/Panel.qml @@ -121,6 +121,9 @@ Panel { // selected while a tuning still exists. property string volumeSinkName: "" + // Carry sub-notch touchpad deltas between wheel events. + property real wheelAccumulator: 0 + readonly property var volumeSink: { if (volumeSinkName === "" || !sink) return sink if (volumeSinkName === String(sink.name)) return sink @@ -395,13 +398,13 @@ Panel { if (selectedIndex < floor) selectedIndex = floor } - function outputIcon() { + function outputIcon(volume) { // Match the old Waybar pulseaudio glyph set. The Material Design speaker // icons render visually smaller in JetBrainsMono Nerd Font. if (!sink || !sink.audio) return "" if (isHeadphones(sink)) return "󰋋" if (outputMuted) return "" - var v = outputVolume + var v = volume === undefined ? outputVolume : volume if (v >= 0.67) return "" if (v >= 0.34) return "" if (v > 0) return "" @@ -421,8 +424,18 @@ Panel { } function setOutputVolume(v) { - if (!sink || !sink.audio) return - volumeSink.audio.volume = Math.max(0, Math.min(1, v)) + if (!volumeSink || !volumeSink.audio) return outputVolume + var volume = Math.max(0, Math.min(1, v)) + volumeSink.audio.volume = volume + return volume + } + + function showVolumeOsd(volume) { + if (!bar || !bar.shell) return + bar.shell.summon("omarchy.osd", JSON.stringify({ + icon: outputIcon(volume), + value: Math.round(volume * 100) + })) } function setInputVolume(v) { @@ -625,8 +638,12 @@ Panel { } onWheelMoved: function(delta) { - var step = 0.05 - root.setOutputVolume(root.outputVolume + (delta > 0 ? step : -step)) + if (!root.hasOutput) return + var wheel = Util.wheelSteps(root.wheelAccumulator, delta) + root.wheelAccumulator = wheel.remainder + if (wheel.steps === 0) return + var volume = root.setOutputVolume(root.outputVolume + wheel.steps * 0.05) + root.showVolumeOsd(volume) } } diff --git a/shell/plugins/panels/monitor/Panel.qml b/shell/plugins/panels/monitor/Panel.qml index d822629c..bf4ab21d 100644 --- a/shell/plugins/panels/monitor/Panel.qml +++ b/shell/plugins/panels/monitor/Panel.qml @@ -27,6 +27,9 @@ Panel { property var displays: [] property int enabledDisplayCount: 0 + // Carry sub-notch touchpad deltas between wheel events. + property real wheelAccumulator: 0 + // Cursor model shared by keyboard and mouse. Sections: // "brightness" - single slider row, selectedIndex = -1 sentinel // (mirrors Audio's slider rows). Only present if a @@ -250,6 +253,14 @@ Panel { brightnessDebounce.restart() } + function showBrightnessOsd(percent) { + if (!bar || !bar.shell) return + bar.shell.summon("omarchy.osd", JSON.stringify({ + icon: "brightness", + value: percent + })) + } + function normalizeScale(scale) { return Model.normalizeScale(scale) } @@ -461,7 +472,12 @@ Panel { text: Quickshell.screens.length > 1 ? "󰍺" : "󰍹" onPressed: function(b) { root.toggle() } onWheelMoved: function(delta) { - if (root.brightnessAvailable) root.setBrightness(root.brightnessPercent + (delta > 0 ? 5 : -5)) + if (!root.brightnessAvailable) return + var wheel = Util.wheelSteps(root.wheelAccumulator, delta) + root.wheelAccumulator = wheel.remainder + if (wheel.steps === 0) return + root.setBrightness(root.brightnessPercent + wheel.steps * 5) + root.showBrightnessOsd(root.brightnessPercent) } }