Build the Home accessory shelf
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
// A light dimmer that previews locally and sends one value when interaction
|
||||
// ends. Home Assistant never sees the intermediate pointer positions.
|
||||
|
||||
import QtQuick
|
||||
import qs.config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int value: 0
|
||||
property int previewValue: 0
|
||||
|
||||
readonly property bool pressed: drag.pressed
|
||||
|
||||
signal previewChanged(int value)
|
||||
signal committed(int value)
|
||||
|
||||
implicitWidth: 160
|
||||
implicitHeight: 32
|
||||
activeFocusOnTab: root.enabled
|
||||
opacity: root.enabled ? 1 : 0.42
|
||||
|
||||
onValueChanged: {
|
||||
if (!drag.pressed)
|
||||
root.updatePreview(root.value, false);
|
||||
}
|
||||
|
||||
Component.onCompleted: root.updatePreview(root.value, false)
|
||||
|
||||
Keys.onLeftPressed: root.commitStep(-5)
|
||||
Keys.onDownPressed: root.commitStep(-5)
|
||||
Keys.onRightPressed: root.commitStep(5)
|
||||
Keys.onUpPressed: root.commitStep(5)
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 10
|
||||
radius: 5
|
||||
color: Theme.alpha(Theme.fg, 0.105)
|
||||
border.width: root.activeFocus ? 1 : 0
|
||||
border.color: Theme.alpha(Theme.warn, 0.72)
|
||||
|
||||
Rectangle {
|
||||
id: fill
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: track.width * root.previewValue / 100
|
||||
radius: track.radius
|
||||
color: Theme.warn
|
||||
|
||||
Behavior on width {
|
||||
enabled: !drag.pressed
|
||||
NumberAnimation {
|
||||
duration: Theme.durFast
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: knob
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: Math.max(0, Math.min(track.width - width,
|
||||
track.width * root.previewValue / 100 - width / 2))
|
||||
width: 16
|
||||
height: 16
|
||||
radius: 8
|
||||
color: Theme.fg
|
||||
border.width: 1
|
||||
border.color: Theme.alpha(Theme.bgDark, 0.38)
|
||||
}
|
||||
}
|
||||
|
||||
// The 32 px interaction surface is intentionally much taller than the
|
||||
// 10 px track, while remaining inside this component's layout bounds.
|
||||
MouseArea {
|
||||
id: drag
|
||||
anchors.fill: parent
|
||||
enabled: root.enabled
|
||||
hoverEnabled: true
|
||||
preventStealing: true
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
|
||||
onPressed: event => {
|
||||
root.forceActiveFocus();
|
||||
root.previewAt(event.x);
|
||||
event.accepted = true;
|
||||
}
|
||||
onPositionChanged: event => {
|
||||
if (pressed)
|
||||
root.previewAt(event.x);
|
||||
}
|
||||
onReleased: event => {
|
||||
root.committed(root.previewValue);
|
||||
event.accepted = true;
|
||||
}
|
||||
onCanceled: root.updatePreview(root.value, false)
|
||||
onWheel: event => {
|
||||
const direction = event.angleDelta.y >= 0 ? 5 : -5;
|
||||
root.commitStep(direction);
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
function clamp(candidate: real): int {
|
||||
return Math.max(0, Math.min(100, Math.round(candidate)));
|
||||
}
|
||||
|
||||
function updatePreview(candidate: real, announce: bool): void {
|
||||
const nextValue = root.clamp(candidate);
|
||||
if (root.previewValue === nextValue)
|
||||
return;
|
||||
root.previewValue = nextValue;
|
||||
if (announce)
|
||||
root.previewChanged(nextValue);
|
||||
}
|
||||
|
||||
function previewAt(pointerX: real): void {
|
||||
root.updatePreview(pointerX / Math.max(1, drag.width) * 100, true);
|
||||
}
|
||||
|
||||
function commitStep(delta: int): void {
|
||||
if (!root.enabled)
|
||||
return;
|
||||
root.updatePreview(root.previewValue + delta, true);
|
||||
root.committed(root.previewValue);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user