added beta of hover texts

This commit is contained in:
Hannes
2026-06-15 23:19:07 +02:00
parent 256c8360f5
commit c4a018e39c
7 changed files with 541 additions and 37 deletions
+1
View File
@@ -14,6 +14,7 @@ QtObject {
function font_size(spaceHeight) { return Math.floor(spaceHeight * 0.75) }
function font_size_small(spaceHeight) { return Math.floor(spaceHeight * 0.625) }
function font_size_tiny(spaceHeight) { return Math.floor(spaceHeight * 0.4) }
readonly property int small_radius: 8
readonly property int radius: 16
+54
View File
@@ -0,0 +1,54 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
QtObject {
id: root
property string shortDate: Qt.formatDate(new Date(), "ddd, dd.MM.yyyy - ")
property string fullDate: Qt.formatDate(new Date(), "dddd, MMMM d, yyyy")
property string currentTime: Qt.formatTime(new Date(), "hh:mm:ss")
property string uptime: "Uptime: ..."
property bool expanded: false
property Timer timeTimer: Timer {
interval: 1000
running: true
repeat: true
triggeredOnStart: true
onTriggered: root.currentTime = Qt.formatTime(new Date(), "hh:mm:ss")
}
property Timer dateTimer: Timer {
interval: 60000
running: true
repeat: true
triggeredOnStart: true
onTriggered: {
root.shortDate = Qt.formatDate(new Date(), "ddd, dd.MM.yyyy - ")
root.fullDate = Qt.formatDate(new Date(), "dddd, MMMM d, yyyy")
}
}
property Process sysInfoProcess: Process {
command: ["sh", "-c", "echo $(cat /proc/uptime)"]
stdout: StdioCollector {
onStreamFinished: {
var uptimeSecs = parseFloat(text.trim().split(" ")[0])
var days = Math.floor(uptimeSecs / 86400)
var hours = Math.floor((uptimeSecs % 86400) / 3600)
var mins = Math.floor((uptimeSecs % 3600) / 60)
root.uptime = "Uptime: " + days + "d " + hours + "h " + mins + "m"
}
}
}
property Timer sysInfoTimer: Timer {
interval: 10000
running: true
repeat: true
triggeredOnStart: true
onTriggered: root.sysInfoProcess.running = true
}
}
+1
View File
@@ -0,0 +1 @@
singleton ClockState ClockState.qml
+183 -5
View File
@@ -8,8 +8,32 @@ QtObject {
id: root
property string status: "..."
property string interfaceName: ""
property string interfaceType: ""
property string connectionName: ""
property string ipv4: ""
property string signalStrength: ""
property string wifiChannel: ""
property string wifiFrequency: ""
property string linkSpeed: ""
property string downloadSpeed: ""
property string uploadSpeed: ""
property string ping: ""
property bool internetAvailable: false
property bool vpnConnected: false
property string vpnName: ""
property string vpnIp: ""
property bool tailscaleConnected: false
property string tailscaleIp: ""
property bool running: false
property string _activeDevice: ""
property real _prevRxBytes: 0
property real _prevTxBytes: 0
property int _prevUpdateTime: 0
property Timer networkTimer: Timer {
interval: 10000
repeat: true
@@ -18,18 +42,172 @@ QtObject {
}
property Process networkScript: Process {
running: true
command: ["sh", "-c", Config.configDir + "/modules/bar/scripts/Network.sh"]
command: ["sh", "-c",
"echo '===SHOW==='; " +
"ACTIVE=$(nmcli -t device status 2>/dev/null | awk -F: '$3==\"connected\"{print $1; exit}'); " +
"echo \"ACTIVE_DEVICE=$ACTIVE\"; " +
"if [ -n \"$ACTIVE\" ]; then " +
" nmcli -t -f ALL device show \"$ACTIVE\" 2>/dev/null; " +
"fi; " +
"echo '===TRAFFIC==='; " +
"if [ -n \"$ACTIVE\" ]; then " +
" echo \"RX_BYTES=$(cat /sys/class/net/$ACTIVE/statistics/rx_bytes 2>/dev/null || echo 0)\"; " +
" echo \"TX_BYTES=$(cat /sys/class/net/$ACTIVE/statistics/tx_bytes 2>/dev/null || echo 0)\"; " +
"fi; " +
"echo '===WIFI==='; " +
"nmcli -t -f IN-USE,SSID,SIGNAL,CHAN,FREQ,RATE device wifi 2>/dev/null | grep '^\\\\*' | head -1 | sed 's/^\\*://'; " +
"echo '===PING==='; " +
"ping -c 1 -W 2 1.1.1.1 2>/dev/null | awk -F/ '/rtt/{print $5}' || echo ''; " +
"echo '===INTERNET==='; " +
"ping -c 1 -W 1 1.1.1.1 2>/dev/null >/dev/null && echo 1 || echo 0; " +
"echo '===VPN==='; " +
"nmcli -t -f NAME,TYPE connection show --active 2>/dev/null | grep -i vpn || true; " +
"echo '===TAILSCALE==='; " +
"TAIL_IP=$(ip -4 addr show tailscale0 2>/dev/null | grep -oP 'inet \\K[\\d.]+'); " +
"if [ -n \"$TAIL_IP\" ]; then echo \"TAILSCALE_IP=$TAIL_IP\"; else echo \"\"; fi"
]
stdout: StdioCollector {
onStreamFinished: {
root.status = this.text.trim()
onStreamFinished: root.parseNetworkOutput(text.trim())
}
}
function formatSpeed(bytesPerSec) {
if (bytesPerSec >= 1073741824) return (bytesPerSec / 1073741824).toFixed(1) + " GB/s"
if (bytesPerSec >= 1048576) return (bytesPerSec / 1048576).toFixed(1) + " MB/s"
if (bytesPerSec >= 1024) return (bytesPerSec / 1024).toFixed(1) + " KB/s"
return bytesPerSec.toFixed(0) + " B/s"
}
function parseNetworkOutput(output) {
var sections = output.split("===TAILSCALE===")
var tailscaleSection = sections.length > 1 ? sections[1].trim() : ""
var beforeTailscale = sections[0]
sections = beforeTailscale.split("===VPN===")
var vpnSection = sections.length > 1 ? sections[1].trim() : ""
var beforeVpn = sections[0]
sections = beforeVpn.split("===INTERNET===")
root.internetAvailable = sections.length > 1 ? sections[1].trim() === "1" : false
var beforeInternet = sections[0]
sections = beforeInternet.split("===PING===")
root.ping = sections.length > 1 ? sections[1].trim() : ""
var beforePing = sections[0]
sections = beforePing.split("===WIFI===")
var wifiSection = sections.length > 1 ? sections[1].trim() : ""
var beforeWifi = sections[0]
sections = beforeWifi.split("===TRAFFIC===")
var trafficSection = sections.length > 1 ? sections[1].trim() : ""
var beforeTraffic = sections[0]
sections = beforeTraffic.split("===SHOW===")
var showSection = sections.length > 1 ? sections[1].trim() : ""
// Parse SHOW section
var activeDevice = ""
var lines = showSection.split("\n")
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim()
if (line.indexOf("ACTIVE_DEVICE=") === 0) {
activeDevice = line.substring("ACTIVE_DEVICE=".length)
} else if (line.indexOf("GENERAL.DEVICE:") === 0) {
root.interfaceName = line.substring("GENERAL.DEVICE:".length)
} else if (line.indexOf("GENERAL.TYPE:") === 0) {
root.interfaceType = line.substring("GENERAL.TYPE:".length)
} else if (line.indexOf("GENERAL.CONNECTION:") === 0) {
root.connectionName = line.substring("GENERAL.CONNECTION:".length)
} else if (line.indexOf("IP4.ADDRESS") >= 0) {
var ipPart = line.substring(line.indexOf(":") + 1)
root.ipv4 = ipPart.split("/")[0]
}
}
// Parse TRAFFIC section
if (trafficSection) {
var trafficLines = trafficSection.split("\n")
var rxBytes = 0
var txBytes = 0
for (var t = 0; t < trafficLines.length; t++) {
var tline = trafficLines[t].trim()
if (tline.indexOf("RX_BYTES=") === 0) rxBytes = parseFloat(tline.substring("RX_BYTES=".length))
else if (tline.indexOf("TX_BYTES=") === 0) txBytes = parseFloat(tline.substring("TX_BYTES=".length))
}
if (activeDevice === root._activeDevice && root._prevRxBytes > 0 && root._prevUpdateTime > 0) {
var interval = (Date.now() - root._prevUpdateTime) / 1000
if (interval > 0) {
root.downloadSpeed = root.formatSpeed((rxBytes - root._prevRxBytes) / interval)
root.uploadSpeed = root.formatSpeed((txBytes - root._prevTxBytes) / interval)
}
} else {
root.downloadSpeed = ""
root.uploadSpeed = ""
}
root._activeDevice = activeDevice
root._prevRxBytes = rxBytes
root._prevTxBytes = txBytes
root._prevUpdateTime = Date.now()
}
// Parse WIFI section
if (wifiSection) {
var wifiParts = wifiSection.split(":")
if (wifiParts.length >= 2) {
root.signalStrength = wifiParts[1] || ""
if (wifiParts.length >= 3) root.wifiChannel = wifiParts[2] || ""
if (wifiParts.length >= 4) root.wifiFrequency = wifiParts[3] || ""
if (wifiParts.length >= 5) root.linkSpeed = wifiParts[4] || ""
}
}
// Determine status icon
if (root.interfaceType === "ethernet") {
root.status = "󰈀"
} else if (root.interfaceType === "wifi") {
if (!root.connectionName) {
root.status = "󰤭"
} else {
var sig = parseInt(root.signalStrength)
if (sig > 80) root.status = "󰤨"
else if (sig > 60) root.status = "󰤥"
else if (sig > 40) root.status = "󰤢"
else if (sig > 20) root.status = "󰤟"
else root.status = "󰤯"
}
} else if (!activeDevice) {
root.status = "󰤭/󱐤"
}
// Parse VPN section
if (vpnSection) {
root.vpnConnected = true
var vpnLines = vpnSection.split("\n")
if (vpnLines.length > 0) {
var vpnParts = vpnLines[0].split(":")
root.vpnName = vpnParts[0] || ""
}
} else {
root.vpnConnected = false
root.vpnName = ""
root.vpnIp = ""
}
// Parse TAILSCALE section
if (tailscaleSection && tailscaleSection.indexOf("TAILSCALE_IP=") === 0) {
root.tailscaleConnected = true
root.tailscaleIp = tailscaleSection.substring("TAILSCALE_IP=".length)
} else {
root.tailscaleConnected = false
root.tailscaleIp = ""
}
}
Component.onCompleted: {
networkScript.running = true
networkTimer.start()
}
}
+82
View File
@@ -0,0 +1,82 @@
import QtQuick
import Quickshell
import "../../../config"
PopupWindow {
id: popup
property Item anchorItem
property bool anchorHovered: false
property int popupWidth: 300
property int popupVerticalOffset: 0
default property alias content: contentColumn.data
visible: false
color: "transparent"
width: popupWidth
height: background.height
onAnchorHoveredChanged: popup.updateVisibility()
function updateVisibility() {
if (anchorHovered || mouseArea.containsMouse) {
closeTimer.stop()
visible = true
} else {
closeTimer.restart()
}
}
Timer {
id: closeTimer
interval: 1
onTriggered: {
if (!anchorHovered && !mouseArea.containsMouse) {
visible = false
}
}
}
anchor {
item: anchorItem
edges: Edges.Top | Edges.Left
gravity: Edges.Bottom | Edges.Right
adjustment: PopupAdjustment.FlipY | PopupAdjustment.SlideX
onAnchoring: {
anchor.rect.x = (anchorItem.width - popup.width) / 2
anchor.rect.y = anchorItem.height + popupVerticalOffset
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: popup.updateVisibility()
Rectangle {
id: background
width: parent.width
height: contentColumn.implicitHeight + 2 * 12
color: Qt.alpha(Colors.primaryContainer, 0.9)
radius: 12
border {
color: Colors.primaryContainer_fg
width: Config.border_width
}
Column {
id: contentColumn
height: implicitHeight
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 12
}
spacing: Config.small_spacing_fun(24)
}
}
}
}
+37 -32
View File
@@ -2,10 +2,11 @@ import QtQuick
import Quickshell
import Quickshell.Hyprland
import "../../../config"
import "../states/Clock"
Rectangle { //workspace
id: clockRect
width: dateTimer.running ? Math.max(dateText.width + timeText.width + Config.spacing_fun(height)*3, bar.height) : Math.max(dateText.width + timeText.width + Config.spacing_fun(height)*2, bar.height)
width: ClockState.expanded ? Math.max(dateText.width + timeText.width + Config.spacing_fun(height)*3, bar.height) : Math.max(timeText.width + Config.spacing_fun(height)*2, bar.height)
height: bar.height
radius: Config.radius_fun(height)
color: clockArea.containsMouse ? Colors.tertiaryContainer : Colors.primaryContainer
@@ -22,9 +23,8 @@ Rectangle { //workspace
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: {
dateTimer.running = !dateTimer.running
if (!dateTimer.running) dateText.currentDate = ""
console.log("[Clock] Date visible: " + dateTimer.running)
ClockState.expanded = !ClockState.expanded
console.log("[Clock] Date visible: " + ClockState.expanded)
}
Row {
@@ -32,54 +32,59 @@ Rectangle { //workspace
Text {
id: dateText
property string currentDate: ""
text: currentDate
text: ClockState.shortDate
visible: ClockState.expanded
color: (
clockArea.containsMouse ? Colors.tertiary :
Colors.primary
)
font.pixelSize: Config.font_size_small(clockRect.height)
font.family: Config.font
Timer {
id: dateTimer
interval: Config.reload_interval
running: false
repeat: true
triggeredOnStart: true
onTriggered: {
var now = new Date()
dateText.currentDate = Qt.formatDate(now, "ddd, dd.MM.yyyy - ")
}
}
}
Text {
id: timeText
property string currentTime: ""
text: currentTime
text: ClockState.currentTime
color: (
clockArea.containsMouse ? Colors.tertiary :
Colors.primary
)
font.pixelSize: Config.font_size_small(clockRect.height)
font.family: Config.font
}
}
}
Component.onCompleted: {
var now = new Date()
currentTime = Qt.formatTime(now, "hh:mm:ss")
}
BarPopup {
id: clockPopup
anchorItem: clockRect
anchorHovered: clockArea.containsMouse
width: Math.max(text1.width, text2.width) + (Config.spacing_fun(clockRect.height) * 3)
height: text1.height*3+(Config.spacing_fun(clockRect.height) * 5)
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var now = new Date()
timeText.currentTime = Qt.formatTime(now, "hh:mm:ss")
}
}
Column {
width: parent.width
spacing: Config.spacing_fun(clockRect.height)
Text {
id: text1
text: ClockState.fullDate
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text2
text: ClockState.uptime
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
}
}
+183
View File
@@ -33,4 +33,187 @@ MouseArea {
color: networkButton.containsMouse ? Colors.tertiary_fg : networkText.text === "󰤭 /󱐤" ? Colors.error : Colors.primary
}
}
BarPopup {
id: clockPopup
anchorItem: networkButton
anchorHovered: networkButton.containsMouse
property int count: 17
width: Math.max(text1.width, text2.width, text3.width, text4.width, text5.width, text6.width) + (Config.spacing_fun(networkButton.height) * 3)
height: text1.height*count+(Config.spacing_fun(networkButton.height) * (count + 2))
Column {
width: parent.width
spacing: Config.spacing_fun(networkButton.height)
Text {
id: text1
text: "interfaceName: " + NetworkState.interfaceName
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text2
text: "interfaceType: " + NetworkState.interfaceType
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text3
text: "connectionName: " + NetworkState.connectionName
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text4
text: "ipv4: " + NetworkState.ipv4
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text5
text: "signalStrength: " + NetworkState.signalStrength
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text6
text: "wifiChannel: " + NetworkState.wifiChannel
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text7
text: "wifiFrequency: " + NetworkState.wifiFrequency
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text8
text: "linkSpeed: " + NetworkState.linkSpeed
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text9
text: "downloadSpeed: " + NetworkState.downloadSpeed
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text10
text: "uploadSpeed: " + NetworkState.uploadSpeed
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text11
text: "ping: " + NetworkState.ping
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text12
text: "internetAvailable: " + NetworkState.internetAvailable
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text13
text: "vpnConnected: " + NetworkState.vpnConnected
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text14
text: "vpnName: " + NetworkState.vpnName
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text15
text: "vpnIp: " + NetworkState.vpnIp
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text16
text: "tailscaleConnected: " + NetworkState.tailscaleConnected
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
Text {
id: text17
text: "tailscaleIp: " + NetworkState.tailscaleIp
wrapMode: Text.WordWrap
color: Colors.primary
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: Config.font_size_tiny(bar.height)
font.family: Config.font
}
}
}
}