diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a45b3e..bc89f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(ENABLE_MODULES "plugin;shell" CACHE STRING "Modules to build/install") +set(ENABLE_MODULES "plugin" CACHE STRING "Modules to build/install") set(INSTALL_LIBDIR "usr/lib/ZShell" CACHE STRING "Library install dir") set(INSTALL_QMLDIR "usr/lib/qt6/qml" CACHE STRING "QML install dir") diff --git a/Config/BarConfig.qml b/Config/BarConfig.qml index 7d83ff2..707ff76 100644 --- a/Config/BarConfig.qml +++ b/Config/BarConfig.qml @@ -40,6 +40,10 @@ JsonObject { id: "upower", enabled: false }, + { + id: "network", + enabled: false + }, { id: "clock", enabled: true @@ -56,5 +60,7 @@ JsonObject { property bool activeWindow: true property bool resources: true property bool clock: true + property bool network: true + property bool upower: true } } diff --git a/Daemons/NotifServer.qml b/Daemons/NotifServer.qml index 0e6a5ff..9b9365e 100644 --- a/Daemons/NotifServer.qml +++ b/Daemons/NotifServer.qml @@ -223,7 +223,7 @@ Singleton { const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0); const cache = `${Paths.notifimagecache}/${hash}.png`; - ZShell.saveItem(this, Qt.resolvedUrl(cache), () => { + ZShellIo.saveItem(this, Qt.resolvedUrl(cache), () => { notif.image = cache; notif.dummyImageLoader.active = false; }); diff --git a/Helpers/Network.qml b/Helpers/Network.qml index f04c9e5..7296f10 100644 --- a/Helpers/Network.qml +++ b/Helpers/Network.qml @@ -6,6 +6,6 @@ import Quickshell.Networking Singleton { id: root - property list devices: Networking.devices + property list devices: Networking.devices.values property NetworkDevice activeDevice: devices.find(d => d.connected) } diff --git a/Modules/Bar/BarLoader.qml b/Modules/Bar/BarLoader.qml index 3d9584d..086b05f 100644 --- a/Modules/Bar/BarLoader.qml +++ b/Modules/Bar/BarLoader.qml @@ -10,6 +10,7 @@ import qs.Helpers import qs.Daemons import qs.Modules.Polkit import qs.Modules.UPower +import qs.Modules.Network RowLayout { id: root @@ -66,6 +67,14 @@ RowLayout { popouts.currentName = "dash"; popouts.currentCenter = root.width / 2; popouts.hasCurrent = true; + } else if ( id === "network" && Config.barConfig.popouts.network ) { + popouts.currentName = "network"; + popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x ); + popouts.hasCurrent = true; + } else if ( id === "upower" && Config.barConfig.popouts.upower ) { + popouts.currentName = "upower"; + popouts.currentCenter = Qt.binding( () => item.mapToItem( root, itemWidth / 2, 0 ).x ); + popouts.hasCurrent = true; } } @@ -167,6 +176,12 @@ RowLayout { sourceComponent: UPowerWidget {} } } + DelegateChoice { + roleValue: "network" + delegate: WrappedLoader { + sourceComponent: NetworkWidget {} + } + } } } diff --git a/Modules/Content.qml b/Modules/Content.qml index 0e9fabb..6f8c06f 100644 --- a/Modules/Content.qml +++ b/Modules/Content.qml @@ -8,6 +8,8 @@ import qs.Modules.Calendar import qs.Modules.WSOverview import qs.Modules.Polkit import qs.Modules.Dashboard +import qs.Modules.Network +import qs.Modules.UPower Item { id: root @@ -97,6 +99,22 @@ Item { wrapper: root.wrapper } } + + Popout { + name: "upower" + + sourceComponent: UPowerPopout { + wrapper: root.wrapper + } + } + + Popout { + name: "network" + + sourceComponent: NetworkPopout { + wrapper: root.wrapper + } + } } component Popout: Loader { diff --git a/Modules/Network/NetworkPopout.qml b/Modules/Network/NetworkPopout.qml index 91f2f8a..e9bcd6a 100644 --- a/Modules/Network/NetworkPopout.qml +++ b/Modules/Network/NetworkPopout.qml @@ -4,21 +4,25 @@ import Quickshell import Quickshell.Networking import QtQuick import QtQuick.Layouts +import qs.Components +import qs.Config import qs.Modules -import qs.Helpers +import qs.Helpers as Helpers Item { id: root required property var wrapper + Component.onCompleted: console.log(Networking.backend.toString()) + ColumnLayout { id: layout spacing: 8 Repeater { - model: Network.devices + model: Helpers.Network.devices CustomRadioButton { id: network @@ -26,8 +30,7 @@ Item { required property NetworkDevice modelData - checked: Network.activeDevice?.name === modelData.name - onClicked: + checked: Helpers.Network.activeDevice?.name === modelData.name text: modelData.description } } diff --git a/Modules/Network/NetworkWidget.qml b/Modules/Network/NetworkWidget.qml new file mode 100644 index 0000000..ac1e378 --- /dev/null +++ b/Modules/Network/NetworkWidget.qml @@ -0,0 +1,25 @@ +import Quickshell +import QtQuick +import QtQuick.Layouts +import qs.Components +import qs.Modules + +Item { + id: root + + anchors.top: parent.top + anchors.bottom: parent.bottom + + implicitWidth: layout.implicitWidth + + RowLayout { + id: layout + anchors.top: parent.top + anchors.bottom: parent.bottom + + MaterialIcon { + text: "android_wifi_4_bar" + Layout.alignment: Qt.AlignVCenter + } + } +} diff --git a/Modules/UPower/UPowerPopout.qml b/Modules/UPower/UPowerPopout.qml new file mode 100644 index 0000000..f92d68e --- /dev/null +++ b/Modules/UPower/UPowerPopout.qml @@ -0,0 +1,179 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell.Services.UPower +import qs.Config +import qs.Components +import qs.Modules + +Item { + id: root + + required property var wrapper + + implicitWidth: profiles.implicitWidth + implicitHeight: profiles.implicitHeight + + CustomRect { + id: profiles + + property string current: { + const p = PowerProfiles.profile; + if (p === PowerProfile.PowerSaver) + return saver.icon; + if (p === PowerProfile.Performance) + return perf.icon; + return balance.icon; + } + + anchors.horizontalCenter: parent.horizontalCenter + + implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth + implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight + + color: DynamicColors.tPalette.m3surfaceContainer + radius: 6 + + CustomRect { + id: indicator + + color: DynamicColors.palette.m3primary + radius: 1000 + state: profiles.current + + states: [ + State { + name: saver.icon + + Fill { + item: saver + } + }, + State { + name: balance.icon + + Fill { + item: balance + } + }, + State { + name: perf.icon + + Fill { + item: perf + } + } + ] + + transitions: Transition { + AnchorAnimation { + duration: MaterialEasing.expressiveEffectsTime + easing.bezierCurve: MaterialEasing.expressiveEffects + easing.type: Easing.BezierSpline + } + } + } + + Profile { + id: saver + + anchors.top: parent.top + anchors.topMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 25 + + text: "Power Saver" + profile: PowerProfile.PowerSaver + icon: "nest_eco_leaf" + } + + CustomText { + id: saverLabel + anchors.top: saver.bottom + anchors.horizontalCenter: saver.horizontalCenter + text: saver.text + } + + Profile { + id: balance + + anchors.top: parent.top + anchors.topMargin: 8 + anchors.horizontalCenter: parent.horizontalCenter + + text: "Balanced" + profile: PowerProfile.Balanced + icon: "power_settings_new" + } + + CustomText { + id: balanceLabel + anchors.top: balance.bottom + anchors.horizontalCenter: balance.horizontalCenter + text: balance.text + } + + Profile { + id: perf + + anchors.top: parent.top + anchors.topMargin: 8 + anchors.right: parent.right + anchors.rightMargin: 25 + + text: "Performance" + profile: PowerProfile.Performance + icon: "bolt" + } + + CustomText { + id: perfLabel + anchors.top: perf.bottom + anchors.horizontalCenter: perf.horizontalCenter + text: perf.text + } + } + + component Fill: AnchorChanges { + required property Item item + + target: indicator + anchors.left: item.left + anchors.right: item.right + anchors.top: item.top + anchors.bottom: item.bottom + } + + component Profile: Item { + required property string icon + required property int profile + required property string text + + implicitWidth: icon.implicitHeight + 5 * 2 + implicitHeight: icon.implicitHeight + 5 * 2 + + StateLayer { + radius: 1000 + color: profiles.current === parent.icon ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface + + function onClicked(): void { + PowerProfiles.profile = parent.profile; + } + } + + MaterialIcon { + id: icon + + anchors.centerIn: parent + + text: parent.icon + font.pointSize: 36 + color: profiles.current === text ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface + fill: profiles.current === text ? 1 : 0 + + Behavior on fill { + Anim {} + } + } + } +} diff --git a/Modules/UPower/UPowerWidget.qml b/Modules/UPower/UPowerWidget.qml index 2f7158e..59ab955 100644 --- a/Modules/UPower/UPowerWidget.qml +++ b/Modules/UPower/UPowerWidget.qml @@ -10,10 +10,18 @@ import qs.Modules Item { id: root - implicitWidth: layout.childrenRect.width + implicitWidth: layout.childrenRect.width + 10 * 2 anchors.top: parent.top anchors.bottom: parent.bottom + CustomRect { + anchors.fill: parent + anchors.topMargin: 3 + anchors.bottomMargin: 3 + color: DynamicColors.tPalette.m3surfaceContainer + radius: 1000 + } + RowLayout { id: layout anchors.centerIn: parent @@ -24,10 +32,10 @@ Item { text: { if (!Helpers.UPower.displayDevice.isLaptopBattery) { if (PowerProfiles.profile === PowerProfile.PowerSaver) - return "energy_savings_leaf"; + return "nest_eco_leaf"; if (PowerProfiles.profile === PowerProfile.Performance) - return "rocket_launch"; - return "balance"; + return "bolt"; + return "power_settings_new"; } const perc = Helpers.UPower.displayDevice.percentage; @@ -45,7 +53,7 @@ Item { CustomText { Layout.alignment: Qt.AlignVCenter - text: Helpers.UPower.displayDevice.isLaptopBattery ? qsTr("%1%").arg(Math.round(UPower.displayDevice.percentage * 100)) : qsTr("NaN") + 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")) } } }