nova-shell/shell/dock/AppletDock.qml

409 lines
12 KiB
QML

import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Io
import Quickshell.Services.Mpris
import Quickshell.Services.Pipewire
import "." as D
import "../services" as S
import "../applets" as C
import "../modules" as M
PanelWindow {
id: root
required property var screen
visible: D.DockState.open
color: "transparent"
WlrLayershell.layer: D.DockState.mode === "pinned" ? WlrLayer.Top : WlrLayer.Overlay
WlrLayershell.exclusiveZone: D.DockState.mode === "pinned" ? _dockWidth : 0
WlrLayershell.namespace: "nova-dock"
anchors.top: true
anchors.right: true
anchors.bottom: true
readonly property int _dockWidth: S.Modules.dock.width ?? 300
readonly property var _applets: S.Modules.dock.applets ?? {}
readonly property color _accent: S.Theme.base0C
implicitWidth: _dockWidth
// Slide animation
property real _slideX: D.DockState.open ? 0 : _dockWidth
Behavior on _slideX {
enabled: !S.Theme.reducedMotion
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
// Overlay mode: close when cursor leaves
HoverHandler {
id: _dockHover
onHoveredChanged: {
if (!hovered && D.DockState.mode === "overlay")
_overlayCloseTimer.restart();
else
_overlayCloseTimer.stop();
}
}
Timer {
id: _overlayCloseTimer
interval: 200
onTriggered: if (D.DockState.mode === "overlay")
D.DockState.close()
}
// Background
Rectangle {
id: _bg
anchors.fill: parent
color: S.Theme.base00
opacity: Math.max(S.Theme.barOpacity, 0.85)
transform: Translate {
x: root._slideX
}
}
// Accent border on the left edge
Rectangle {
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 1
color: root._accent
opacity: _bg.opacity
transform: Translate {
x: root._slideX
}
}
// Content
Flickable {
id: _flickable
anchors.fill: parent
contentHeight: _column.height
clip: true
boundsBehavior: Flickable.StopAtBounds
transform: Translate {
x: root._slideX
}
Column {
id: _column
width: root._dockWidth
spacing: 8
topPadding: 8
bottomPadding: 8
leftPadding: 8
rightPadding: 8
// Clock
D.DockCard {
visible: root._applets.clock ?? true
icon: "\uF017"
title: "Clock"
accentColor: root._accent
width: parent.width - 16
C.ClockApplet {
width: parent.width
accentColor: root._accent
currentDate: _clock.date
}
}
// CPU
D.DockCard {
id: _cpuCard
visible: root._applets.cpu ?? true
icon: "\uF2DB"
title: "CPU"
accentColor: root._accent
width: parent.width - 16
C.CpuApplet {
width: parent.width
cores: S.SystemStats.cpuCores
coreMaxFreq: S.SystemStats.cpuCoreMaxFreq
coreTypes: S.SystemStats.cpuCoreTypes
processes: _cpuProcs.processes
accentColor: root._accent
active: _cpuCard.expanded
}
}
// GPU
D.DockCard {
visible: (root._applets.gpu ?? true) && S.SystemStats.gpuAvailable
icon: "\uEB4C"
title: "GPU"
accentColor: root._accent
width: parent.width - 16
C.GpuApplet {
width: parent.width
active: parent.expanded
accentColor: root._accent
}
}
// Memory
D.DockCard {
id: _memCard
visible: root._applets.memory ?? true
icon: "\uEFC5"
title: "Memory"
accentColor: root._accent
width: parent.width - 16
C.MemoryApplet {
width: parent.width
percent: S.SystemStats.memPercent
usedGb: S.SystemStats.memUsedGb
totalGb: S.SystemStats.memTotalGb
availGb: S.SystemStats.memAvailGb
cachedGb: S.SystemStats.memCachedGb
buffersGb: S.SystemStats.memBuffersGb
processes: _memProcs.processes
accentColor: root._accent
active: _memCard.expanded
}
}
// Temperature
D.DockCard {
visible: root._applets.temperature ?? true
icon: "\uF2C9"
title: "Temperature"
accentColor: root._accent
width: parent.width - 16
C.TemperatureApplet {
width: parent.width
temp: S.SystemStats.tempCelsius
warm: S.Modules.temperature.warm || 80
hot: S.Modules.temperature.hot || 90
history: S.SystemStats.tempHistory
devices: S.SystemStats.tempDevices
accentColor: root._accent
deviceFilter: S.Modules.temperature.device || ""
active: parent.parent.expanded
}
}
// Disk
D.DockCard {
visible: root._applets.disk ?? true
icon: "\uF0C9"
title: "Disk"
accentColor: root._accent
width: parent.width - 16
C.DiskApplet {
width: parent.width
mounts: S.SystemStats.diskMounts
accentColor: root._accent
}
}
// Battery
D.DockCard {
visible: (root._applets.battery ?? true) && S.BatteryService.available
icon: "\uDB80\uDC84"
title: "Battery"
accentColor: root._accent
width: parent.width - 16
C.BatteryApplet {
width: parent.width
active: parent.expanded
accentColor: root._accent
}
}
// Network
D.DockCard {
visible: root._applets.network ?? true
icon: "\uF1EB"
title: "Network"
accentColor: root._accent
width: parent.width - 16
C.NetworkApplet {
width: parent.width
accentColor: root._accent
}
}
// Bluetooth
D.DockCard {
visible: (root._applets.bluetooth ?? true) && S.BluetoothService.state !== "unavailable"
icon: "\uF294"
title: "Bluetooth"
accentColor: root._accent
width: parent.width - 16
C.BluetoothApplet {
width: parent.width
accentColor: root._accent
}
}
// Volume
D.DockCard {
visible: root._applets.volume ?? true
icon: "\uF028"
title: "Sound"
accentColor: root._accent
width: parent.width - 16
C.VolumeApplet {
width: parent.width
sink: Pipewire.defaultAudioSink
sinkList: root._sinkList
streamList: root._streamList
accentColor: root._accent
}
}
// Backlight
D.DockCard {
visible: (root._applets.backlight ?? true) && S.BacklightService.available
icon: "\uF185"
title: "Brightness"
accentColor: root._accent
width: parent.width - 16
C.BacklightApplet {
width: parent.width
percent: S.BacklightService.percent
accentColor: root._accent
onSetPercent: pct => S.BacklightService.setPercent(pct)
}
}
// Weather
D.DockCard {
visible: (root._applets.weather ?? true) && S.WeatherService.available
icon: S.WeatherService.icon
title: "Weather"
accentColor: root._accent
width: parent.width - 16
C.WeatherApplet {
width: parent.width
accentColor: root._accent
}
}
// Now Playing
D.DockCard {
visible: (root._applets.mpris ?? true) && S.MprisService.player !== null
icon: "\uF04B"
title: "Now Playing"
accentColor: root._accent
width: parent.width - 16
C.MprisApplet {
width: parent.width
player: S.MprisService.player
players: S.MprisService.players
playing: S.MprisService.playing
accentColor: root._accent
playerIdx: S.MprisService.playerIdx
onPlayerSwitched: idx => S.MprisService.switchPlayer(idx)
}
}
// Notifications
D.DockCard {
visible: root._applets.notifications ?? true
icon: "\uDB80\uDC9C"
title: "Notifications"
accentColor: root._accent
width: parent.width - 16
C.NotifApplet {
width: parent.width
contentWidth: root._dockWidth - 16
accentColor: root._accent
}
}
// Power
D.DockCard {
visible: root._applets.power ?? true
icon: "\uF011"
title: "Power"
accentColor: root._accent
width: parent.width - 16
C.PowerApplet {
width: parent.width
accentColor: root._accent
onRunCommand: cmd => {
_runner.command = cmd;
_runner.running = true;
}
onDismiss: D.DockState.close()
}
}
}
}
// Shared resources
SystemClock {
id: _clock
precision: SystemClock.Seconds
}
M.ProcessList {
id: _cpuProcs
sortBy: "cpu"
active: _cpuCard.expanded && D.DockState.open
}
M.ProcessList {
id: _memProcs
sortBy: "mem"
active: _memCard.expanded && D.DockState.open
}
PwObjectTracker {
objects: [Pipewire.defaultAudioSink, ...root._streamList]
}
readonly property var _sinkList: {
const sinks = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (!node.isStream && node.isSink)
sinks.push(node);
}
return sinks;
}
readonly property var _streamList: {
const streams = [];
if (Pipewire.nodes) {
for (const node of Pipewire.nodes.values)
if (node.isStream && node.audio)
streams.push(node);
}
return streams;
}
Process {
id: _runner
}
}