wheel: use WheelInput horizontal helper, make default action cycle

window on WS pill
This commit is contained in:
bbedward
2026-10-09 12:45:27 -04:00
parent a0ea04178f
commit 534913dc0b
16 changed files with 113 additions and 91 deletions
Generated
+3 -3
View File
@@ -3,11 +3,11 @@
"dank-qml-common": {
"flake": false,
"locked": {
"lastModified": 1791492221,
"narHash": "sha256-RzuPqnlzsoAIXpyPDaAV9yPO4Tj39PfZ4JRftDpa4UQ=",
"lastModified": 1791563938,
"narHash": "sha256-Pg9M+qGfLKqKih7thzP4QLuKsi9jauSIEiQy8T6+EoY=",
"owner": "AvengeMedia",
"repo": "dank-qml-common",
"rev": "df82b1265c085f615d7403e6b3320e234221fd3b",
"rev": "212ac8cc7d83e1b5d10ae63535abfab0b1903624",
"type": "github"
},
"original": {
+1 -1
View File
@@ -23,7 +23,7 @@ Singleton {
id: root
readonly property var log: Log.scoped("SettingsData")
readonly property int settingsConfigVersion: 39
readonly property int settingsConfigVersion: 40
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
+1 -1
View File
@@ -1553,7 +1553,7 @@ var LOCAL_SPEC = {
maximizeDetection: true,
useOverlayLayer: false,
scrollEnabled: true,
scrollXBehavior: "column",
scrollXBehavior: "focusWindow",
scrollYBehavior: "workspace",
middleClickAction: "none",
rightClickAction: "none",
+19 -1
View File
@@ -206,7 +206,8 @@ function toJson(root, setKeys) {
value = withoutInstancePositions(value);
if (k === "builtInPluginSettings")
value = withoutSessionBackedPluginState(value);
if (!setKeys.has(k) && Util.isDefault(value, SPEC[k].def)) continue;
if (!setKeys.has(k) && Util.isDefault(value, SPEC[k].def))
continue;
out[k] = value;
}
out.configVersion = root.settingsConfigVersion;
@@ -722,9 +723,26 @@ function migrateToVersion(obj, targetVersion) {
settings.configVersion = 39;
}
if (currentVersion < 40 && targetVersion >= 40) {
migrateScrollColumnBehavior(settings);
settings.configVersion = 40;
}
return settings;
}
// v40: the niri-only "column" scroll behavior became the compositor-neutral "focusWindow"
function migrateScrollColumnBehavior(settings) {
for (const bar of Array.isArray(settings.barConfigs) ? settings.barConfigs : []) {
if (!bar || typeof bar !== "object")
continue;
for (const key of ["scrollXBehavior", "scrollYBehavior"]) {
if (bar[key] === "column")
bar[key] = "focusWindow";
}
}
}
// v39: the per-island toggle folded into the global reduceMotion; any bar that had it on turns the global one on
function migrateIslandReducedMotion(settings) {
for (const bar of Array.isArray(settings.barConfigs) ? settings.barConfigs : []) {
+23 -15
View File
@@ -9,7 +9,7 @@ MouseArea {
id: root
property bool scrollEnabled: true
property string xBehavior: "column"
property string xBehavior: "focusWindow"
property string yBehavior: "workspace"
property string screenName: ""
property var barConfig: null
@@ -18,6 +18,8 @@ MouseArea {
property real mouseAccumulatorX: 0
property real mouseAccumulatorY: 0
property bool actionInProgress: false
property bool gestureActive: false
property bool gestureHorizontal: false
signal workspaceSwitchRequested(int direction)
@@ -34,14 +36,8 @@ MouseArea {
case "workspace":
workspaceSwitchRequested(direction);
return true;
case "column":
if (!CompositorService.isNiri)
return false;
if (direction > 0)
NiriService.moveColumnRight(screenName);
else
NiriService.moveColumnLeft(screenName);
return true;
case "focusWindow":
return CompositorService.stepWindowFocus(screenName, direction);
default:
return false;
}
@@ -88,22 +84,34 @@ MouseArea {
mouseAccumulatorY = 0;
}
function horizontalGesture(wheel, isTouchpad) {
if (!isTouchpad)
return WheelInput.isHorizontal(wheel);
if (!gestureActive || wheel.phase === Qt.ScrollBegin)
gestureHorizontal = WheelInput.isHorizontal(wheel);
gestureActive = true;
return gestureHorizontal;
}
function processWheel(wheel) {
wheel.accepted = false;
if (wheel.phase === Qt.ScrollEnd)
gestureActive = false;
if (!scrollEnabled || actionInProgress)
return;
const deltaY = wheel.angleDelta.y;
const deltaX = wheel.angleDelta.x;
const delta = WheelInput.dominantDelta(wheel.angleDelta);
if (delta === 0)
return;
const isTouchpad = WheelInput.isTouchpad(wheel);
if (CompositorService.isNiri && xBehavior !== "none" && Math.abs(deltaX) > Math.abs(deltaY)) {
accumulateX(isTouchpad, deltaX, xBehavior);
if (horizontalGesture(wheel, isTouchpad)) {
if (xBehavior !== "none")
accumulateX(isTouchpad, delta, xBehavior);
return;
}
if (yBehavior === "none")
return;
accumulateY(isTouchpad, deltaY, yBehavior);
accumulateY(isTouchpad, delta, yBehavior);
}
onWheel: wheel => processWheel(wheel)
+1 -1
View File
@@ -1162,7 +1162,7 @@ Item {
propagateComposedEvents: true
z: -1
scrollEnabled: barWindow.barConfig?.scrollEnabled ?? true
xBehavior: barWindow.barConfig?.scrollXBehavior ?? "column"
xBehavior: barWindow.barConfig?.scrollXBehavior ?? "focusWindow"
yBehavior: barWindow.barConfig?.scrollYBehavior ?? "workspace"
screenName: barWindow.screenName
barConfig: barWindow.barConfig
@@ -294,13 +294,12 @@ DCard {
acceptedButtons: Qt.NoButton
hoverEnabled: true
onWheel: wheel => {
const horizontal = wheel.angleDelta.x !== 0 || wheel.pixelDelta.x !== 0 || (wheel.modifiers & Qt.ShiftModifier);
if (!horizontal || !root.scrollable) {
if (!WheelInput.isHorizontal(wheel) || !root.scrollable) {
wheel.accepted = false;
return;
}
const pixels = WheelInput.isTouchpad(wheel) ? wheel.pixelDelta.x || wheel.pixelDelta.y : 0;
const steps = (wheel.angleDelta.x || wheel.angleDelta.y) / 120;
const pixels = WheelInput.isTouchpad(wheel) ? WheelInput.dominantDelta(wheel.pixelDelta) : 0;
const steps = WheelInput.dominantDelta(wheel.angleDelta) / 120;
root.scrollTo(timeline.contentX - (pixels || steps * timeline.mouseWheelSpeed));
wheel.accepted = true;
}
@@ -9,6 +9,7 @@ import qs.Modules.Plugins
import qs.Services
import qs.DCommon.Widgets
import qs.Widgets
import "../../../DCommon/Common/WheelInput.js" as WheelInput
BasePill {
id: root
@@ -230,7 +231,7 @@ BasePill {
}
onWheel: wheel => {
if (Math.abs(wheel.angleDelta.x) > Math.abs(wheel.angleDelta.y))
if (WheelInput.isHorizontal(wheel))
return;
wheel.accepted = true;
workspaces.handleWheel(wheel);
@@ -5,6 +5,7 @@ import qs.Common
import qs.Services
import qs.DCommon.Widgets
import qs.Modules.DDash
import "../../../DCommon/Common/WheelInput.js" as WheelInput
Item {
id: root
@@ -60,12 +61,11 @@ Item {
anchors.fill: parent
acceptedButtons: Qt.NoButton
onWheel: wheel => {
const horizontal = wheel.angleDelta.x !== 0 || (wheel.modifiers & Qt.ShiftModifier);
if (!horizontal || root.maxStart === 0) {
if (!WheelInput.isHorizontal(wheel) || root.maxStart === 0) {
wheel.accepted = false;
return;
}
const delta = wheel.angleDelta.x || wheel.angleDelta.y;
const delta = WheelInput.dominantDelta(wheel.angleDelta);
const step = delta > 0 ? -1 : 1;
const next = root.start + step;
if (delta !== 0 && next >= 0 && next <= root.maxStart)
+11 -54
View File
@@ -458,11 +458,12 @@ Item {
}
SettingsToggleCard {
id: scrollCard
iconName: "mouse"
settingKey: "barScrollWheel"
resetStore: bar
resetKeys: ["scrollEnabled"]
tags: ["scroll", "wheel", "workspace", "column", "axis"]
tags: ["scroll", "wheel", "workspace", "focus", "window", "axis"]
title: I18n.tr("Scroll wheel")
visible: (bar.selectedBarConfig?.enabled ?? false) && !bar.selectedBarIsIsland
checked: bar.selectedBarConfig?.scrollEnabled ?? true
@@ -470,43 +471,23 @@ Item {
scrollEnabled: checked
})
readonly property var behaviors: CompositorService.canStepWindowFocus ? ["none", "workspace", "focusWindow"] : ["none", "workspace"]
readonly property var behaviorLabels: CompositorService.canStepWindowFocus ? [I18n.tr("None"), I18n.tr("Workspace"), I18n.tr("Focus window")] : [I18n.tr("None"), I18n.tr("Workspace")]
SettingsButtonGroupRow {
text: I18n.tr("Y axis")
resetStore: bar
resetKeys: ["scrollYBehavior"]
model: CompositorService.isNiri ? [I18n.tr("None"), I18n.tr("Workspace"), I18n.tr("Column", "noun, bar scroll behavior option, niri window column")] : [I18n.tr("None"), I18n.tr("Workspace")]
model: scrollCard.behaviorLabels
buttonPadding: Theme.spacingS
minButtonWidth: 44
textSize: Theme.fontSizeSmall
currentIndex: {
switch (bar.selectedBarConfig?.scrollYBehavior || "workspace") {
case "none":
return 0;
case "workspace":
return 1;
case "column":
return 2;
default:
return 1;
}
}
currentIndex: Math.max(0, scrollCard.behaviors.indexOf(bar.selectedBarConfig?.scrollYBehavior || "workspace"))
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let behavior = "workspace";
switch (index) {
case 0:
behavior = "none";
break;
case 1:
behavior = "workspace";
break;
case 2:
behavior = "column";
break;
}
SettingsData.updateBarConfig(bar.selectedBarId, {
scrollYBehavior: behavior
scrollYBehavior: scrollCard.behaviors[index]
});
}
}
@@ -515,40 +496,16 @@ Item {
text: I18n.tr("X axis")
resetStore: bar
resetKeys: ["scrollXBehavior"]
visible: CompositorService.isNiri
model: [I18n.tr("None"), I18n.tr("Workspace"), I18n.tr("Column")]
model: scrollCard.behaviorLabels
buttonPadding: Theme.spacingS
minButtonWidth: 44
textSize: Theme.fontSizeSmall
currentIndex: {
switch (bar.selectedBarConfig?.scrollXBehavior || "column") {
case "none":
return 0;
case "workspace":
return 1;
case "column":
return 2;
default:
return 2;
}
}
currentIndex: Math.max(0, scrollCard.behaviors.indexOf(bar.selectedBarConfig?.scrollXBehavior || "focusWindow"))
onSelectionChanged: (index, selected) => {
if (!selected)
return;
let behavior = "column";
switch (index) {
case 0:
behavior = "none";
break;
case 1:
behavior = "workspace";
break;
case 2:
behavior = "column";
break;
}
SettingsData.updateBarConfig(bar.selectedBarId, {
scrollXBehavior: behavior
scrollXBehavior: scrollCard.behaviors[index]
});
}
}
@@ -208,13 +208,13 @@ Scope {
}
if (event.key === Qt.Key_Left) {
NiriService.moveColumnLeft();
NiriService.focusColumnLeft();
event.accepted = true;
return;
}
if (event.key === Qt.Key_Right) {
NiriService.moveColumnRight();
NiriService.focusColumnRight();
event.accepted = true;
return;
}
+29
View File
@@ -2126,6 +2126,35 @@ Singleton {
}
}
readonly property bool canStepWindowFocus: compositor === "niri" || compositor === "hyprland" || compositor === "mango"
function stepWindowFocus(screenName, direction) {
switch (compositor) {
case "niri":
if (direction > 0)
NiriService.focusColumnRight(screenName);
else
NiriService.focusColumnLeft(screenName);
return true;
case "hyprland":
if (screenName && screenName !== Hyprland.focusedMonitor?.name)
HyprlandService.focusMonitor(screenName);
HyprlandService.cycleWindow(direction > 0);
return true;
case "mango":
{
const command = direction > 0 ? "focusstack,next" : "focusstack,prev";
if (screenName)
MangoService.dispatchOnOutput(screenName, command);
else
MangoService.dispatch(command);
return true;
}
default:
return false;
}
}
function focusWindow(windowId) {
switch (compositor) {
case "aqueous":
+10 -1
View File
@@ -10,7 +10,7 @@ import "../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
import qs.Services
import "../Common/OutputModel.js" as OutputModel
import "../Common/BlurStrength.js" as BlurStrength
import "../Common/WorkspaceModel.js" as WorkspaceModel
import "../Common/WorkspaceModel.js" as WorkspaceModel // qmllint disable unused-imports
Singleton {
id: root
@@ -549,6 +549,7 @@ hl.config({
${sections.join("\n")}
})
`;
if (layoutXrayEnabled) {
content += `
hl.layer_rule({
@@ -969,6 +970,14 @@ hl.layer_rule({
Hyprland.dispatch(`hl.dsp.window.move({ workspace = ${luaValue(workspace)}, window = ${luaString(selector)}, follow = ${follow ? "true" : "false"} })`);
}
function cycleWindow(forward) {
if (!luaConfigActive) {
Hyprland.dispatch(forward ? "cyclenext" : "cyclenext prev");
return;
}
Hyprland.dispatch(`hl.dsp.window.cycle_next({ next = ${forward ? "true" : "false"} })`);
}
function focusMonitor(monitor) {
if (!luaConfigActive) {
Hyprland.dispatch(`focusmonitor ${monitor}`);
+2 -2
View File
@@ -836,7 +836,7 @@ Singleton {
});
}
function moveColumnLeft(outputName) {
function focusColumnLeft(outputName) {
if (outputName && outputName !== currentOutput)
focusMonitor(outputName);
return send({
@@ -846,7 +846,7 @@ Singleton {
});
}
function moveColumnRight(outputName) {
function focusColumnRight(outputName) {
if (outputName && outputName !== currentOutput)
focusMonitor(outputName);
return send({
@@ -4161,7 +4161,7 @@
"keywords": [
"axis",
"bar",
"column",
"focus",
"general",
"panel",
"scroll",
@@ -4169,6 +4169,7 @@
"taskbar",
"topbar",
"wheel",
"window",
"workspace"
],
"parentLabel": "Bar",