Make Home light ordering explicit and durable
This commit is contained in:
@@ -9,93 +9,61 @@ Rectangle {
|
|||||||
required property string sourceName
|
required property string sourceName
|
||||||
required property int index
|
required property int index
|
||||||
required property bool featured
|
required property bool featured
|
||||||
|
required property bool canMoveEarlier
|
||||||
|
required property bool canMoveLater
|
||||||
|
|
||||||
signal aliasCommitted(string id, string alias)
|
signal aliasCommitted(string id, string alias)
|
||||||
signal removeRequested(string id)
|
signal removeRequested(string id)
|
||||||
signal moveRequested(string id, int targetIndex)
|
signal moveRequested(string id, int targetIndex)
|
||||||
|
|
||||||
readonly property bool dragging: dragHandler.active
|
|
||||||
|
|
||||||
implicitHeight: 108
|
implicitHeight: 108
|
||||||
radius: Theme.cardRadius
|
radius: Theme.cardRadius
|
||||||
color: root.dragging
|
color: Theme.alpha(Theme.bgDark, 0.7)
|
||||||
? Theme.mix(Theme.bgDark, Theme.accent, 0.09)
|
border.width: 1
|
||||||
: Theme.alpha(Theme.bgDark, 0.7)
|
border.color: Theme.alpha(Theme.fg, 0.07)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
PrismEdge {
|
PrismEdge {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
inset: root.radius
|
inset: root.radius
|
||||||
opacity: root.dragging ? 0.82 : 0.2
|
opacity: 0.2
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Column {
|
||||||
id: dragHandle
|
id: reorderControls
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 11
|
anchors.leftMargin: 8
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
width: 30
|
width: 30
|
||||||
height: 42
|
spacing: 4
|
||||||
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)
|
|
||||||
|
|
||||||
Text {
|
SettingsButton {
|
||||||
anchors.centerIn: parent
|
width: 30
|
||||||
text: "⠿"
|
height: 29
|
||||||
color: root.dragging ? Theme.accent : Theme.fgDim
|
text: "↑"
|
||||||
font.family: Theme.fontFamily
|
enabled: root.canMoveEarlier
|
||||||
font.pixelSize: 16
|
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 {
|
SettingsButton {
|
||||||
id: handleMouse
|
width: 30
|
||||||
anchors.fill: parent
|
height: 29
|
||||||
hoverEnabled: true
|
text: "↓"
|
||||||
acceptedButtons: Qt.NoButton
|
enabled: root.canMoveLater
|
||||||
cursorShape: Qt.SizeAllCursor
|
activeFocusOnTab: enabled
|
||||||
}
|
onClicked: root.moveRequested(root.favorite.id, root.index + 1)
|
||||||
|
Keys.onReturnPressed: if (enabled) root.moveRequested(root.favorite.id, root.index + 1)
|
||||||
DragHandler {
|
Keys.onSpacePressed: if (enabled) root.moveRequested(root.favorite.id, root.index + 1)
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: aliasFrame
|
id: aliasFrame
|
||||||
anchors.left: dragHandle.right
|
anchors.left: reorderControls.right
|
||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.right: removeButton.left
|
anchors.right: removeButton.left
|
||||||
anchors.rightMargin: 12
|
anchors.rightMargin: 12
|
||||||
@@ -188,16 +156,4 @@ Rectangle {
|
|||||||
Keys.onReturnPressed: root.removeRequested(root.favorite.id)
|
Keys.onReturnPressed: root.removeRequested(root.favorite.id)
|
||||||
Keys.onSpacePressed: 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,8 @@ SettingsPage {
|
|||||||
})
|
})
|
||||||
sourceName: modelData.sourceName
|
sourceName: modelData.sourceName
|
||||||
featured: index < 4
|
featured: index < 4
|
||||||
|
canMoveEarlier: index > 0
|
||||||
|
canMoveLater: index < favoritesGrid.count - 1
|
||||||
onAliasCommitted: (id, alias) => HomePreferences.setAlias(id, alias)
|
onAliasCommitted: (id, alias) => HomePreferences.setAlias(id, alias)
|
||||||
onMoveRequested: (id, targetIndex) => HomePreferences.move(id, targetIndex)
|
onMoveRequested: (id, targetIndex) => HomePreferences.move(id, targetIndex)
|
||||||
onRemoveRequested: id => HomePreferences.remove(id)
|
onRemoveRequested: id => HomePreferences.remove(id)
|
||||||
|
|||||||
@@ -111,7 +111,15 @@ fi
|
|||||||
assert_contains 'signal aliasCommitted(string id, string alias)' "$favorite_card"
|
assert_contains 'signal aliasCommitted(string id, string alias)' "$favorite_card"
|
||||||
assert_contains 'signal removeRequested(string id)' "$favorite_card"
|
assert_contains 'signal removeRequested(string id)' "$favorite_card"
|
||||||
assert_contains 'signal moveRequested(string id, int targetIndex)' "$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 'onEditingFinished:' "$favorite_card"
|
||||||
assert_contains 'text: "Control Center"' "$favorite_card"
|
assert_contains 'text: "Control Center"' "$favorite_card"
|
||||||
assert_contains 'activeFocusOnTab: true' "$favorite_card"
|
assert_contains 'activeFocusOnTab: true' "$favorite_card"
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ assert_reset_persists_without_debounce() {
|
|||||||
initial_ids=' ["light.kitchen","light.hall","light.desk"]'
|
initial_ids=' ["light.kitchen","light.hall","light.desk"]'
|
||||||
expected='{"initialized":true,"favorites":[{"id":"light.desk","alias":""},{"id":"light.kitchen","alias":"Island"}],"saveError":""}'
|
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"}]}'
|
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_expected='{"initialized":true,"favorites":[],"saveError":""}'
|
||||||
empty_file='{"initialized":true,"favorites":[]}'
|
empty_file='{"initialized":true,"favorites":[]}'
|
||||||
reset_expected='{"initialized":false,"favorites":[],"saveError":""}'
|
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 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 alias light.kitchen ' Island ' >/dev/null
|
||||||
qs_for_harness ipc call home-pref-test move light.desk 0 >/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
|
qs_for_harness ipc call home-pref-test remove light.hall >/dev/null
|
||||||
wait_for_status "$expected"
|
wait_for_status "$expected"
|
||||||
wait_for_file_content "$expected_file"
|
wait_for_file_content "$expected_file"
|
||||||
|
|||||||
Reference in New Issue
Block a user