From 39a26adcc2f88ab1c06da441f46f3437c1c6e74a Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Tue, 18 Aug 2026 05:56:37 -0400 Subject: [PATCH] Make Home light ordering explicit and durable --- .../modules/settings/HomeFavoriteCard.qml | 102 +++++------------- .../modules/settings/HomePhonePage.qml | 2 + .../home-phone-settings-contract.sh | 10 +- tests/quickshell/home-preferences-contract.sh | 8 ++ 4 files changed, 48 insertions(+), 74 deletions(-) diff --git a/config/dot/quickshell/modules/settings/HomeFavoriteCard.qml b/config/dot/quickshell/modules/settings/HomeFavoriteCard.qml index 0c6e245..03cc967 100644 --- a/config/dot/quickshell/modules/settings/HomeFavoriteCard.qml +++ b/config/dot/quickshell/modules/settings/HomeFavoriteCard.qml @@ -9,93 +9,61 @@ Rectangle { required property string sourceName required property int index required property bool featured + required property bool canMoveEarlier + required property bool canMoveLater signal aliasCommitted(string id, string alias) signal removeRequested(string id) signal moveRequested(string id, int targetIndex) - readonly property bool dragging: dragHandler.active - implicitHeight: 108 radius: Theme.cardRadius - color: root.dragging - ? Theme.mix(Theme.bgDark, Theme.accent, 0.09) - : Theme.alpha(Theme.bgDark, 0.7) - border.width: root.dragging ? 2 : 1 - border.color: root.dragging - ? Theme.alpha(Theme.accent, 0.82) - : Theme.alpha(Theme.fg, 0.07) - z: root.dragging ? 10 : 0 - - transform: Translate { - x: root.dragging ? dragHandler.translation.x : 0 - y: root.dragging ? dragHandler.translation.y : 0 - } + color: Theme.alpha(Theme.bgDark, 0.7) + border.width: 1 + border.color: Theme.alpha(Theme.fg, 0.07) PrismEdge { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right inset: root.radius - opacity: root.dragging ? 0.82 : 0.2 + opacity: 0.2 } - Rectangle { - id: dragHandle + Column { + id: reorderControls anchors.left: parent.left - anchors.leftMargin: 11 + anchors.leftMargin: 8 anchors.verticalCenter: parent.verticalCenter width: 30 - height: 42 - radius: 9 - activeFocusOnTab: true - color: root.dragging || activeFocus - ? Theme.alpha(Theme.accent, 0.14) - : (handleMouse.containsMouse ? Theme.alpha(Theme.fg, 0.09) : Theme.alpha(Theme.fg, 0.045)) - border.width: activeFocus ? 2 : 1 - border.color: activeFocus ? Theme.accent : Theme.alpha(Theme.fg, 0.06) + spacing: 4 - Text { - anchors.centerIn: parent - text: "⠿" - color: root.dragging ? Theme.accent : Theme.fgDim - font.family: Theme.fontFamily - font.pixelSize: 16 + SettingsButton { + width: 30 + height: 29 + text: "↑" + enabled: root.canMoveEarlier + activeFocusOnTab: enabled + onClicked: root.moveRequested(root.favorite.id, root.index - 1) + Keys.onReturnPressed: if (enabled) root.moveRequested(root.favorite.id, root.index - 1) + Keys.onSpacePressed: if (enabled) root.moveRequested(root.favorite.id, root.index - 1) } - MouseArea { - id: handleMouse - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton - cursorShape: Qt.SizeAllCursor - } - - DragHandler { - id: dragHandler - target: null - onActiveChanged: { - if (!active) - root.commitDrag(); - } - } - - Keys.onPressed: event => { - if (event.key === Qt.Key_Left || event.key === Qt.Key_Up) { - root.moveRequested(root.favorite.id, Math.max(0, root.index - 1)); - event.accepted = true; - } else if (event.key === Qt.Key_Right || event.key === Qt.Key_Down) { - const grid = root.GridView.view; - const lastIndex = grid ? grid.count - 1 : root.index; - root.moveRequested(root.favorite.id, Math.min(lastIndex, root.index + 1)); - event.accepted = true; - } + SettingsButton { + width: 30 + height: 29 + text: "↓" + enabled: root.canMoveLater + activeFocusOnTab: enabled + onClicked: root.moveRequested(root.favorite.id, root.index + 1) + Keys.onReturnPressed: if (enabled) root.moveRequested(root.favorite.id, root.index + 1) + Keys.onSpacePressed: if (enabled) root.moveRequested(root.favorite.id, root.index + 1) } } Rectangle { id: aliasFrame - anchors.left: dragHandle.right + anchors.left: reorderControls.right anchors.leftMargin: 10 anchors.right: removeButton.left anchors.rightMargin: 12 @@ -188,16 +156,4 @@ Rectangle { Keys.onReturnPressed: root.removeRequested(root.favorite.id) Keys.onSpacePressed: root.removeRequested(root.favorite.id) } - - function commitDrag(): void { - const grid = root.GridView.view; - if (!grid || grid.count <= 0) - return; - const centerX = root.x + dragHandler.translation.x + root.width / 2; - const centerY = root.y + dragHandler.translation.y + root.height / 2; - const modelCount = grid.count; - const column = Math.max(0, Math.min(1, Math.floor(centerX / grid.cellWidth))); - const row = Math.max(0, Math.floor(centerY / grid.cellHeight)); - root.moveRequested(root.favorite.id, Math.min(modelCount - 1, row * 2 + column)); - } } diff --git a/config/dot/quickshell/modules/settings/HomePhonePage.qml b/config/dot/quickshell/modules/settings/HomePhonePage.qml index eae1bc3..65c67f0 100644 --- a/config/dot/quickshell/modules/settings/HomePhonePage.qml +++ b/config/dot/quickshell/modules/settings/HomePhonePage.qml @@ -122,6 +122,8 @@ SettingsPage { }) sourceName: modelData.sourceName featured: index < 4 + canMoveEarlier: index > 0 + canMoveLater: index < favoritesGrid.count - 1 onAliasCommitted: (id, alias) => HomePreferences.setAlias(id, alias) onMoveRequested: (id, targetIndex) => HomePreferences.move(id, targetIndex) onRemoveRequested: id => HomePreferences.remove(id) diff --git a/tests/quickshell/home-phone-settings-contract.sh b/tests/quickshell/home-phone-settings-contract.sh index c9bd64d..6c45675 100755 --- a/tests/quickshell/home-phone-settings-contract.sh +++ b/tests/quickshell/home-phone-settings-contract.sh @@ -111,7 +111,15 @@ fi assert_contains 'signal aliasCommitted(string id, string alias)' "$favorite_card" assert_contains 'signal removeRequested(string id)' "$favorite_card" assert_contains 'signal moveRequested(string id, int targetIndex)' "$favorite_card" -assert_contains 'DragHandler {' "$favorite_card" +assert_contains 'text: "↑"' "$favorite_card" +assert_contains 'text: "↓"' "$favorite_card" +assert_contains 'enabled: root.canMoveEarlier' "$favorite_card" +assert_contains 'enabled: root.canMoveLater' "$favorite_card" +if rg -Fq 'DragHandler {' "$favorite_card"; then + fail 'Home light cards still expose the broken drag affordance' +fi +assert_contains 'canMoveEarlier: index > 0' "$home_page" +assert_contains 'canMoveLater: index < favoritesGrid.count - 1' "$home_page" assert_contains 'onEditingFinished:' "$favorite_card" assert_contains 'text: "Control Center"' "$favorite_card" assert_contains 'activeFocusOnTab: true' "$favorite_card" diff --git a/tests/quickshell/home-preferences-contract.sh b/tests/quickshell/home-preferences-contract.sh index 1255dcf..8cb9202 100755 --- a/tests/quickshell/home-preferences-contract.sh +++ b/tests/quickshell/home-preferences-contract.sh @@ -96,6 +96,8 @@ assert_reset_persists_without_debounce() { initial_ids=' ["light.kitchen","light.hall","light.desk"]' expected='{"initialized":true,"favorites":[{"id":"light.desk","alias":""},{"id":"light.kitchen","alias":"Island"}],"saveError":""}' expected_file='{"initialized":true,"favorites":[{"id":"light.desk","alias":""},{"id":"light.kitchen","alias":"Island"}]}' +reordered_expected='{"initialized":true,"favorites":[{"id":"light.desk","alias":""},{"id":"light.kitchen","alias":"Island"},{"id":"light.hall","alias":""}],"saveError":""}' +reordered_file='{"initialized":true,"favorites":[{"id":"light.desk","alias":""},{"id":"light.kitchen","alias":"Island"},{"id":"light.hall","alias":""}]}' empty_expected='{"initialized":true,"favorites":[],"saveError":""}' empty_file='{"initialized":true,"favorites":[]}' reset_expected='{"initialized":false,"favorites":[],"saveError":""}' @@ -106,6 +108,12 @@ start_harness qs_for_harness ipc call home-pref-test initialize "$initial_ids" >/dev/null qs_for_harness ipc call home-pref-test alias light.kitchen ' Island ' >/dev/null qs_for_harness ipc call home-pref-test move light.desk 0 >/dev/null +wait_for_status "$reordered_expected" +wait_for_file_content "$reordered_file" +stop_harness +start_harness +wait_for_status "$reordered_expected" + qs_for_harness ipc call home-pref-test remove light.hall >/dev/null wait_for_status "$expected" wait_for_file_content "$expected_file"