Files
dotfiles/home/dot_config/quickshell/bar/DetailPopup.qml
T

387 lines
12 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Services.UPower
PopupWindow {
id: root
property var hardware
property var sink
property var source
property var battery
property var clock
property var anchorItem: null
property string page: ""
function toggle(nextPage, item) {
if (visible && page === nextPage) {
visible = false;
return;
}
page = nextPage;
anchorItem = item;
if (page === "network")
hardware.refreshWifi();
anchor.updateAnchor();
visible = true;
}
function duration(seconds) {
if (seconds <= 0)
return "Calculating";
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return hours + "h " + minutes + "m";
}
function batteryState() {
switch (battery.state) {
case UPowerDeviceState.Charging: return "Charging";
case UPowerDeviceState.Discharging: return "Discharging";
case UPowerDeviceState.FullyCharged: return "Fully charged";
case UPowerDeviceState.Empty: return "Empty";
case UPowerDeviceState.PendingCharge: return "Waiting to charge";
case UPowerDeviceState.PendingDischarge: return "Waiting to discharge";
default: return "Unknown";
}
}
visible: false
implicitWidth: Theme.popupWidth
implicitHeight: contentLoader.item
? contentLoader.item.implicitHeight + Theme.popupPadding * 2 : 100
color: "transparent"
anchor {
item: root.anchorItem
edges: Edges.Bottom | Edges.Right
gravity: Edges.Bottom | Edges.Left
margins.top: 4
}
Rectangle {
anchors.fill: parent
color: Theme.popupBackground
Loader {
id: contentLoader
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: Theme.popupPadding
}
sourceComponent: root.page === "clock" ? clockPage
: root.page === "audio" ? audioPage
: root.page === "network" ? networkPage
: batteryPage
}
Rectangle {
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: Theme.activeLineWidth
color: Theme.accent
}
}
Component {
id: clockPage
Item {
id: clockContent
property date now: root.clock.date
property int year: now.getFullYear()
property int month: now.getMonth()
property int firstDay: new Date(year, month, 1).getDay()
property int days: new Date(year, month + 1, 0).getDate()
implicitWidth: Theme.popupContentWidth
implicitHeight: 310
PopupHeader {
width: parent.width
text: Qt.formatDateTime(parent.now, "dddd, MMMM d")
}
Text {
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: 40
}
text: Qt.formatDateTime(parent.now, "h:mm:ss AP")
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontClock
}
Row {
anchors {
left: parent.left
top: parent.top
topMargin: 86
}
spacing: 4
Repeater {
model: ["S", "M", "T", "W", "T", "F", "S"]
Text {
required property string modelData
width: 37
text: modelData
color: Theme.muted
font.family: Theme.fontFamily
horizontalAlignment: Text.AlignHCenter
font.bold: true
}
}
}
Grid {
anchors {
left: parent.left
top: parent.top
topMargin: 112
}
columns: 7
spacing: 4
Repeater {
model: 42
Rectangle {
required property int index
property int day: index - clockContent.firstDay + 1
property bool today: day === clockContent.now.getDate()
width: 37
height: 29
radius: 0
color: today ? Theme.primary : "transparent"
Rectangle {
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: Theme.activeLineWidth
visible: parent.today
color: Theme.accent
}
Text {
anchors.centerIn: parent
text: parent.day > 0 && parent.day <= clockContent.days
? parent.day : ""
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSmall
}
}
}
}
}
}
Component {
id: audioPage
Column {
width: Theme.popupContentWidth
spacing: 12
PopupHeader {
width: parent.width
text: "Audio"
}
PopupSlider {
width: parent.width
label: "Output"
value: root.sink && root.sink.audio ? root.sink.audio.volume : 0
muted: root.sink && root.sink.audio ? root.sink.audio.muted : false
onValueRequested: value => {
if (root.sink && root.sink.audio)
root.sink.audio.volume = value;
}
onMuteRequested: {
if (root.sink && root.sink.audio)
root.sink.audio.muted = !root.sink.audio.muted;
}
}
PopupSlider {
width: parent.width
label: "Microphone"
value: root.source && root.source.audio ? root.source.audio.volume : 0
muted: root.source && root.source.audio ? root.source.audio.muted : false
onValueRequested: value => {
if (root.source && root.source.audio)
root.source.audio.volume = value;
}
onMuteRequested: {
if (root.source && root.source.audio)
root.source.audio.muted = !root.source.audio.muted;
}
}
}
}
Component {
id: networkPage
Column {
width: Theme.popupContentWidth
spacing: 10
PopupHeader {
width: parent.width
text: "Network"
}
Text {
text: "Connected to " + root.hardware.connection
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontBody
}
Column {
id: networks
width: parent.width
spacing: 5
Repeater {
model: root.hardware.wifiNetworks
Rectangle {
required property var modelData
width: networks.width
height: 29
radius: 0
color: modelData.active ? Theme.primary : Theme.background
Rectangle {
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: Theme.activeLineWidth
visible: parent.modelData.active
color: Theme.accent
}
Text {
anchors {
left: parent.left
leftMargin: 9
verticalCenter: parent.verticalCenter
}
width: parent.width - 70
elide: Text.ElideRight
text: modelData.ssid
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSmall
}
Text {
anchors {
right: parent.right
rightMargin: 9
verticalCenter: parent.verticalCenter
}
text: modelData.signal + "%"
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontCaption
}
}
}
}
Rectangle {
width: parent.width
height: 30
radius: 0
color: Theme.primary
Text {
anchors.centerIn: parent
text: "Open network settings"
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontCaption
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Quickshell.execDetached(["nm-connection-editor"])
}
}
}
}
Component {
id: batteryPage
Column {
width: Theme.popupContentWidth
spacing: 12
PopupHeader {
width: parent.width
text: "Battery"
}
Text {
text: Math.round(root.battery.percentage * 100) + "% "
+ root.batteryState()
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontDisplay
}
Rectangle {
width: parent.width
height: 8
radius: 0
color: Theme.track
Rectangle {
width: parent.width * root.battery.percentage
height: parent.height
radius: 0
color: root.battery.percentage <= 0.15 ? Theme.urgent : Theme.primary
}
}
Text {
text: UPower.onBattery
? "Remaining: " + root.duration(root.battery.timeToEmpty)
: "Until full: " + root.duration(root.battery.timeToFull)
color: Theme.accent
font.family: Theme.fontFamily
font.pixelSize: Theme.fontBody
}
Text {
text: "Rate: " + Math.abs(root.battery.changeRate).toFixed(1) + " W"
+ (root.battery.healthSupported
? " Health: " + Math.round(root.battery.healthPercentage * 100) + "%"
: "")
color: Theme.foreground
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSmall
}
}
}
}