170 lines
5.1 KiB
QML
170 lines
5.1 KiB
QML
import QtQuick
|
||
import qs.config
|
||
|
||
Rectangle {
|
||
id: root
|
||
|
||
required property var entity
|
||
property bool busy: false
|
||
property int pendingBrightness: -1
|
||
property string actionError: ""
|
||
|
||
signal powerRequested
|
||
signal brightnessRequested(int value)
|
||
|
||
readonly property int confirmedBrightness: root.clamp(root.entity.brightnessPct ?? 0)
|
||
readonly property int displayedBrightness: brightnessSlider.previewValue
|
||
readonly property bool powerEnabled: root.entity.available && !root.busy
|
||
readonly property bool dimmerEnabled: root.entity.available
|
||
&& root.entity.dimmable
|
||
&& !root.busy
|
||
|
||
implicitHeight: 124
|
||
radius: 14
|
||
color: {
|
||
if (root.entity.active)
|
||
return Theme.alpha(Theme.warn, powerArea.containsMouse ? 0.145 : 0.095);
|
||
return Theme.alpha(Theme.fg, powerArea.containsMouse ? 0.078 : 0.045);
|
||
}
|
||
border.width: 1
|
||
border.color: {
|
||
if (root.activeFocus)
|
||
return Theme.alpha(Theme.accent, 0.72);
|
||
if (root.entity.active)
|
||
return Theme.alpha(Theme.warn, 0.17);
|
||
return Theme.alpha(Theme.fg, 0.065);
|
||
}
|
||
activeFocusOnTab: root.powerEnabled
|
||
|
||
Behavior on color { ColorAnimation { duration: Theme.durFast } }
|
||
Behavior on border.color { ColorAnimation { duration: Theme.durFast } }
|
||
|
||
Keys.onSpacePressed: {
|
||
if (root.powerEnabled)
|
||
root.powerRequested();
|
||
}
|
||
Keys.onReturnPressed: {
|
||
if (root.powerEnabled)
|
||
root.powerRequested();
|
||
}
|
||
|
||
Rectangle {
|
||
id: bulb
|
||
anchors.left: parent.left
|
||
anchors.leftMargin: 13
|
||
anchors.top: parent.top
|
||
anchors.topMargin: 13
|
||
width: 30
|
||
height: 30
|
||
radius: 10
|
||
color: root.entity.active
|
||
? Theme.alpha(Theme.warn, 0.16)
|
||
: Theme.alpha(Theme.fg, 0.065)
|
||
|
||
Text {
|
||
anchors.centerIn: parent
|
||
text: root.busy ? "\u{F0772}" : "\u{F0335}"
|
||
color: root.entity.active ? Theme.warn : Theme.fgDim
|
||
font.family: Theme.fontMono
|
||
font.pixelSize: 15
|
||
}
|
||
}
|
||
|
||
Text {
|
||
anchors.right: parent.right
|
||
anchors.rightMargin: 13
|
||
anchors.verticalCenter: bulb.verticalCenter
|
||
text: root.entity.dimmable ? root.displayedBrightness + "%" : "—"
|
||
color: root.entity.active || root.pendingBrightness >= 0
|
||
? Theme.warn
|
||
: Theme.fgDim
|
||
font.family: Theme.fontFamily
|
||
font.pixelSize: Theme.fontSizeSmall
|
||
font.weight: Font.DemiBold
|
||
font.features: Theme.tabularFigures
|
||
}
|
||
|
||
Text {
|
||
id: aliasLabel
|
||
anchors.left: parent.left
|
||
anchors.leftMargin: 13
|
||
anchors.right: parent.right
|
||
anchors.rightMargin: 13
|
||
anchors.top: bulb.bottom
|
||
anchors.topMargin: 8
|
||
text: root.entity.name
|
||
color: Theme.fg
|
||
elide: Text.ElideRight
|
||
font.family: Theme.fontFamily
|
||
font.pixelSize: Theme.fontSize
|
||
font.weight: Font.DemiBold
|
||
}
|
||
|
||
Text {
|
||
anchors.left: aliasLabel.left
|
||
anchors.right: aliasLabel.right
|
||
anchors.top: aliasLabel.bottom
|
||
anchors.topMargin: 2
|
||
text: root.secondaryText()
|
||
color: root.actionError !== ""
|
||
? Theme.warn
|
||
: (root.entity.active ? Theme.warn : Theme.fgMuted)
|
||
elide: Text.ElideRight
|
||
font.family: Theme.fontFamily
|
||
font.pixelSize: Theme.fontSizeSmall
|
||
}
|
||
|
||
MouseArea {
|
||
id: powerArea
|
||
anchors.left: parent.left
|
||
anchors.right: parent.right
|
||
anchors.top: parent.top
|
||
anchors.bottom: brightnessSlider.top
|
||
anchors.bottomMargin: 1
|
||
enabled: root.powerEnabled
|
||
hoverEnabled: true
|
||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||
onPressed: root.forceActiveFocus()
|
||
onClicked: root.powerRequested()
|
||
}
|
||
|
||
HomeBrightnessSlider {
|
||
id: brightnessSlider
|
||
anchors.left: parent.left
|
||
anchors.leftMargin: 13
|
||
anchors.right: parent.right
|
||
anchors.rightMargin: 13
|
||
anchors.bottom: parent.bottom
|
||
anchors.bottomMargin: 5
|
||
value: root.pendingBrightness >= 0
|
||
? root.pendingBrightness
|
||
: root.confirmedBrightness
|
||
enabled: root.dimmerEnabled
|
||
onCommitted: value => root.brightnessRequested(value)
|
||
}
|
||
|
||
function clamp(candidate: real): int {
|
||
return Math.max(0, Math.min(100, Math.round(candidate)));
|
||
}
|
||
|
||
function secondaryText(): string {
|
||
if (root.actionError !== "")
|
||
return root.errorText(root.actionError);
|
||
if (!root.entity.available)
|
||
return "Unavailable";
|
||
if (root.busy && root.pendingBrightness >= 0)
|
||
return "Setting " + root.pendingBrightness + "%";
|
||
if (root.busy)
|
||
return "Updating";
|
||
return root.entity.active ? "On" : "Off";
|
||
}
|
||
|
||
function errorText(code: string): string {
|
||
if (code === "authentication-required")
|
||
return "Authentication required";
|
||
if (code === "entity-not-discovered")
|
||
return "Light is unavailable";
|
||
return "Couldn’t update light";
|
||
}
|
||
}
|