Merge pull request 'test new systray' (#50) from new-systray into main

Reviewed-on: #50
This commit was merged in pull request #50.
This commit is contained in:
2026-04-09 19:26:57 +02:00
23 changed files with 353 additions and 293 deletions
+6 -6
View File
@@ -66,8 +66,8 @@ JsonObject {
} }
component Padding: JsonObject { component Padding: JsonObject {
property int large: 15 * scale property int large: 15 * scale
property int larger: 12 * scale property int larger: 13 * scale
property int normal: 10 * scale property int normal: 9 * scale
property real scale: 1 property real scale: 1
property int small: 5 * scale property int small: 5 * scale
property int smaller: 7 * scale property int smaller: 7 * scale
@@ -75,18 +75,18 @@ JsonObject {
} }
component Rounding: JsonObject { component Rounding: JsonObject {
property int full: 1000 * scale property int full: 1000 * scale
property int large: 25 * scale property int large: 24 * scale
property int normal: 17 * scale property int normal: 18 * scale
property real scale: 1 property real scale: 1
property int small: 12 * scale property int small: 12 * scale
property int smallest: 8 * scale property int smallest: 8 * scale
} }
component Spacing: JsonObject { component Spacing: JsonObject {
property int large: 20 * scale property int large: 20 * scale
property int larger: 15 * scale property int larger: 16 * scale
property int normal: 12 * scale property int normal: 12 * scale
property real scale: 1 property real scale: 1
property int small: 7 * scale property int small: 8 * scale
property int smaller: 10 * scale property int smaller: 10 * scale
} }
component Transparency: JsonObject { component Transparency: JsonObject {
+1 -1
View File
@@ -270,6 +270,7 @@ Singleton {
function serializeServices(): var { function serializeServices(): var {
return { return {
weatherLocation: services.weatherLocation, weatherLocation: services.weatherLocation,
updates: services.updates,
useFahrenheit: services.useFahrenheit, useFahrenheit: services.useFahrenheit,
ddcutilService: services.ddcutilService, ddcutilService: services.ddcutilService,
useTwelveHourClock: services.useTwelveHourClock, useTwelveHourClock: services.useTwelveHourClock,
@@ -322,7 +323,6 @@ Singleton {
ElapsedTimer { ElapsedTimer {
id: timer id: timer
} }
Timer { Timer {
+29 -61
View File
@@ -78,12 +78,29 @@ Singleton {
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1); 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 { function setMode(mode: string): void {
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]); Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", mode]);
Config.general.color.mode = mode; Config.general.color.mode = mode;
Config.save(); Config.save();
} }
Component.onCompleted: debounceTimer.triggered()
Connections {
function onConfigReloaded(): void {
root.reloadHyprRules();
}
target: Hypr
}
FileView { FileView {
path: `${Paths.state}/scheme.json` path: `${Paths.state}/scheme.json`
watchChanges: true watchChanges: true
@@ -92,72 +109,20 @@ Singleton {
onLoaded: root.load(text(), false) onLoaded: root.load(text(), false)
} }
Timer {
id: debounceTimer
interval: 300
onTriggered: root.reloadHyprRules()
}
ImageAnalyser { ImageAnalyser {
id: analyser id: analyser
source: WallpaperPath.currentWallpaperPath 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 { component M3Palette: QtObject {
property color m3background: "#191114" property color m3background: "#191114"
property color m3error: "#ffb4ab" property color m3error: "#ffb4ab"
@@ -279,8 +244,11 @@ Singleton {
readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor) readonly property color m3tertiary_paletteKeyColor: root.layer(root.palette.m3tertiary_paletteKeyColor)
} }
component Transparency: QtObject { 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 bool enabled: Appearance.transparency.enabled
readonly property real layers: Appearance.transparency.layers readonly property real layers: Appearance.transparency.layers
onBaseChanged: debounceTimer.restart()
onEnabledChanged: debounceTimer.restart()
} }
} }
+1
View File
@@ -14,6 +14,7 @@ JsonObject {
"to": "YT Music" "to": "YT Music"
} }
] ]
property bool updates: true
property bool useFahrenheit: false property bool useFahrenheit: false
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a") property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property int visualizerBars: 30 property int visualizerBars: 30
+2 -1
View File
@@ -67,6 +67,7 @@ ColumnLayout {
anchors.fill: parent anchors.fill: parent
clip: true clip: true
currentIndex: root.greeter.selectedUser ? root.greeter.users.indexOf(root.greeter.selectedUser) : -1
highlightFollowsCurrentItem: false highlightFollowsCurrentItem: false
model: root.greeter.users model: root.greeter.users
spacing: Appearance.spacing.small spacing: Appearance.spacing.small
@@ -84,7 +85,7 @@ ColumnLayout {
StateLayer { StateLayer {
function onClicked(): void { function onClicked(): void {
users.currentIndex = index; root.greeter.selectUser(modelData.username);
} }
} }
+39
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import glob
import json import json
import os import os
import pwd import pwd
@@ -17,21 +18,59 @@ INVALID_SHELLS = {
# Minimum UID for regular users (typically 1000 on most Linux distributions) # Minimum UID for regular users (typically 1000 on most Linux distributions)
MIN_UID = 1000 MIN_UID = 1000
GREETER_IMAGES_DIR = "/etc/zshell-greeter/images"
def get_face_path(home: str, username: str) -> str: def get_face_path(home: str, username: str) -> str:
"""Get the path to the user's face/avatar image if it exists.""" """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 # Check common locations for user avatars
candidates = [ candidates = [
os.path.join(home, ".face"), os.path.join(home, ".face"),
os.path.join(home, ".face.icon"), os.path.join(home, ".face.icon"),
f"/var/lib/AccountsService/icons/{username}", f"/var/lib/AccountsService/icons/{username}",
f"/usr/share/pixmaps/faces/{username}",
] ]
for path in candidates: for path in candidates:
if os.path.isfile(path): if os.path.isfile(path):
return path return path
for path in sorted(glob.glob(f"/var/lib/AccountsService/icons/{username}.*")):
if os.path.isfile(path):
return path
return "" return ""
+10 -4
View File
@@ -5,6 +5,7 @@ import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Paths import qs.Paths
import qs.Config
Singleton { Singleton {
id: root id: root
@@ -44,13 +45,14 @@ Singleton {
Timer { Timer {
interval: 1 interval: 1
repeat: true repeat: true
running: true running: Config.services.updates
onTriggered: { onTriggered: {
if (!root.loaded || !root.commandReady) if (!root.loaded || !root.commandReady)
return; return;
updatesProc.running = true; if (Config.services.updates)
updatesProc.running = true;
interval = 5000; interval = 5000;
} }
} }
@@ -66,7 +68,7 @@ Singleton {
Process { Process {
id: cmdDetect 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 running: true
stdout: StdioCollector { stdout: StdioCollector {
@@ -80,7 +82,11 @@ Singleton {
helper = "pacman"; helper = "pacman";
} }
updatesProc.command = [helper, "-Qu"]; if (helper === "checkupdates") {
updatesProc.command = [helper];
} else {
updatesProc.command = [helper, "-Qu"];
}
root.commandReady = true; root.commandReady = true;
} }
} }
-96
View File
@@ -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
}
}
}
}
}
+20 -19
View File
@@ -7,7 +7,8 @@ import qs.Components
import qs.Modules import qs.Modules
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
import qs.Modules.UPower import qs.Modules.SysTray
import qs.Modules.SysTray.Widgets
import qs.Modules.Network import qs.Modules.Network
import qs.Modules.Updates import qs.Modules.Updates
@@ -31,6 +32,24 @@ RowLayout {
if (visibilities.sidebar || visibilities.dashboard || visibilities.resources || visibilities.settings) if (visibilities.sidebar || visibilities.dashboard || visibilities.resources || visibilities.settings)
return; 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 id = ch.id;
const top = ch.x; const top = ch.x;
const item = ch.item; const item = ch.item;
@@ -93,15 +112,6 @@ RowLayout {
} }
} }
DelegateChoice {
roleValue: "audio"
delegate: WrappedLoader {
sourceComponent: AudioWidget {
}
}
}
DelegateChoice { DelegateChoice {
roleValue: "tray" roleValue: "tray"
@@ -165,15 +175,6 @@ RowLayout {
} }
} }
DelegateChoice {
roleValue: "upower"
delegate: WrappedLoader {
sourceComponent: UPowerWidget {
}
}
}
DelegateChoice { DelegateChoice {
roleValue: "network" roleValue: "network"
+2 -1
View File
@@ -8,7 +8,8 @@ import qs.Components
import qs.Modules import qs.Modules
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
import qs.Modules.UPower import qs.Modules.SysTray
import qs.Modules.SysTray.Widgets
import qs.Modules.Network import qs.Modules.Network
Item { Item {
+1 -1
View File
@@ -7,7 +7,7 @@ import qs.Config
import qs.Components import qs.Components
import qs.Modules.WSOverview import qs.Modules.WSOverview
import qs.Modules.Network import qs.Modules.Network
import qs.Modules.UPower import qs.Modules.SysTray.Popouts
import qs.Modules.Updates import qs.Modules.Updates
Item { Item {
+7
View File
@@ -21,6 +21,7 @@ RowLayout {
property int warningThreshold: 80 property int warningThreshold: 80
percentage: 0 percentage: 0
spacing: Appearance.spacing.smaller
Behavior on animatedPercentage { Behavior on animatedPercentage {
Anim { Anim {
@@ -38,6 +39,7 @@ RowLayout {
MaterialIcon { MaterialIcon {
id: icon id: icon
Layout.preferredWidth: 16
color: root.iconColor color: root.iconColor
font.pointSize: Appearance.font.size.larger font.pointSize: Appearance.font.size.larger
text: root.icon text: root.icon
@@ -57,6 +59,11 @@ RowLayout {
anchors.right: parent.right anchors.right: parent.right
color: root.mainColor color: root.mainColor
implicitHeight: Math.ceil(root.percentage * parent.height) implicitHeight: Math.ceil(root.percentage * parent.height)
Behavior on implicitHeight {
Anim {
}
}
} }
} }
} }
+13 -4
View File
@@ -18,6 +18,15 @@ SettingsPage {
Separator { Separator {
} }
SettingSwitch {
name: "Check for updates"
object: Config.services
setting: "updates"
}
Separator {
}
SettingSwitch { SettingSwitch {
name: "Use Fahrenheit" name: "Use Fahrenheit"
object: Config.services object: Config.services
@@ -60,9 +69,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Audio increment"
max: 1 max: 1
min: 0 min: 0
name: "Audio increment"
object: Config.services object: Config.services
setting: "audioIncrement" setting: "audioIncrement"
step: 0.05 step: 0.05
@@ -72,9 +81,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Brightness increment"
max: 1 max: 1
min: 0 min: 0
name: "Brightness increment"
object: Config.services object: Config.services
setting: "brightnessIncrement" setting: "brightnessIncrement"
step: 0.05 step: 0.05
@@ -84,9 +93,9 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Max volume"
max: 5 max: 5
min: 0 min: 0
name: "Max volume"
object: Config.services object: Config.services
setting: "maxVolume" setting: "maxVolume"
step: 0.05 step: 0.05
@@ -105,8 +114,8 @@ SettingsPage {
} }
SettingSpinBox { SettingSpinBox {
name: "Visualizer bars"
min: 1 min: 1
name: "Visualizer bars"
object: Config.services object: Config.services
setting: "visualizerBars" setting: "visualizerBars"
step: 1 step: 1
+122
View File
@@ -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"
}
}
}
}
+33
View File
@@ -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"
}
}
+35
View File
@@ -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`;
}
}
}
-48
View File
@@ -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
}
}
}
}
-51
View File
@@ -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"))
}
}
}
+32
View File
@@ -7,6 +7,38 @@ main() {
sudo mkdir -p "/etc/zshell-greeter/images" sudo mkdir -p "/etc/zshell-greeter/images"
sudo cp "$WALLPAPER" "/etc/zshell-greeter/images/greeter_bg.png" sudo cp "$WALLPAPER" "/etc/zshell-greeter/images/greeter_bg.png"
sudo cp "$SCHEME" "/etc/zshell-greeter/scheme.json" 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 </etc/passwd
} }
main "$@" main "$@"