Files
dotfiles/.config/quickshell/caelestia/modules/bar/components/StatusIcons.qml

271 lines
8.5 KiB
QML

pragma ComponentBehavior: Bound
import qs.components
import qs.services
import qs.utils
import qs.config
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Services.UPower
import QtQuick
import QtQuick.Layouts
StyledRect {
id: root
property color colour: Colours.palette.m3secondary
readonly property alias items: iconColumn
color: Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.full
clip: true
implicitWidth: Config.bar.sizes.innerWidth
implicitHeight: iconColumn.implicitHeight + Appearance.padding.normal * 2 - (Config.bar.status.showLockStatus && !Hypr.capsLock && !Hypr.numLock ? iconColumn.spacing : 0)
ColumnLayout {
id: iconColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: Appearance.padding.normal
spacing: Appearance.spacing.smaller / 2
// Lock keys status
WrappedLoader {
name: "lockstatus"
active: Config.bar.status.showLockStatus
sourceComponent: ColumnLayout {
spacing: 0
Item {
implicitWidth: capslockIcon.implicitWidth
implicitHeight: Hypr.capsLock ? capslockIcon.implicitHeight : 0
MaterialIcon {
id: capslockIcon
anchors.centerIn: parent
scale: Hypr.capsLock ? 1 : 0.5
opacity: Hypr.capsLock ? 1 : 0
text: "keyboard_capslock_badge"
color: root.colour
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
}
Behavior on implicitHeight {
Anim {}
}
}
Item {
Layout.topMargin: Hypr.capsLock && Hypr.numLock ? iconColumn.spacing : 0
implicitWidth: numlockIcon.implicitWidth
implicitHeight: Hypr.numLock ? numlockIcon.implicitHeight : 0
MaterialIcon {
id: numlockIcon
anchors.centerIn: parent
scale: Hypr.numLock ? 1 : 0.5
opacity: Hypr.numLock ? 1 : 0
text: "looks_one"
color: root.colour
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
}
Behavior on implicitHeight {
Anim {}
}
}
}
}
// Audio icon
WrappedLoader {
name: "audio"
active: Config.bar.status.showAudio
sourceComponent: MaterialIcon {
animate: true
text: Icons.getVolumeIcon(Audio.volume, Audio.muted)
color: root.colour
}
}
// Microphone icon
WrappedLoader {
name: "audio"
active: Config.bar.status.showMicrophone
sourceComponent: MaterialIcon {
animate: true
text: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
color: root.colour
}
}
// Keyboard layout icon
WrappedLoader {
name: "kblayout"
active: Config.bar.status.showKbLayout
sourceComponent: StyledText {
animate: true
text: Hypr.kbLayout
color: root.colour
font.family: Appearance.font.family.mono
}
}
// Network icon
WrappedLoader {
name: "network"
active: Config.bar.status.showNetwork && (!Nmcli.activeEthernet || Config.bar.status.showWifi)
sourceComponent: MaterialIcon {
animate: true
text: Nmcli.active ? Icons.getNetworkIcon(Nmcli.active.strength ?? 0) : "wifi_off"
color: root.colour
}
}
// Ethernet icon
WrappedLoader {
name: "ethernet"
active: Config.bar.status.showNetwork && Nmcli.activeEthernet
sourceComponent: MaterialIcon {
animate: true
text: "cable"
color: root.colour
}
}
// Bluetooth section
WrappedLoader {
Layout.preferredHeight: implicitHeight
name: "bluetooth"
active: Config.bar.status.showBluetooth
sourceComponent: ColumnLayout {
spacing: Appearance.spacing.smaller / 2
// Bluetooth icon
MaterialIcon {
animate: true
text: {
if (!Bluetooth.defaultAdapter?.enabled)
return "bluetooth_disabled";
if (Bluetooth.devices.values.some(d => d.connected))
return "bluetooth_connected";
return "bluetooth";
}
color: root.colour
}
// Connected bluetooth devices
Repeater {
model: ScriptModel {
values: Bluetooth.devices.values.filter(d => d.state !== BluetoothDeviceState.Disconnected)
}
MaterialIcon {
id: device
required property BluetoothDevice modelData
animate: true
text: Icons.getBluetoothIcon(modelData?.icon)
color: root.colour
fill: 1
SequentialAnimation on opacity {
running: device.modelData?.state !== BluetoothDeviceState.Connected
alwaysRunToEnd: true
loops: Animation.Infinite
Anim {
from: 1
to: 0
duration: Appearance.anim.durations.large
easing.bezierCurve: Appearance.anim.curves.standardAccel
}
Anim {
from: 0
to: 1
duration: Appearance.anim.durations.large
easing.bezierCurve: Appearance.anim.curves.standardDecel
}
}
}
}
}
Behavior on Layout.preferredHeight {
Anim {}
}
}
// Battery icon
WrappedLoader {
name: "battery"
active: Config.bar.status.showBattery
sourceComponent: MaterialIcon {
animate: true
text: {
if (!UPower.displayDevice.isLaptopBattery) {
if (PowerProfiles.profile === PowerProfile.PowerSaver)
return "energy_savings_leaf";
if (PowerProfiles.profile === PowerProfile.Performance)
return "rocket_launch";
return "balance";
}
const perc = UPower.displayDevice.percentage;
const charging = [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state);
if (perc === 1)
return charging ? "battery_charging_full" : "battery_full";
let level = Math.floor(perc * 7);
if (charging && (level === 4 || level === 1))
level--;
return charging ? `battery_charging_${(level + 3) * 10}` : `battery_${level}_bar`;
}
color: !UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? root.colour : Colours.palette.m3error
fill: 1
}
}
}
component WrappedLoader: Loader {
required property string name
Layout.alignment: Qt.AlignHCenter
visible: active
}
}