Files
NixOS-Configs/home/hypr/quickshell/components/SidePanel.qml

532 lines
17 KiB
QML

import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import "../theme"
import ".."
PanelWindow {
id: root
property var screen
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "quickshell-sidepanel"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
anchors {
top: true
bottom: true
left: true
right: false
}
exclusiveZone: Theme.sidebarWidth
implicitWidth: Theme.sidebarWidth
color: Theme.bgPrimary
// Right edge hairline border
Rectangle {
width: 1
color: Theme.glassBorder
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
}
}
component Separator: Rectangle {
width: 36
height: 1
color: Theme.glassBorder
Layout.alignment: Qt.AlignHCenter
}
component DockIcon: Item {
id: dockIconRoot
width: 36
height: 36
Layout.alignment: Qt.AlignHCenter
property string appClass: ""
property string execCmd: ""
property string iconSource: ""
property string tooltipText: ""
property bool isRunning: (appClass !== "" && GlobalState.activeWindowClass === appClass) || (GlobalState.runningApps && GlobalState.runningApps.indexOf(appClass) !== -1)
Image {
id: img
anchors.centerIn: parent
width: 24
height: 24
source: iconSource ? Qt.resolvedUrl("../assets/" + iconSource) : ""
sourceSize: Qt.size(24, 24)
fillMode: Image.PreserveAspectFit
opacity: mouseArea.containsMouse ? 1.0 : 0.8
scale: mouseArea.containsMouse ? 1.15 : 1.0
Behavior on scale {
NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Behavior on opacity {
NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
Rectangle {
id: runningDot
width: 4
height: 4
radius: 2
color: Theme.accentVolt
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
visible: isRunning
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (execCmd !== "") {
GlobalState.launchApp(execCmd)
}
}
}
// Tooltip
Rectangle {
id: tooltip
visible: mouseArea.containsMouse && tooltipText !== ""
color: Theme.glassBg
radius: 4
width: tooltipLabel.implicitWidth + 16
height: tooltipLabel.implicitHeight + 8
anchors.left: parent.right
anchors.leftMargin: 24
anchors.verticalCenter: parent.verticalCenter
Text {
id: tooltipLabel
anchors.centerIn: parent
text: tooltipText
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 11
}
}
}
ColumnLayout {
anchors.fill: parent
spacing: 8
// Top spacer
Item { height: 4; Layout.fillWidth: true }
// 1. AG Logo Button
Rectangle {
width: 36
height: 36
radius: 4
color: logoMouse.containsMouse ? Qt.rgba(1,1,1,0.05) : "transparent"
Layout.alignment: Qt.AlignHCenter
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Text {
anchors.centerIn: parent
text: "AG"
font.family: Theme.fontSerif
font.italic: true
font.pixelSize: 16
color: Theme.accentVolt
}
MouseArea {
id: logoMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.appleMenuVisible = !GlobalState.appleMenuVisible
}
}
Separator {}
// 3. Workspace Indicators
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 2
Repeater {
model: GlobalState.workspaceList
delegate: Rectangle {
width: 52
height: 36
color: wsMouse.containsMouse ? Qt.rgba(1,1,1,0.04) : "transparent"
property bool isActive: GlobalState.activeWorkspace === modelData
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
Rectangle {
width: 2
height: 20
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
color: Theme.accentVolt
visible: isActive
}
Column {
anchors.centerIn: parent
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (modelData < 10 ? "0" : "") + modelData
color: isActive ? Theme.accentVolt : Theme.textMuted
font.family: Theme.fontMono
font.pixelSize: 12
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (GlobalState.workspaceNames && GlobalState.workspaceNames[modelData]) ? GlobalState.workspaceNames[modelData] : ""
color: Theme.textMuted
font.family: Theme.fontSans
font.pixelSize: 8
opacity: 0.7
}
}
MouseArea {
id: wsMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.switchWorkspace(modelData)
}
}
}
}
Separator {}
// 5. Spotlight trigger
Item {
width: 36
height: 36
Layout.alignment: Qt.AlignHCenter
Text {
anchors.centerIn: parent
text: "⌕"
font.pixelSize: 20
color: spotlightMouse.containsMouse ? Theme.accentVolt : Theme.textMuted
Behavior on color {
ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic }
}
}
MouseArea {
id: spotlightMouse
anchors.fill: parent
hoverEnabled: true
onClicked: GlobalState.toggleSpotlight()
}
}
Separator {}
// 7. Pinned Apps
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
DockIcon {
appClass: ""
execCmd: "nautilus"
iconSource: "finder.svg"
tooltipText: "Finder"
}
DockIcon {
appClass: "brave-browser"
execCmd: "brave"
iconSource: "browser.svg"
tooltipText: "Brave Browser"
}
DockIcon {
appClass: "foot"
execCmd: "foot"
iconSource: "terminal.svg"
tooltipText: "Terminal"
}
DockIcon {
appClass: "nvim"
execCmd: "foot -e nvim"
iconSource: "code.svg"
tooltipText: "Code"
}
DockIcon {
appClass: "pavucontrol"
execCmd: "pavucontrol"
iconSource: "preferences.svg"
tooltipText: "Settings"
}
DockIcon {
appClass: ""
execCmd: "spotify"
iconSource: "music.svg"
tooltipText: "Music"
}
}
Separator {}
// 9. Running Apps (non-pinned)
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
Repeater {
model: {
var pinned = ["foot", "brave-browser", "pavucontrol", "nvim", "nautilus", "spotify", ""]
var running = []
var seen = {}
if (GlobalState.runningApps) {
for (var i = 0; i < GlobalState.runningApps.length; i++) {
var cls = GlobalState.runningApps[i]
if (cls && pinned.indexOf(cls) === -1 && !seen[cls]) {
seen[cls] = true
running.push(cls)
}
}
}
return running
}
delegate: Item {
width: 36
height: 36
Rectangle {
anchors.centerIn: parent
width: 24
height: 24
radius: 12
color: Qt.rgba(1, 1, 1, 0.1)
Text {
anchors.centerIn: parent
text: modelData.charAt(0).toUpperCase()
color: Theme.textPrimary
font.family: Theme.fontSans
font.pixelSize: 12
}
}
Rectangle {
width: 4
height: 4
radius: 2
color: Theme.accentVolt
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
// Focus app logic here
}
}
}
}
}
// 10. Flexible spacer
Item { Layout.fillHeight: true }
Separator {}
// 12. Telemetry mini-readouts
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "CPU" + (GlobalState.cpuPercent !== undefined ? GlobalState.cpuPercent : "0") + "%"
font.family: Theme.fontMono
font.pixelSize: 11
color: (GlobalState.cpuPercent !== undefined && GlobalState.cpuPercent > 80) ? Theme.accentCoral : Theme.textMuted
Behavior on color { ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "RAM" + (GlobalState.memUsedGb !== undefined ? GlobalState.memUsedGb : "0") + "G"
font.family: Theme.fontMono
font.pixelSize: 11
color: (GlobalState.memPercent !== undefined && GlobalState.memPercent > 85) ? Theme.accentCoral : Theme.textMuted
Behavior on color { ColorAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "VRM" + (GlobalState.gpuVramUsed !== undefined ? GlobalState.gpuVramUsed : "0") + "G"
font.family: Theme.fontMono
font.pixelSize: 11
color: Theme.textMuted
}
}
Separator {}
// 14. System tray icons
Column {
Layout.alignment: Qt.AlignHCenter
spacing: 8
// Volume
Item {
width: 36
height: 36
Image {
anchors.centerIn: parent
width: 16
height: 16
source: Qt.resolvedUrl("../assets/volume.svg")
sourceSize: Qt.size(16, 16)
opacity: volMouse.containsMouse ? 1.0 : 0.7
Behavior on opacity { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
MouseArea {
id: volMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "volume"
GlobalState.systemHudVisible = true
}
}
}
// WiFi
Item {
width: 36
height: 36
Image {
anchors.centerIn: parent
width: 16
height: 16
source: Qt.resolvedUrl("../assets/wifi.svg")
sourceSize: Qt.size(16, 16)
opacity: GlobalState.wifiConnected ? 0.7 : 0.3
Behavior on opacity { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "network"
GlobalState.systemHudVisible = true
}
}
}
// Battery
Item {
width: 36
height: 44
visible: GlobalState.batteryPresent !== undefined ? GlobalState.batteryPresent : false
Column {
anchors.centerIn: parent
spacing: 2
Image {
anchors.horizontalCenter: parent.horizontalCenter
width: 16
height: 16
source: Qt.resolvedUrl("../assets/battery.svg")
sourceSize: Qt.size(16, 16)
opacity: (GlobalState.batteryPercent !== undefined && GlobalState.batteryPercent < 20) ? 1.0 : 0.7
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: (GlobalState.batteryPercent !== undefined ? GlobalState.batteryPercent : "100") + "%"
font.family: Theme.fontMono
font.pixelSize: 9
color: (GlobalState.batteryPercent !== undefined && GlobalState.batteryPercent < 20) ? Theme.accentCoral : Theme.textSecondary
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "battery"
GlobalState.systemHudVisible = true
}
}
}
}
Separator {}
// 16. Clock
Item {
width: 36
height: 44
Layout.alignment: Qt.AlignHCenter
Column {
anchors.centerIn: parent
spacing: 2
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: GlobalState.timeString !== undefined ? GlobalState.timeString : "00:00"
font.family: Theme.fontMono
font.pixelSize: 12
color: Theme.textPrimary
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: GlobalState.dateString !== undefined ? GlobalState.dateString : "Jan 01"
font.family: Theme.fontMono
font.pixelSize: 9
color: Theme.textMuted
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
GlobalState.systemHudActiveSection = "calendar"
GlobalState.systemHudVisible = true
}
}
}
// Bottom spacer
Item { height: 4; Layout.fillWidth: true }
}
}