diff --git a/Config/AppearanceConf.qml b/Config/AppearanceConf.qml index 60c648c..0debb62 100644 --- a/Config/AppearanceConf.qml +++ b/Config/AppearanceConf.qml @@ -66,8 +66,8 @@ JsonObject { } component Padding: JsonObject { property int large: 15 * scale - property int larger: 12 * scale - property int normal: 10 * scale + property int larger: 13 * scale + property int normal: 9 * scale property real scale: 1 property int small: 5 * scale property int smaller: 7 * scale @@ -75,18 +75,18 @@ JsonObject { } component Rounding: JsonObject { property int full: 1000 * scale - property int large: 25 * scale - property int normal: 17 * scale + property int large: 24 * scale + property int normal: 18 * scale property real scale: 1 property int small: 12 * scale property int smallest: 8 * scale } component Spacing: JsonObject { property int large: 20 * scale - property int larger: 15 * scale + property int larger: 16 * scale property int normal: 12 * scale property real scale: 1 - property int small: 7 * scale + property int small: 8 * scale property int smaller: 10 * scale } component Transparency: JsonObject { diff --git a/Config/Config.qml b/Config/Config.qml index b88f373..c4777dc 100644 --- a/Config/Config.qml +++ b/Config/Config.qml @@ -270,6 +270,7 @@ Singleton { function serializeServices(): var { return { weatherLocation: services.weatherLocation, + updates: services.updates, useFahrenheit: services.useFahrenheit, ddcutilService: services.ddcutilService, useTwelveHourClock: services.useTwelveHourClock, @@ -322,7 +323,6 @@ Singleton { ElapsedTimer { id: timer - } Timer { diff --git a/Config/DynamicColors.qml b/Config/DynamicColors.qml index ce13971..84d16df 100644 --- a/Config/DynamicColors.qml +++ b/Config/DynamicColors.qml @@ -78,12 +78,29 @@ Singleton { return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1); } + function reloadHyprRules(): void { + const barStr = "keyword layerrule %1 %2, match:namespace ZShell-Bar"; + const authStr = "keyword layerrule %1 %2, match:namespace ZShell-Auth"; + Hypr.extras.batchMessage([barStr.arg("blur").arg(transparency.enabled ? 1 : 0), barStr.arg("ignore_alpha").arg(transparency.base - 0.03)]); + Hypr.extras.batchMessage([authStr.arg("blur").arg(transparency.enabled ? 1 : 0), authStr.arg("ignore_alpha").arg(transparency.base - 0.03)]); + } + function setMode(mode: string): void { Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]); Config.general.color.mode = mode; Config.save(); } + Component.onCompleted: debounceTimer.triggered() + + Connections { + function onConfigReloaded(): void { + root.reloadHyprRules(); + } + + target: Hypr + } + FileView { path: `${Paths.state}/scheme.json` watchChanges: true @@ -92,72 +109,20 @@ Singleton { onLoaded: root.load(text(), false) } + Timer { + id: debounceTimer + + interval: 300 + + onTriggered: root.reloadHyprRules() + } + ImageAnalyser { id: analyser source: WallpaperPath.currentWallpaperPath } - component M3MaccchiatoPalette: QtObject { - property color m3background: "#131317" - property color m3error: "#ffb4ab" - property color m3errorContainer: "#93000a" - property color m3inverseOnSurface: "#303034" - property color m3inversePrimary: "#525b92" - property color m3inverseSurface: "#e4e1e7" - property color m3neutral_paletteKeyColor: "#77767b" - property color m3neutral_variant_paletteKeyColor: "#767680" - property color m3onBackground: "#e4e1e7" - property color m3onError: "#690005" - property color m3onErrorContainer: "#ffdad6" - property color m3onPrimary: "#232c60" - property color m3onPrimaryContainer: "#ffffff" - property color m3onPrimaryFixed: "#0b154b" - property color m3onPrimaryFixedVariant: "#3a4378" - property color m3onSecondary: "#2c2f44" - property color m3onSecondaryContainer: "#b1b3ce" - property color m3onSecondaryFixed: "#171a2e" - property color m3onSecondaryFixedVariant: "#42455c" - property color m3onSuccess: "#213528" - property color m3onSuccessContainer: "#D1E9D6" - property color m3onSurface: "#e4e1e7" - property color m3onSurfaceVariant: "#c6c5d1" - property color m3onTertiary: "#4c1f48" - property color m3onTertiaryContainer: "#000000" - property color m3onTertiaryFixed: "#340831" - property color m3onTertiaryFixedVariant: "#66365f" - property color m3outline: "#90909a" - property color m3outlineVariant: "#46464f" - property color m3primary: "#bac3ff" - property color m3primaryContainer: "#6a73ac" - property color m3primaryFixed: "#dee0ff" - property color m3primaryFixedDim: "#bac3ff" - property color m3primary_paletteKeyColor: "#6a73ac" - property color m3scrim: "#000000" - property color m3secondary: "#c3c5e0" - property color m3secondaryContainer: "#42455c" - property color m3secondaryFixed: "#dfe1fd" - property color m3secondaryFixedDim: "#c3c5e0" - property color m3secondary_paletteKeyColor: "#72758e" - property color m3shadow: "#000000" - property color m3success: "#B5CCBA" - property color m3successContainer: "#374B3E" - property color m3surface: "#131317" - property color m3surfaceBright: "#39393d" - property color m3surfaceContainer: "#1f1f23" - property color m3surfaceContainerHigh: "#2a2a2e" - property color m3surfaceContainerHighest: "#353438" - property color m3surfaceContainerLow: "#1b1b1f" - property color m3surfaceContainerLowest: "#0e0e12" - property color m3surfaceDim: "#131317" - property color m3surfaceTint: "#bac3ff" - property color m3surfaceVariant: "#46464f" - property color m3tertiary: "#f1b3e5" - property color m3tertiaryContainer: "#b77ead" - property color m3tertiaryFixed: "#ffd7f4" - property color m3tertiaryFixedDim: "#f1b3e5" - property color m3tertiary_paletteKeyColor: "#9b6592" - } component M3Palette: QtObject { property color m3background: "#191114" property color m3error: "#ffb4ab" @@ -279,8 +244,11 @@ Singleton { readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor) } component Transparency: QtObject { - readonly property real base: Appearance.transparency.base - (root.light ? 0.1 : 0) + readonly property real base: Math.max(0, Math.min(1, Appearance.transparency.base - (root.light ? 0.1 : 0))) readonly property bool enabled: Appearance.transparency.enabled readonly property real layers: Appearance.transparency.layers + + onBaseChanged: debounceTimer.restart() + onEnabledChanged: debounceTimer.restart() } } diff --git a/Config/Services.qml b/Config/Services.qml index e091fa2..4711120 100644 --- a/Config/Services.qml +++ b/Config/Services.qml @@ -14,6 +14,7 @@ JsonObject { "to": "YT Music" } ] + property bool updates: true property bool useFahrenheit: false property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a") property int visualizerBars: 30 diff --git a/Greeter/UserDock.qml b/Greeter/UserDock.qml index 6418a93..c9f067a 100644 --- a/Greeter/UserDock.qml +++ b/Greeter/UserDock.qml @@ -67,6 +67,7 @@ ColumnLayout { anchors.fill: parent clip: true + currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1 highlightFollowsCurrentItem: false model: root.greeter.users spacing: Appearance.spacing.small @@ -84,7 +85,7 @@ ColumnLayout { StateLayer { function onClicked(): void { - users.currentIndex = index; + root.greeter.selectUser(modelData.username); } } diff --git a/Greeter/scripts/get-users b/Greeter/scripts/get-users index 7226e4c..94ffccc 100755 --- a/Greeter/scripts/get-users +++ b/Greeter/scripts/get-users @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import glob import json import os import pwd @@ -17,21 +18,59 @@ INVALID_SHELLS = { # Minimum UID for regular users (typically 1000 on most Linux distributions) MIN_UID = 1000 +GREETER_IMAGES_DIR = "/etc/zshell-greeter/images" def get_face_path(home: str, username: str) -> str: """Get the path to the user's face/avatar image if it exists.""" + greeter_candidates = [ + os.path.join(GREETER_IMAGES_DIR, f"{username}.face"), + os.path.join(GREETER_IMAGES_DIR, f"{username}.face.icon"), + ] + + for path in greeter_candidates: + if os.path.isfile(path): + return path + + for path in sorted(glob.glob(os.path.join(GREETER_IMAGES_DIR, f"{username}.*"))): + if os.path.isfile(path): + return path + + direct_path = os.path.join(GREETER_IMAGES_DIR, username) + if os.path.isfile(direct_path): + return direct_path + + accountsservice_user = f"/var/lib/AccountsService/users/{username}" + if os.path.isfile(accountsservice_user): + try: + with open(accountsservice_user, "r", encoding="utf-8") as handle: + for line in handle: + if line.startswith("Icon="): + icon_path = line.split("=", 1)[1].strip() + if icon_path: + if not os.path.isabs(icon_path): + icon_path = os.path.join("/var/lib/AccountsService/icons", icon_path) + if os.path.isfile(icon_path): + return icon_path + except OSError: + pass + # Check common locations for user avatars candidates = [ os.path.join(home, ".face"), os.path.join(home, ".face.icon"), f"/var/lib/AccountsService/icons/{username}", + f"/usr/share/pixmaps/faces/{username}", ] for path in candidates: if os.path.isfile(path): return path + for path in sorted(glob.glob(f"/var/lib/AccountsService/icons/{username}.*")): + if os.path.isfile(path): + return path + return "" diff --git a/Helpers/Updates.qml b/Helpers/Updates.qml index 48faa89..fb1bbab 100644 --- a/Helpers/Updates.qml +++ b/Helpers/Updates.qml @@ -5,6 +5,7 @@ import QtQuick import Quickshell import Quickshell.Io import qs.Paths +import qs.Config Singleton { id: root @@ -44,13 +45,14 @@ Singleton { Timer { interval: 1 repeat: true - running: true + running: Config.services.updates onTriggered: { if (!root.loaded || !root.commandReady) return; - updatesProc.running = true; + if (Config.services.updates) + updatesProc.running = true; interval = 5000; } } @@ -66,7 +68,7 @@ Singleton { Process { id: cmdDetect - command: ["sh", "-c", "command -v yay || command -v paru"] + command: ["sh", "-c", "command -v checkupdates || command -v yay || command -v paru"] running: true stdout: StdioCollector { @@ -80,7 +82,11 @@ Singleton { helper = "pacman"; } - updatesProc.command = [helper, "-Qu"]; + if (helper === "checkupdates") { + updatesProc.command = [helper]; + } else { + updatesProc.command = [helper, "-Qu"]; + } root.commandReady = true; } } diff --git a/Modules/AudioWidget.qml b/Modules/AudioWidget.qml deleted file mode 100644 index 2a99d5f..0000000 --- a/Modules/AudioWidget.qml +++ /dev/null @@ -1,96 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import Quickshell.Io -import Quickshell.Services.Pipewire -import qs.Daemons -import qs.Modules -import qs.Config -import qs.Components - -CustomRect { - id: root - - property color barColor: DynamicColors.palette.m3primary - property color textColor: DynamicColors.palette.m3onSurface - - color: DynamicColors.tPalette.m3surfaceContainer - implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 - implicitWidth: 150 - radius: Appearance.rounding.full - - Behavior on implicitWidth { - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - } - - Component.onCompleted: console.log(root.height, root.implicitHeight) - - RowLayout { - id: layout - - anchors.left: parent.left - anchors.leftMargin: Appearance.padding.small - anchors.verticalCenter: parent.verticalCenter - width: root.implicitWidth - Appearance.padding.small * 3 - - MaterialIcon { - Layout.alignment: Qt.AlignVCenter - animate: true - color: Audio.muted ? DynamicColors.palette.m3error : root.textColor - font.pointSize: Appearance.font.size.larger - text: Audio.muted ? "volume_off" : "volume_up" - } - - CustomRect { - Layout.fillWidth: true - color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2) - implicitHeight: 4 - radius: 20 - - CustomRect { - id: sinkVolumeBar - - color: Audio.muted ? DynamicColors.palette.m3error : root.barColor - implicitWidth: parent.width * (Audio.volume ?? 0) - radius: parent.radius - - anchors { - bottom: parent.bottom - left: parent.left - top: parent.top - } - } - } - - MaterialIcon { - Layout.alignment: Qt.AlignVCenter - animate: true - color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor - font.pointSize: Appearance.font.size.larger - text: Audio.sourceMuted ? "mic_off" : "mic" - } - - CustomRect { - Layout.fillWidth: true - color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2) - implicitHeight: 4 - radius: 20 - - CustomRect { - id: sourceVolumeBar - - color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.barColor - implicitWidth: parent.width * (Audio.sourceVolume ?? 0) - radius: parent.radius - - anchors { - bottom: parent.bottom - left: parent.left - top: parent.top - } - } - } - } -} diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 8115e8e..74459f4 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -7,7 +7,8 @@ import qs.Components import qs.Modules import qs.Config import qs.Helpers -import qs.Modules.UPower +import qs.Modules.SysTray +import qs.Modules.SysTray.Widgets import qs.Modules.Network import qs.Modules.Updates @@ -31,6 +32,24 @@ RowLayout { if (visibilities.sidebar || visibilities.dashboard || visibilities.resources || visibilities.settings) return; + if (ch.id === "tray") { + const tray = ch.item; + const localPos = tray.mapFromItem(root, x, height / 2); + const sub = tray.getHoveredSubItem(localPos.x, localPos.y); + if (sub) { + popouts.currentName = sub.id; + popouts.currentCenter = Qt.binding(() => { + const centerX = sub.item.mapToItem(root, sub.item.width / 2, 0).x; + return centerX; + }); + popouts.hasCurrent = true; + return; + } + + if (!popouts.currentName.startsWith("traymenu")) + popouts.hasCurrent = false; + } + const id = ch.id; const top = ch.x; const item = ch.item; @@ -93,15 +112,6 @@ RowLayout { } } - DelegateChoice { - roleValue: "audio" - - delegate: WrappedLoader { - sourceComponent: AudioWidget { - } - } - } - DelegateChoice { roleValue: "tray" @@ -165,15 +175,6 @@ RowLayout { } } - DelegateChoice { - roleValue: "upower" - - delegate: WrappedLoader { - sourceComponent: UPowerWidget { - } - } - } - DelegateChoice { roleValue: "network" diff --git a/Modules/Bar/BarLoader.qml b/Modules/Bar/BarLoader.qml index 0e4863f..6c16698 100644 --- a/Modules/Bar/BarLoader.qml +++ b/Modules/Bar/BarLoader.qml @@ -8,7 +8,8 @@ import qs.Components import qs.Modules import qs.Config import qs.Helpers -import qs.Modules.UPower +import qs.Modules.SysTray +import qs.Modules.SysTray.Widgets import qs.Modules.Network Item { diff --git a/Modules/Content.qml b/Modules/Content.qml index 0040b7a..f52280b 100644 --- a/Modules/Content.qml +++ b/Modules/Content.qml @@ -7,7 +7,7 @@ import qs.Config import qs.Components import qs.Modules.WSOverview import qs.Modules.Network -import qs.Modules.UPower +import qs.Modules.SysTray.Popouts import qs.Modules.Updates Item { diff --git a/Modules/Resource.qml b/Modules/Resource.qml index f927712..e741ac5 100644 --- a/Modules/Resource.qml +++ b/Modules/Resource.qml @@ -21,6 +21,7 @@ RowLayout { property int warningThreshold: 80 percentage: 0 + spacing: Appearance.spacing.smaller Behavior on animatedPercentage { Anim { @@ -38,6 +39,7 @@ RowLayout { MaterialIcon { id: icon + Layout.preferredWidth: 16 color: root.iconColor font.pointSize: Appearance.font.size.larger text: root.icon @@ -57,6 +59,11 @@ RowLayout { anchors.right: parent.right color: root.mainColor implicitHeight: Math.ceil(root.percentage * parent.height) + + Behavior on implicitHeight { + Anim { + } + } } } } diff --git a/Modules/Settings/Categories/Services.qml b/Modules/Settings/Categories/Services.qml index 3d34fea..d730c74 100644 --- a/Modules/Settings/Categories/Services.qml +++ b/Modules/Settings/Categories/Services.qml @@ -18,6 +18,15 @@ SettingsPage { Separator { } + SettingSwitch { + name: "Check for updates" + object: Config.services + setting: "updates" + } + + Separator { + } + SettingSwitch { name: "Use Fahrenheit" object: Config.services @@ -60,9 +69,9 @@ SettingsPage { } SettingSpinBox { - name: "Audio increment" max: 1 min: 0 + name: "Audio increment" object: Config.services setting: "audioIncrement" step: 0.05 @@ -72,9 +81,9 @@ SettingsPage { } SettingSpinBox { - name: "Brightness increment" max: 1 min: 0 + name: "Brightness increment" object: Config.services setting: "brightnessIncrement" step: 0.05 @@ -84,9 +93,9 @@ SettingsPage { } SettingSpinBox { - name: "Max volume" max: 5 min: 0 + name: "Max volume" object: Config.services setting: "maxVolume" step: 0.05 @@ -105,8 +114,8 @@ SettingsPage { } SettingSpinBox { - name: "Visualizer bars" min: 1 + name: "Visualizer bars" object: Config.services setting: "visualizerBars" step: 1 diff --git a/Modules/AudioPopup.qml b/Modules/SysTray/Popouts/AudioPopup.qml similarity index 100% rename from Modules/AudioPopup.qml rename to Modules/SysTray/Popouts/AudioPopup.qml diff --git a/Modules/TrayMenuPopout.qml b/Modules/SysTray/Popouts/TrayMenuPopout.qml similarity index 100% rename from Modules/TrayMenuPopout.qml rename to Modules/SysTray/Popouts/TrayMenuPopout.qml diff --git a/Modules/UPower/UPowerPopout.qml b/Modules/SysTray/Popouts/UPowerPopout.qml similarity index 100% rename from Modules/UPower/UPowerPopout.qml rename to Modules/SysTray/Popouts/UPowerPopout.qml diff --git a/Modules/TrayItem.qml b/Modules/SysTray/TrayItem.qml similarity index 100% rename from Modules/TrayItem.qml rename to Modules/SysTray/TrayItem.qml diff --git a/Modules/SysTray/TrayWidget.qml b/Modules/SysTray/TrayWidget.qml new file mode 100644 index 0000000..c368a26 --- /dev/null +++ b/Modules/SysTray/TrayWidget.qml @@ -0,0 +1,122 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.SystemTray +import qs.Components +import qs.Config +import qs.Modules.SysTray.Widgets +import qs.Modules.SysTray.Popouts +import qs.Modules + +RowLayout { + id: root + + readonly property alias items: repeater + required property RowLayout loader + required property Wrapper popouts + + function getHoveredSubItem(localX, localY) { + let modPos = mapToItem(sysTrayMod, localX, localY); + if (sysTrayMod.contains(Qt.point(modPos.x, modPos.y))) { + let modRowPos = sysTrayMod.mapToItem(sysModRow, modPos.x, modPos.y); + let child = sysModRow.childAt(modRowPos.x, modRowPos.y); + if (child) { + if (child.objectName === "audioWidget") + return { + id: "audio", + item: child + }; + if (child.objectName === "upowerWidget") + return { + id: "upower", + item: child + }; + } + } + + let trayPos = mapToItem(sysTray, localX, localY); + if (sysTray.contains(Qt.point(trayPos.x, trayPos.y))) { + let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y); + let child = sysRow.childAt(trayRowPos.x, trayRowPos.y); + if (child && child.hasOwnProperty("popoutId")) { + return { + id: child.popoutId, + item: child + }; + } + } + + return null; + } + + height: Config.barConfig.height + Appearance.padding.smallest * 2 + spacing: Appearance.padding.small + width: sysTray.implicitWidth + sysTrayMod.implicitWidth + Appearance.padding.small + + CustomClippingRect { + id: sysTray + + Layout.fillHeight: true + bottomRightRadius: Appearance.rounding.smallest / 2 + color: DynamicColors.tPalette.m3surfaceContainer + implicitWidth: sysRow.width + Appearance.padding.small * 2 + radius: Appearance.rounding.full + topRightRadius: Appearance.rounding.smallest / 2 + + Row { + id: sysRow + + anchors.centerIn: parent + spacing: 0 + + Repeater { + id: repeater + + model: SystemTray.items + + TrayItem { + id: trayItem + + required property int index + required property SystemTrayItem modelData + + implicitHeight: 34 + implicitWidth: 34 + ind: index + item: modelData + loader: root.loader + popouts: root.popouts + } + } + } + } + + CustomClippingRect { + id: sysTrayMod + + Layout.fillHeight: true + bottomLeftRadius: Appearance.rounding.smallest / 2 + color: DynamicColors.tPalette.m3surfaceContainer + implicitWidth: sysModRow.width + Appearance.padding.smaller * 2 + radius: Appearance.rounding.full + topLeftRadius: Appearance.rounding.smallest / 2 + + Row { + id: sysModRow + + anchors.centerIn: parent + spacing: Appearance.padding.small + + AudioWidget { + objectName: "audioWidget" + } + + UPowerWidget { + height: parent.height + objectName: "upowerWidget" + } + } + } +} diff --git a/Modules/SysTray/Widgets/AudioWidget.qml b/Modules/SysTray/Widgets/AudioWidget.qml new file mode 100644 index 0000000..c124acd --- /dev/null +++ b/Modules/SysTray/Widgets/AudioWidget.qml @@ -0,0 +1,33 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Io +import Quickshell.Services.Pipewire +import qs.Daemons +import qs.Modules +import qs.Config +import qs.Components + +RowLayout { + id: root + + property color barColor: DynamicColors.palette.m3primary + property color textColor: DynamicColors.palette.m3onSurface + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + animate: true + color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor + fill: 1 + font.pointSize: Appearance.font.size.larger + text: Audio.sourceMuted ? "mic_off" : "mic" + } + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + animate: true + color: Audio.muted ? DynamicColors.palette.m3error : root.textColor + fill: 1 + font.pointSize: Appearance.font.size.larger + text: Audio.muted ? "volume_off" : "volume_up" + } +} diff --git a/Modules/SysTray/Widgets/UPowerWidget.qml b/Modules/SysTray/Widgets/UPowerWidget.qml new file mode 100644 index 0000000..e7810e5 --- /dev/null +++ b/Modules/SysTray/Widgets/UPowerWidget.qml @@ -0,0 +1,35 @@ +import Quickshell.Services.UPower +import QtQuick +import QtQuick.Layouts +import qs.Components +import qs.Config +import qs.Helpers as Helpers + +RowLayout { + id: root + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + animate: true + color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3error + fill: 1 + text: { + if (!Helpers.UPower.displayDevice.isLaptopBattery) { + if (PowerProfiles.profile === PowerProfile.PowerSaver) + return "nest_eco_leaf"; + if (PowerProfiles.profile === PowerProfile.Performance) + return "bolt"; + return "power_settings_new"; + } + + const perc = Helpers.UPower.displayDevice.percentage; + const charging = [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(Helpers.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`; + } + } +} diff --git a/Modules/TrayWidget.qml b/Modules/TrayWidget.qml deleted file mode 100644 index 9f1cb5b..0000000 --- a/Modules/TrayWidget.qml +++ /dev/null @@ -1,48 +0,0 @@ -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Layouts -import Quickshell -import Quickshell.Services.SystemTray -import qs.Components -import qs.Config - -CustomClippingRect { - id: root - - readonly property alias items: repeater - required property RowLayout loader - required property Wrapper popouts - - color: DynamicColors.tPalette.m3surfaceContainer - implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 - implicitWidth: row.width + Appearance.padding.small * 2 - radius: height / 2 - - Row { - id: row - - anchors.centerIn: parent - spacing: 0 - - Repeater { - id: repeater - - model: SystemTray.items - - TrayItem { - id: trayItem - - required property int index - required property SystemTrayItem modelData - - implicitHeight: 34 - implicitWidth: 34 - ind: index - item: modelData - loader: root.loader - popouts: root.popouts - } - } - } -} diff --git a/Modules/UPower/UPowerWidget.qml b/Modules/UPower/UPowerWidget.qml deleted file mode 100644 index d93a956..0000000 --- a/Modules/UPower/UPowerWidget.qml +++ /dev/null @@ -1,51 +0,0 @@ -import Quickshell.Services.UPower -import QtQuick -import QtQuick.Layouts -import qs.Components -import qs.Config -import qs.Helpers as Helpers - -CustomRect { - id: root - - color: DynamicColors.tPalette.m3surfaceContainer - implicitHeight: Config.barConfig.height + Appearance.padding.smallest * 2 - implicitWidth: layout.implicitWidth + Appearance.padding.normal * 2 - radius: Appearance.rounding.full - - RowLayout { - id: layout - - anchors.centerIn: parent - - MaterialIcon { - Layout.alignment: Qt.AlignVCenter - animate: true - color: !Helpers.UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3error - fill: 1 - text: { - if (!Helpers.UPower.displayDevice.isLaptopBattery) { - if (PowerProfiles.profile === PowerProfile.PowerSaver) - return "nest_eco_leaf"; - if (PowerProfiles.profile === PowerProfile.Performance) - return "bolt"; - return "power_settings_new"; - } - - const perc = Helpers.UPower.displayDevice.percentage; - const charging = [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(Helpers.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`; - } - } - - CustomText { - Layout.alignment: Qt.AlignVCenter - text: Helpers.UPower.displayDevice.isLaptopBattery ? qsTr("%1%").arg(Math.round(UPower.displayDevice.percentage * 100)) : (PowerProfiles.profile === PowerProfile.PowerSaver ? qsTr("Pwr Sav") : PowerProfiles.profile === PowerProfile.Performance ? qsTr("Perf") : qsTr("Bal")) - } - } -} diff --git a/scripts/copy-scheme-wallpaper.sh b/scripts/copy-scheme-wallpaper.sh index 46dde39..62179e7 100755 --- a/scripts/copy-scheme-wallpaper.sh +++ b/scripts/copy-scheme-wallpaper.sh @@ -7,6 +7,38 @@ main() { sudo mkdir -p "/etc/zshell-greeter/images" sudo cp "$WALLPAPER" "/etc/zshell-greeter/images/greeter_bg.png" sudo cp "$SCHEME" "/etc/zshell-greeter/scheme.json" + + while IFS=: read -r username _ uid _ _ home _; do + if [[ -z "$username" || -z "$uid" || -z "$home" ]]; then + continue + fi + + if [[ "$uid" -lt 1000 ]]; then + continue + fi + + face_path="" + for candidate in "$home/.face" "$home/.face.icon"; do + if [[ -f "$candidate" ]]; then + face_path="$candidate" + break + fi + done + + if [[ -n "$face_path" ]]; then + ext="${face_path##*.}" + case "$face_path" in + "$home/.face" | "$home/.face.icon") + ext="" + ;; + esac + if [[ -n "$ext" ]]; then + sudo cp "$face_path" "/etc/zshell-greeter/images/${username}.${ext}" + else + sudo cp "$face_path" "/etc/zshell-greeter/images/${username}.face" + fi + fi + done