diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 63b4692..098aa1d 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -82,6 +82,22 @@ Scope { anchors.left: parent.left } + Row { + id: leftWidgetsRow + anchors.verticalCenter: barBackground.verticalCenter + anchors.left: barBackground.left + anchors.leftMargin: 18 + spacing: 12 + + SystemInfo { + anchors.verticalCenter: parent.verticalCenter + } + + Media { + anchors.verticalCenter: parent.verticalCenter + } + } + ActiveWindow {} Workspace { diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 301a06a..b772331 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -153,13 +153,6 @@ PanelWindow { anchors.margins: 32 spacing: 18 - Rectangle { - Layout.fillWidth: true - height: 1.5 - color: Theme.outline - opacity: 0.10 - } - // Search Bar Rectangle { id: searchBar @@ -372,7 +365,7 @@ PanelWindow { size: 1.1 fillColor: Theme.backgroundPrimary anchors.top: root.top - offsetX: 397 + offsetX: 396 offsetY: 0 } @@ -382,7 +375,7 @@ PanelWindow { size: 1.1 fillColor: Theme.backgroundPrimary anchors.top: root.top - offsetX: -397 + offsetX: -396 offsetY: 0 } } \ No newline at end of file diff --git a/Bar/Modules/Media.qml b/Bar/Modules/Media.qml new file mode 100644 index 0000000..cc8dd41 --- /dev/null +++ b/Bar/Modules/Media.qml @@ -0,0 +1,125 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import Qt5Compat.GraphicalEffects +import qs.Settings +import qs.Services +import qs.Components + +Item { + id: mediaControl + width: visible ? mediaRow.width : 0 + height: 32 + visible: Settings.showMediaInBar && MusicManager.currentPlayer + + RowLayout { + id: mediaRow + height: parent.height + spacing: 8 + + Item { + id: albumArtContainer + width: 24 + height: 24 + Layout.alignment: Qt.AlignVCenter + + // Circular spectrum visualizer + CircularSpectrum { + id: spectrum + values: MusicManager.cavaValues + anchors.centerIn: parent + innerRadius: 10 + outerRadius: 18 + fillColor: Theme.accentPrimary + strokeColor: Theme.accentPrimary + strokeWidth: 0 + z: 0 + } + + // Album art image + Rectangle { + id: albumArtwork + width: 20 + height: 20 + anchors.centerIn: parent + radius: 12 // circle + color: Qt.darker(Theme.surface, 1.1) + border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) + border.width: 1 + z: 1 + + Image { + id: albumArt + anchors.fill: parent + anchors.margins: 1 + fillMode: Image.PreserveAspectCrop + smooth: true + cache: false + asynchronous: true + sourceSize.width: 24 + sourceSize.height: 24 + source: MusicManager.trackArtUrl + visible: source.toString() !== "" + + // Rounded corners using layer + layer.enabled: true + layer.effect: OpacityMask { + cached: true + maskSource: Rectangle { + width: albumArt.width + height: albumArt.height + radius: albumArt.width / 2 // circle + visible: false + } + } + } + + // Fallback icon + Text { + anchors.centerIn: parent + text: "music_note" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.4) + visible: !albumArt.visible + } + + // Play/Pause overlay (only visible on hover) + Rectangle { + anchors.fill: parent + radius: parent.radius + color: Qt.rgba(0, 0, 0, 0.5) + visible: playButton.containsMouse + z: 2 + + Text { + anchors.centerIn: parent + text: MusicManager.isPlaying ? "pause" : "play_arrow" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: "white" + } + } + + MouseArea { + id: playButton + anchors.fill: parent + hoverEnabled: true + enabled: MusicManager.canPlay || MusicManager.canPause + onClicked: MusicManager.playPause() + } + } + } + + // Track info + Text { + text: MusicManager.trackTitle + " - " + MusicManager.trackArtist + color: Theme.textPrimary + font.family: Theme.fontFamily + font.pixelSize: 12 + elide: Text.ElideRight + Layout.maximumWidth: 300 + Layout.alignment: Qt.AlignVCenter + } + } +} diff --git a/Bar/Modules/SystemInfo.qml b/Bar/Modules/SystemInfo.qml new file mode 100644 index 0000000..48a99b1 --- /dev/null +++ b/Bar/Modules/SystemInfo.qml @@ -0,0 +1,81 @@ +import QtQuick +import Quickshell +import qs.Settings +import qs.Services + +Row { + id: layout + spacing: 10 + visible: Settings.showSystemInfoInBar + + Row { + id: cpuUsageLayout + spacing: 6 + + Text { + id: cpuUsageIcon + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeBody + text: "speed" + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + color: Theme.accentPrimary + } + + Text { + id: cpuUsageText + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeSmall + color: Theme.textPrimary + text: Sysinfo.cpuUsageStr + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } + + // CPU Temperature Component + Row { + id: cpuTempLayout + spacing: 3 + Text { + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeBody + text: "thermometer" + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + color: Theme.accentPrimary + } + + Text { + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeSmall + color: Theme.textPrimary + text: Sysinfo.cpuTempStr + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } + + // Memory Usage Component + Row { + id: memoryUsageLayout + spacing: 3 + Text { + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeBody + text: "memory" + color: Theme.accentPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + } + + Text { + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeSmall + color: Theme.textPrimary + text: Sysinfo.memoryUsageStr + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } +} diff --git a/Bar/Modules/Volume.qml b/Bar/Modules/Volume.qml index ad93fe7..7e342a5 100644 --- a/Bar/Modules/Volume.qml +++ b/Bar/Modules/Volume.qml @@ -47,13 +47,13 @@ Item { hoverEnabled: true acceptedButtons: Qt.NoButton // Accept wheel events only propagateComposedEvents: true - onWheel: { + onWheel:(wheel) => { if (!shell) return; let step = 5; if (wheel.angleDelta.y > 0) { - shell.volume = Math.min(100, shell.volume + step); + shell.updateVolume(Math.min(100, shell.volume + step)); } else if (wheel.angleDelta.y < 0) { - shell.volume = Math.max(0, shell.volume - step); + shell.updateVolume(Math.max(0, shell.volume - step)); } } } diff --git a/Components/Cava.qml b/Components/Cava.qml index 0e9f7d9..679ceef 100644 --- a/Components/Cava.qml +++ b/Components/Cava.qml @@ -2,6 +2,7 @@ import QtQuick import Quickshell import Quickshell.Io import qs.Components +import qs.Services Scope { id: root @@ -21,15 +22,11 @@ Scope { }) property var values: Array(count).fill(0) // 0 <= value <= 1 - onConfigChanged: { - process.running = false - process.running = true - } - Process { property int index: 0 id: process stdinEnabled: true + running: MusicManager.isPlaying command: ["cava", "-p", "/dev/stdin"] onExited: { stdinEnabled = true; index = 0 } onStarted: { diff --git a/Components/CircularProgressBar.qml b/Components/CircularProgressBar.qml index 7950bc0..075f201 100644 --- a/Components/CircularProgressBar.qml +++ b/Components/CircularProgressBar.qml @@ -12,7 +12,8 @@ Rectangle { property color progressColor: Theme.accentPrimary property int strokeWidth: 6 property bool showText: true - property string text: Math.round(progress * 100) + "%" + property string units: "%" + property string text: Math.round(progress * 100) + units property int textSize: 10 property color textColor: Theme.textPrimary diff --git a/Components/CircularSpectrum.qml b/Components/CircularSpectrum.qml index 4e36168..04ebf56 100644 --- a/Components/CircularSpectrum.qml +++ b/Components/CircularSpectrum.qml @@ -5,26 +5,20 @@ Item { id: root property int innerRadius: 34 property int outerRadius: 48 - property int barCount: 40 property color fillColor: "#fff" property color strokeColor: "#fff" property int strokeWidth: 0 + property var values: [] width: outerRadius * 2 height: outerRadius * 2 - // Cava input - Cava { - id: cava - count: root.barCount - } - Repeater { - model: root.barCount + model: root.values.length Rectangle { - property real value: cava.values[index] - property real angle: (index / root.barCount) * 360 - width: Math.max(2, (root.innerRadius * 2 * Math.PI) / root.barCount - 4) + property real value: root.values[index] + property real angle: (index / root.values.length) * 360 + width: Math.max(2, (root.innerRadius * 2 * Math.PI) / root.values.length - 4) height: value * (root.outerRadius - root.innerRadius) radius: width / 2 color: root.fillColor @@ -32,8 +26,8 @@ Item { border.width: root.strokeWidth antialiasing: true - x: root.width / 2 + (root.innerRadius) * Math.cos(Math.PI/2 + 2 * Math.PI * index / root.barCount) - width / 2 - y: root.height / 2 - (root.innerRadius) * Math.sin(Math.PI/2 + 2 * Math.PI * index / root.barCount) - height + x: root.width / 2 + (root.innerRadius) * Math.cos(Math.PI/2 + 2 * Math.PI * index / root.values.length) - width / 2 + y: root.height / 2 - (root.innerRadius) * Math.sin(Math.PI/2 + 2 * Math.PI * index / root.values.length) - height transform: Rotation { origin.x: width / 2 diff --git a/Programs/zigstat b/Programs/zigstat new file mode 100755 index 0000000..4130452 Binary files /dev/null and b/Programs/zigstat differ diff --git a/README.md b/README.md index 290d03f..c6d886f 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ You will need to install a few things to get everything working: - `swww` to add fancy wallpaper animations (optional) - `wallust` to theme the setup based on wallpaper (optional) +zigstat is bundled - source can be found [here](https://git.pika-os.com/wm-packages/pikabar/src/branch/main/src/zigstat). --- ## Known issues diff --git a/Services/MusicManager.qml b/Services/MusicManager.qml new file mode 100644 index 0000000..27f0226 --- /dev/null +++ b/Services/MusicManager.qml @@ -0,0 +1,157 @@ +pragma Singleton +import QtQuick +import Quickshell +import Quickshell.Services.Mpris +import qs.Settings +import qs.Components + +Singleton { + id: manager + + // Properties + property var currentPlayer: null + property real currentPosition: 0 + property int selectedPlayerIndex: 0 + property bool isPlaying: currentPlayer ? currentPlayer.isPlaying : false + property string trackTitle: currentPlayer ? (currentPlayer.trackTitle || "Unknown Track") : "" + property string trackArtist: currentPlayer ? (currentPlayer.trackArtist || "Unknown Artist") : "" + property string trackAlbum: currentPlayer ? (currentPlayer.trackAlbum || "Unknown Album") : "" + property string trackArtUrl: currentPlayer ? (currentPlayer.trackArtUrl || "") : "" + property real trackLength: currentPlayer ? currentPlayer.length : 0 + property bool canPlay: currentPlayer ? currentPlayer.canPlay : false + property bool canPause: currentPlayer ? currentPlayer.canPause : false + property bool canGoNext: currentPlayer ? currentPlayer.canGoNext : false + property bool canGoPrevious: currentPlayer ? currentPlayer.canGoPrevious : false + property bool canSeek: currentPlayer ? currentPlayer.canSeek : false + property bool hasPlayer: getAvailablePlayers().length > 0 + + // Initialize + Item { + Component.onCompleted: { + updateCurrentPlayer() + } + } + + // Returns available MPRIS players + function getAvailablePlayers() { + if (!Mpris.players || !Mpris.players.values) { + return [] + } + + let allPlayers = Mpris.players.values + let controllablePlayers = [] + + for (let i = 0; i < allPlayers.length; i++) { + let player = allPlayers[i] + if (player && player.canControl) { + controllablePlayers.push(player) + } + } + + return controllablePlayers + } + + // Returns active player or first available + function findActivePlayer() { + let availablePlayers = getAvailablePlayers() + if (availablePlayers.length === 0) { + return null + } + + // Use selected player if valid, otherwise use first available + if (selectedPlayerIndex < availablePlayers.length) { + return availablePlayers[selectedPlayerIndex] + } else { + selectedPlayerIndex = 0 + return availablePlayers[0] + } + } + + // Updates currentPlayer and currentPosition + function updateCurrentPlayer() { + let newPlayer = findActivePlayer() + if (newPlayer !== currentPlayer) { + currentPlayer = newPlayer + currentPosition = currentPlayer ? currentPlayer.position : 0 + } + } + + // Player control functions + function playPause() { + if (currentPlayer) { + if (currentPlayer.isPlaying) { + currentPlayer.pause() + } else { + currentPlayer.play() + } + } + } + + function play() { + if (currentPlayer && currentPlayer.canPlay) { + currentPlayer.play() + } + } + + function pause() { + if (currentPlayer && currentPlayer.canPause) { + currentPlayer.pause() + } + } + + function next() { + if (currentPlayer && currentPlayer.canGoNext) { + currentPlayer.next() + } + } + + function previous() { + if (currentPlayer && currentPlayer.canGoPrevious) { + currentPlayer.previous() + } + } + + function seek(position) { + if (currentPlayer && currentPlayer.canSeek) { + currentPlayer.position = position + currentPosition = position + } + } + + function seekByRatio(ratio) { + if (currentPlayer && currentPlayer.canSeek && currentPlayer.length > 0) { + let seekPosition = ratio * currentPlayer.length + currentPlayer.position = seekPosition + currentPosition = seekPosition + } + } + + // Updates progress bar every second + Timer { + id: positionTimer + interval: 1000 + running: currentPlayer && currentPlayer.isPlaying && currentPlayer.length > 0 + repeat: true + onTriggered: { + if (currentPlayer && currentPlayer.isPlaying) { + currentPosition = currentPlayer.position + } + } + } + + // Reacts to player list changes + Connections { + target: Mpris.players + function onValuesChanged() { + updateCurrentPlayer() + } + } + + Cava { + id: cava + count: 44 + } + + // Expose cava values + property alias cavaValues: cava.values +} diff --git a/Services/Sysinfo.qml b/Services/Sysinfo.qml new file mode 100644 index 0000000..9a168c3 --- /dev/null +++ b/Services/Sysinfo.qml @@ -0,0 +1,47 @@ +pragma Singleton +import QtQuick +import Qt.labs.folderlistmodel +import Quickshell +import Quickshell.Io +import qs.Settings + +Singleton { + id: manager + + property string updateInterval: "2s" + property string cpuUsageStr: "" + property string cpuTempStr: "" + property string memoryUsageStr: "" + property string memoryUsagePerStr: "" + property real cpuUsage: 0 + property real memoryUsage: 0 + property real cpuTemp: 0 + property real diskUsage: 0 + property real memoryUsagePer: 0 + property string diskUsageStr: "" + + Process { + id: zigstatProcess + running: true + command: [Quickshell.shellRoot + "/Programs/zigstat", updateInterval] + stdout: SplitParser { + onRead: function (line) { + try { + const data = JSON.parse(line); + cpuUsage = +data.cpu; + cpuTemp = +data.cputemp; + memoryUsage = +data.mem; + memoryUsagePer = +data.memper; + diskUsage = +data.diskper; + cpuUsageStr = data.cpu + "%"; + cpuTempStr = data.cputemp + "°C"; + memoryUsageStr = data.mem + "G"; + memoryUsagePerStr = data.memper + "%"; + diskUsageStr = data.diskper + "%"; + } catch (e) { + console.error("Failed to parse zigstat output:", e); + } + } + } + } +} \ No newline at end of file diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 6ff0e62..e78f201 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -14,7 +14,7 @@ QtObject { } property string weatherCity: "Dinslaken" property string profileImage: "/home/" + Quickshell.env("USER") + "/.face" - property bool useFahrenheit + property bool useFahrenheit: false property string wallpaperFolder: "/usr/share/wallpapers" property string currentWallpaper: "" property string videoPath: "~/Videos/" @@ -22,6 +22,8 @@ QtObject { property bool useSWWW: false property bool randomWallpaper: false property bool useWallpaperTheme: false + property bool showSystemInfoInBar: true + property bool showMediaInBar: false property int wallpaperInterval: 300 property string wallpaperResize: "crop" property int transitionFps: 60 @@ -43,6 +45,10 @@ QtObject { videoPath = settings.value("videoPath", videoPath) let showActiveWindowIconFlag = settings.value("showActiveWindowIconFlag", "false") showActiveWindowIcon = showActiveWindowIconFlag === "true" + let showSystemInfoInBarFlag = settings.value("showSystemInfoInBarFlag", "true") + showSystemInfoInBar = showSystemInfoInBarFlag === "true" + let showMediaInBarFlag = settings.value("showMediaInBarFlag", "true") + showMediaInBar = showMediaInBarFlag === "true" let useSWWWFlag = settings.value("useSWWWFlag", "false") useSWWW = useSWWWFlag === "true" let randomWallpaperFlag = settings.value("randomWallpaperFlag", "false") @@ -54,6 +60,7 @@ QtObject { transitionFps = settings.value("transitionFps", transitionFps) transitionType = settings.value("transitionType", transitionType) transitionDuration = settings.value("transitionDuration", transitionDuration) + WallpaperManager.setCurrentWallpaper(currentWallpaper, true); } @@ -65,6 +72,8 @@ QtObject { settings.setValue("currentWallpaper", currentWallpaper) settings.setValue("videoPath", videoPath) settings.setValue("showActiveWindowIconFlag", showActiveWindowIcon ? "true" : "false") + settings.setValue("showSystemInfoInBarFlag", showSystemInfoInBar ? "true" : "false") + settings.setValue("showMediaInBarFlag", showMediaInBar ? "true" : "false") settings.setValue("useSWWWFlag", useSWWW ? "true" : "false") settings.setValue("randomWallpaperFlag", randomWallpaper ? "true" : "false") settings.setValue("useWallpaperThemeFlag", useWallpaperTheme ? "true" : "false") diff --git a/Settings/Theme.json b/Settings/Theme.json index 8cbefc2..41c634a 100644 --- a/Settings/Theme.json +++ b/Settings/Theme.json @@ -23,6 +23,6 @@ "onAccent": "#0E0F10", "outline": "#565758", - "shadow": "#0E0F10B3", - "overlay": "#0E0F10CC" + "shadow": "#0E0F10", + "overlay": "#0E0F10" } diff --git a/Settings/Theme.qml b/Settings/Theme.qml index 99daf3f..ac3b6a5 100644 --- a/Settings/Theme.qml +++ b/Settings/Theme.qml @@ -6,6 +6,10 @@ import Quickshell.Io Singleton { id: root + + function applyOpacity(color, opacity) { + return color.replace("#", "#" + opacity); + } // FileView to load theme data from JSON file FileView { @@ -50,8 +54,8 @@ Singleton { property string outline: "#44485A" // Shadows & Overlays - property string shadow: "#000000B3" - property string overlay: "#11121ACC" + property string shadow: "#000000" + property string overlay: "#11121A" } } @@ -87,8 +91,8 @@ Singleton { property color outline: themeData.outline // Shadows & Overlays - property color shadow: themeData.shadow - property color overlay: themeData.overlay + property color shadow: applyOpacity(themeData.shadow, "B3") + property color overlay: applyOpacity(themeData.overlay, "66") // Font Properties property string fontFamily: "Roboto" // Family for all text @@ -98,3 +102,4 @@ Singleton { property int fontSizeSmall: 14 // Small text like clock, labels property int fontSizeCaption: 12 // Captions and fine print } + diff --git a/Templates/templates/quickshell.json b/Templates/templates/quickshell.json index 58d6afd..16aef1c 100644 --- a/Templates/templates/quickshell.json +++ b/Templates/templates/quickshell.json @@ -14,8 +14,8 @@ "accentSecondary": "{{ color4 | lighten(0.2) }}", "accentTertiary": "{{ color4 | darken(0.2) }}", - "error": "{{ color5 | darken(0.1) }}", - "warning": "{{ color5 | lighten(0.1) }}", + "error": "{{ color5 | lighten(0.1) }}", + "warning": "{{ color5 | lighten(0.3) }}", "highlight": "{{ color4 | lighten(0.4) }}", "rippleEffect": "{{ color4 | lighten(0.1) }}", @@ -23,6 +23,6 @@ "onAccent": "{{ background }}", "outline": "{{ background | lighten(0.3) }}", - "shadow": "{{ background }}B3", - "overlay": "{{ background }}CC" + "shadow": "{{ background }}", + "overlay": "{{ background }}" } diff --git a/Widgets/LockScreen.qml b/Widgets/LockScreen.qml index 04830bc..78ab2ef 100644 --- a/Widgets/LockScreen.qml +++ b/Widgets/LockScreen.qml @@ -28,31 +28,36 @@ WlSessionLock { // On component completed, fetch weather data Component.onCompleted: { - fetchWeatherData() + fetchWeatherData(); } // Weather fetching function function fetchWeatherData() { - WeatherHelper.fetchCityWeather(weatherCity, - function(result) { - weatherData = result.weather; - weatherError = ""; - }, - function(err) { - weatherError = err; - } - ); + WeatherHelper.fetchCityWeather(weatherCity, function (result) { + weatherData = result.weather; + weatherError = ""; + }, function (err) { + weatherError = err; + }); } function materialSymbolForCode(code) { - if (code === 0) return "sunny"; - if (code === 1 || code === 2) return "partly_cloudy_day"; - if (code === 3) return "cloud"; - if (code >= 45 && code <= 48) return "foggy"; - if (code >= 51 && code <= 67) return "rainy"; - if (code >= 71 && code <= 77) return "weather_snowy"; - if (code >= 80 && code <= 82) return "rainy"; - if (code >= 95 && code <= 99) return "thunderstorm"; + if (code === 0) + return "sunny"; + if (code === 1 || code === 2) + return "partly_cloudy_day"; + if (code === 3) + return "cloud"; + if (code >= 45 && code <= 48) + return "foggy"; + if (code >= 51 && code <= 67) + return "rainy"; + if (code >= 71 && code <= 77) + return "weather_snowy"; + if (code >= 80 && code <= 82) + return "rainy"; + if (code >= 95 && code <= 99) + return "thunderstorm"; return "cloud"; } @@ -72,12 +77,12 @@ WlSessionLock { console.log("Starting PAM authentication..."); lock.authenticating = true; lock.errorMessage = ""; - - console.log("[LockScreen] About to create PAM context with userName:", Quickshell.env("USER")) + + console.log("[LockScreen] About to create PAM context with userName:", Quickshell.env("USER")); var pam = Qt.createQmlObject('import Quickshell.Services.Pam; PamContext { config: "login"; user: "' + Quickshell.env("USER") + '" }', lock); console.log("PamContext created", pam); - - pam.onCompleted.connect(function(result) { + + pam.onCompleted.connect(function (result) { console.log("PAM completed with result:", result); lock.authenticating = false; if (result === PamResult.Success) { @@ -92,30 +97,30 @@ WlSessionLock { } pam.destroy(); }); - - pam.onError.connect(function(error) { + + pam.onError.connect(function (error) { console.log("PAM error:", error); lock.authenticating = false; lock.errorMessage = pam.message || "Authentication error."; lock.password = ""; pam.destroy(); }); - - pam.onPamMessage.connect(function() { + + pam.onPamMessage.connect(function () { console.log("PAM message:", pam.message, "isError:", pam.messageIsError); if (pam.messageIsError) { lock.errorMessage = pam.message; } }); - - pam.onResponseRequiredChanged.connect(function() { + + pam.onResponseRequiredChanged.connect(function () { console.log("PAM response required:", pam.responseRequired); if (pam.responseRequired && lock.authenticating) { console.log("Responding to PAM with password"); pam.respond(lock.password); } }); - + var started = pam.start(); console.log("PAM start result:", started); } @@ -151,7 +156,7 @@ WlSessionLock { height: 80 radius: 40 color: Theme.accentPrimary - + Image { id: avatarImage anchors.fill: parent @@ -222,10 +227,10 @@ WlSessionLock { echoMode: TextInput.Password passwordCharacter: "●" passwordMaskDelay: 0 - + text: lock.password onTextChanged: lock.password = text - + // Placeholder text Text { anchors.centerIn: parent @@ -237,30 +242,36 @@ WlSessionLock { } // Handle Enter key - Keys.onPressed: function(event) { + Keys.onPressed: function (event) { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { - lock.unlockAttempt() + lock.unlockAttempt(); } } Component.onCompleted: { - forceActiveFocus() + forceActiveFocus(); } } } // Error message - Text { + Rectangle { + id: errorMessageRect Layout.alignment: Qt.AlignHCenter - text: lock.errorMessage - color: Theme.error - font.family: Theme.fontFamily - font.pixelSize: 14 + width: parent.width * 0.8 + height: 44 + color: Theme.overlay + radius: 22 visible: lock.errorMessage !== "" - opacity: lock.errorMessage !== "" ? 1 : 0 - Behavior on opacity { - NumberAnimation { duration: 200 } + Text { + anchors.centerIn: parent + text: lock.errorMessage + color: Theme.error + font.family: Theme.fontFamily + font.pixelSize: 14 + opacity: 1 + visible: lock.errorMessage !== "" } } @@ -292,13 +303,15 @@ WlSessionLock { hoverEnabled: true onClicked: { if (!lock.authenticating) { - lock.unlockAttempt() + lock.unlockAttempt(); } } } Behavior on opacity { - NumberAnimation { duration: 200 } + NumberAnimation { + duration: 200 + } } } } @@ -344,7 +357,7 @@ WlSessionLock { verticalAlignment: Text.AlignVCenter } Text { - text: weatherData && weatherData.current_weather ? (Settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9/5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.useFahrenheit ? "--°F" : "--°C") + text: weatherData && weatherData.current_weather ? (Settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.useFahrenheit ? "--°F" : "--°C") font.family: Theme.fontFamily font.pixelSize: 18 color: Theme.textSecondary @@ -369,8 +382,8 @@ WlSessionLock { running: true repeat: true onTriggered: { - timeText.text = Qt.formatDateTime(new Date(), "HH:mm") - dateText.text = Qt.formatDateTime(new Date(), "dddd, MMMM d") + timeText.text = Qt.formatDateTime(new Date(), "HH:mm"); + dateText.text = Qt.formatDateTime(new Date(), "dddd, MMMM d"); } } @@ -380,7 +393,7 @@ WlSessionLock { running: true repeat: true onTriggered: { - fetchWeatherData() + fetchWeatherData(); } } @@ -392,7 +405,9 @@ WlSessionLock { spacing: 12 // Shutdown Rectangle { - width: 48; height: 48; radius: 24 + width: 48 + height: 48 + radius: 24 color: shutdownArea.containsMouse ? Theme.error : "transparent" border.color: Theme.error border.width: 1 @@ -401,7 +416,7 @@ WlSessionLock { anchors.fill: parent hoverEnabled: true onClicked: { - Qt.createQmlObject('import Quickshell.Io; Process { command: ["shutdown", "-h", "now"]; running: true }', lock) + Qt.createQmlObject('import Quickshell.Io; Process { command: ["shutdown", "-h", "now"]; running: true }', lock); } } Text { @@ -414,7 +429,9 @@ WlSessionLock { } // Reboot Rectangle { - width: 48; height: 48; radius: 24 + width: 48 + height: 48 + radius: 24 color: rebootArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary border.width: 1 @@ -423,7 +440,7 @@ WlSessionLock { anchors.fill: parent hoverEnabled: true onClicked: { - Qt.createQmlObject('import Quickshell.Io; Process { command: ["reboot"]; running: true }', lock) + Qt.createQmlObject('import Quickshell.Io; Process { command: ["reboot"]; running: true }', lock); } } Text { @@ -436,7 +453,9 @@ WlSessionLock { } // Logout Rectangle { - width: 48; height: 48; radius: 24 + width: 48 + height: 48 + radius: 24 color: logoutArea.containsMouse ? Theme.accentSecondary : "transparent" border.color: Theme.accentSecondary border.width: 1 @@ -445,7 +464,7 @@ WlSessionLock { anchors.fill: parent hoverEnabled: true onClicked: { - Qt.createQmlObject('import Quickshell.Io; Process { command: ["loginctl", "terminate-user", "' + Quickshell.env("USER") + '"]; running: true }', lock) + Qt.createQmlObject('import Quickshell.Io; Process { command: ["loginctl", "terminate-user", "' + Quickshell.env("USER") + '"]; running: true }', lock); } } Text { @@ -458,4 +477,4 @@ WlSessionLock { } } } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Config.qml b/Widgets/Sidebar/Config.qml index e4bd633..c36c89c 100644 --- a/Widgets/Sidebar/Config.qml +++ b/Widgets/Sidebar/Config.qml @@ -10,7 +10,7 @@ import qs.Settings Rectangle { id: settingsModal anchors.centerIn: parent - color: Settings.Theme.backgroundPrimary + color: Theme.backgroundPrimary radius: 20 visible: false z: 100 @@ -36,15 +36,15 @@ Rectangle { text: "settings" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeHeader - color: Settings.Theme.accentPrimary + color: Theme.accentPrimary } Text { text: "Settings" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeHeader font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary Layout.fillWidth: true } @@ -52,8 +52,8 @@ Rectangle { width: 36 height: 36 radius: 18 - color: closeButtonArea.containsMouse ? Settings.Theme.accentPrimary : "transparent" - border.color: Settings.Theme.accentPrimary + color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary border.width: 1 Text { @@ -61,7 +61,7 @@ Rectangle { text: "close" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody - color: closeButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.accentPrimary + color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } MouseArea { @@ -77,7 +77,7 @@ Rectangle { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 180 - color: Settings.Theme.surface + color: Theme.surface radius: 18 ColumnLayout { @@ -94,15 +94,15 @@ Rectangle { text: "wb_sunny" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody - color: Settings.Theme.accentPrimary + color: Theme.accentPrimary } Text { text: "Weather Settings" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeBody font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary Layout.fillWidth: true } } @@ -114,18 +114,18 @@ Rectangle { Text { text: "City" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary } Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 radius: 8 - color: Settings.Theme.surfaceVariant - border.color: cityInput.activeFocus ? Settings.Theme.accentPrimary : Settings.Theme.outline + color: Theme.surfaceVariant + border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 TextInput { @@ -139,9 +139,9 @@ Rectangle { anchors.topMargin: 6 anchors.bottomMargin: 6 text: tempWeatherCity - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall - color: Settings.Theme.textPrimary + color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true focus: true @@ -170,10 +170,10 @@ Rectangle { Text { text: "Temperature Unit" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary } Item { @@ -186,8 +186,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: Settings.Theme.accentPrimary - border.color: Settings.Theme.accentPrimary + color: Theme.accentPrimary + border.color: Theme.accentPrimary border.width: 2 Rectangle { @@ -195,8 +195,8 @@ Rectangle { width: 28 height: 28 radius: 14 - color: Settings.Theme.surface - border.color: Settings.Theme.outline + color: Theme.surface + border.color: Theme.outline border.width: 1 y: 2 x: tempUseFahrenheit ? customSwitch.width - width - 2 : 2 @@ -204,10 +204,10 @@ Rectangle { Text { anchors.centerIn: parent text: tempUseFahrenheit ? "°F" : "°C" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeCaption font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary } Behavior on x { @@ -230,7 +230,7 @@ Rectangle { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 140 - color: Settings.Theme.surface + color: Theme.surface radius: 18 anchors.left: parent.left anchors.right: parent.right @@ -253,15 +253,15 @@ Rectangle { text: "person" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody - color: Settings.Theme.accentPrimary + color: Theme.accentPrimary } Text { text: "Profile Image" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeBody font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary Layout.fillWidth: true } } @@ -275,8 +275,8 @@ Rectangle { width: 36 height: 36 radius: 18 - color: Settings.Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Settings.Theme.accentPrimary : Settings.Theme.outline + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 Image { @@ -306,7 +306,7 @@ Rectangle { text: "person" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody - color: Settings.Theme.accentPrimary + color: Theme.accentPrimary visible: tempProfileImage === "" } } @@ -316,8 +316,8 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 radius: 8 - color: Settings.Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Settings.Theme.accentPrimary : Settings.Theme.outline + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 TextInput { @@ -331,9 +331,9 @@ Rectangle { anchors.topMargin: 6 anchors.bottomMargin: 6 text: tempProfileImage - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall - color: Settings.Theme.textPrimary + color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true focus: true @@ -359,7 +359,7 @@ Rectangle { Rectangle { Layout.fillWidth: true Layout.preferredHeight: 100 - color: Settings.Theme.surface + color: Theme.surface radius: 18 ColumnLayout { @@ -375,14 +375,14 @@ Rectangle { text: "image" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody - color: Settings.Theme.accentPrimary + color: Theme.accentPrimary } Text { text: "Wallpaper Folder" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeBody font.bold: true - color: Settings.Theme.textPrimary + color: Theme.textPrimary Layout.fillWidth: true } } @@ -392,8 +392,8 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 radius: 8 - color: Settings.Theme.surfaceVariant - border.color: wallpaperFolderInput.activeFocus ? Settings.Theme.accentPrimary : Settings.Theme.outline + color: Theme.surfaceVariant + border.color: wallpaperFolderInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 TextInput { @@ -407,9 +407,9 @@ Rectangle { anchors.topMargin: 6 anchors.bottomMargin: 6 text: tempWallpaperFolder - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall - color: Settings.Theme.textPrimary + color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true selectByMouse: true @@ -435,7 +435,7 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 44 radius: 12 - color: applyButtonArea.containsMouse ? Settings.Theme.accentPrimary : Settings.Theme.accentPrimary + color: applyButtonArea.containsMouse ? Theme.accentPrimary : Theme.accentPrimary border.color: "transparent" border.width: 0 opacity: 1.0 @@ -443,10 +443,10 @@ Rectangle { Text { anchors.centerIn: parent text: "Apply Changes" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.bold: true - color: applyButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.onAccent + color: applyButtonArea.containsMouse ? Theme.onAccent : Theme.onAccent } MouseArea { diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 016ec8b..6dc642b 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -7,7 +7,7 @@ import qs.Settings Rectangle { id: profileSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 200 + Layout.preferredHeight: 300 color: Theme.surface radius: 18 border.color: "transparent" @@ -15,6 +15,10 @@ Rectangle { Layout.bottomMargin: 16 property bool showActiveWindowIcon: false signal showAWIconChanged(bool showActiveWindowIcon) + property bool showSystemInfoInBar: true + signal showSystemInfoChanged(bool showSystemInfoInBar) + property bool showMediaInBar: false + signal showMediaChanged(bool showMediaInBar) ColumnLayout { anchors.fill: parent @@ -125,7 +129,6 @@ Rectangle { } } - // Show Active Window Icon Setting RowLayout { spacing: 8 @@ -148,8 +151,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: Theme.accentPrimary - border.color: Theme.accentPrimary + color: showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant + border.color: showActiveWindowIcon ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -177,6 +180,110 @@ Rectangle { } } + // Show System Info In Bar Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Show System Info In Bar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + Layout.alignment: Qt.AlignVCenter + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: customSwitch2 + width: 52 + height: 32 + radius: 16 + color: showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: showSystemInfoInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: thumb2 + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: showSystemInfoInBar ? customSwitch2.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + showSystemInfoChanged(!showSystemInfoInBar) + } + } + } + } + + // Show Media In Bar Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Show Media In Bar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + Layout.alignment: Qt.AlignVCenter + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: customSwitch3 + width: 52 + height: 32 + radius: 16 + color: showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: showMediaInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: thumb3 + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: showMediaInBar ? customSwitch3.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + showMediaChanged(!showMediaInBar) + } + } + } + } + // Video Path Input Row RowLayout { spacing: 8 @@ -184,7 +291,8 @@ Rectangle { Text { text: "Video Path" - font.pixelSize: 14 + font.pixelSize: 13 + font.bold: true color: Theme.textPrimary Layout.alignment: Qt.AlignVCenter } diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index 05311c1..7ea62bc 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -9,7 +9,7 @@ import qs.Services PanelWindow { id: settingsModal implicitWidth: 480 - implicitHeight: 720 + implicitHeight: 800 visible: false color: "transparent" anchors.top: true @@ -35,6 +35,8 @@ PanelWindow { property int tempTransitionFps: Settings.transitionFps property string tempTransitionType: Settings.transitionType property real tempTransitionDuration: Settings.transitionDuration + property bool tempShowSystemInfoInBar: Settings.showSystemInfoInBar + property bool tempShowMediaInBar: Settings.showMediaInBar Rectangle { anchors.fill: parent @@ -141,6 +143,14 @@ PanelWindow { onShowAWIconChanged: function (showActiveWindowIcon) { tempShowActiveWindowIcon = showActiveWindowIcon; } + showSystemInfoInBar: tempShowSystemInfoInBar + onShowSystemInfoChanged: function (showSystemInfoInBar) { + tempShowSystemInfoInBar = showSystemInfoInBar; + } + showMediaInBar: tempShowMediaInBar + onShowMediaChanged: function (showMediaInBar) { + tempShowMediaInBar = showMediaInBar; + } } } CollapsibleCategory { @@ -224,6 +234,8 @@ PanelWindow { Settings.transitionFps = tempTransitionFps; Settings.transitionType = tempTransitionType; Settings.transitionDuration = tempTransitionDuration; + Settings.showSystemInfoInBar = tempShowSystemInfoInBar; + Settings.showMediaInBar = tempShowMediaInBar; Settings.saveSettings(); if (typeof weather !== 'undefined' && weather) { weather.fetchCityWeather(); diff --git a/Widgets/Sidebar/Panel/BluetoothPanel.qml b/Widgets/Sidebar/Panel/BluetoothPanel.qml index 7820dec..4848e0c 100644 --- a/Widgets/Sidebar/Panel/BluetoothPanel.qml +++ b/Widgets/Sidebar/Panel/BluetoothPanel.qml @@ -73,7 +73,7 @@ Item { PanelWindow { id: bluetoothPanelModal implicitWidth: 480 - implicitHeight: 720 + implicitHeight: 800 visible: false color: "transparent" anchors.top: true diff --git a/Widgets/Sidebar/Panel/Music.qml b/Widgets/Sidebar/Panel/Music.qml index ef5a215..06dd621 100644 --- a/Widgets/Sidebar/Panel/Music.qml +++ b/Widgets/Sidebar/Panel/Music.qml @@ -2,90 +2,16 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import Qt5Compat.GraphicalEffects -import Quickshell.Services.Mpris import qs.Settings import qs.Components -import QtQuick +import qs.Services Rectangle { id: musicCard width: 360 - height: 200 + height: 250 color: "transparent" - property var currentPlayer: null - property real currentPosition: 0 - property int selectedPlayerIndex: 0 - - // Returns available MPRIS players - function getAvailablePlayers() { - if (!Mpris.players || !Mpris.players.values) { - return [] - } - - let allPlayers = Mpris.players.values - let controllablePlayers = [] - - for (let i = 0; i < allPlayers.length; i++) { - let player = allPlayers[i] - if (player && player.canControl) { - controllablePlayers.push(player) - } - } - - return controllablePlayers - } - - // Returns active player or first available - function findActivePlayer() { - let availablePlayers = getAvailablePlayers() - if (availablePlayers.length === 0) { - return null - } - - // Use selected player if valid, otherwise use first available - if (selectedPlayerIndex < availablePlayers.length) { - return availablePlayers[selectedPlayerIndex] - } else { - selectedPlayerIndex = 0 - return availablePlayers[0] - } - } - - // Updates currentPlayer and currentPosition - function updateCurrentPlayer() { - let newPlayer = findActivePlayer() - if (newPlayer !== currentPlayer) { - currentPlayer = newPlayer - currentPosition = currentPlayer ? currentPlayer.position : 0 - } - } - - // Updates progress bar every second - Timer { - id: positionTimer - interval: 1000 - running: currentPlayer && currentPlayer.isPlaying && currentPlayer.length > 0 - repeat: true - onTriggered: { - if (currentPlayer && currentPlayer.isPlaying) { - currentPosition = currentPlayer.position - } - } - } - - // Reacts to player list changes - Connections { - target: Mpris.players - function onValuesChanged() { - updateCurrentPlayer() - } - } - - Component.onCompleted: { - updateCurrentPlayer() - } - Rectangle { id: card anchors.fill: parent @@ -96,7 +22,7 @@ Rectangle { Item { width: parent.width height: parent.height - visible: !currentPlayer + visible: !MusicManager.currentPlayer ColumnLayout { anchors.centerIn: parent @@ -111,7 +37,7 @@ Rectangle { } Text { - text: getAvailablePlayers().length > 0 ? "No controllable player selected" : "No music player detected" + text: MusicManager.hasPlayer ? "No controllable player selected" : "No music player detected" color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall @@ -141,6 +67,7 @@ Rectangle { // Spectrum visualizer CircularSpectrum { id: spectrum + values: MusicManager.cavaValues anchors.centerIn: parent innerRadius: 30 // just outside 60x60 album art outerRadius: 48 // how far bars extend @@ -170,7 +97,7 @@ Rectangle { asynchronous: true sourceSize.width: 60 sourceSize.height: 60 - source: currentPlayer ? (currentPlayer.trackArtUrl || "") : "" + source: MusicManager.trackArtUrl visible: source.toString() !== "" // Rounded corners using layer @@ -204,7 +131,7 @@ Rectangle { spacing: 4 Text { - text: currentPlayer ? (currentPlayer.trackTitle || "Unknown Track") : "" + text: MusicManager.trackTitle color: Theme.textPrimary font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall @@ -216,7 +143,7 @@ Rectangle { } Text { - text: currentPlayer ? (currentPlayer.trackArtist || "Unknown Artist") : "" + text: MusicManager.trackArtist color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.8) font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeCaption @@ -225,7 +152,7 @@ Rectangle { } Text { - text: currentPlayer ? (currentPlayer.trackAlbum || "Unknown Album") : "" + text: MusicManager.trackAlbum color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeCaption @@ -244,8 +171,8 @@ Rectangle { color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.15) Layout.fillWidth: true - property real progressRatio: currentPlayer && currentPlayer.length > 0 ? - (currentPosition / currentPlayer.length) : 0 + property real progressRatio: MusicManager.trackLength > 0 ? + (MusicManager.currentPosition / MusicManager.trackLength) : 0 Rectangle { id: progressFill @@ -272,7 +199,7 @@ Rectangle { x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2)) anchors.verticalCenter: parent.verticalCenter - visible: currentPlayer && currentPlayer.length > 0 + visible: MusicManager.trackLength > 0 scale: progressMouseArea.containsMouse || progressMouseArea.pressed ? 1.2 : 1.0 Behavior on scale { @@ -285,23 +212,17 @@ Rectangle { id: progressMouseArea anchors.fill: parent hoverEnabled: true - enabled: currentPlayer && currentPlayer.length > 0 && currentPlayer.canSeek + enabled: MusicManager.trackLength > 0 && MusicManager.canSeek onClicked: function(mouse) { - if (currentPlayer && currentPlayer.length > 0) { - let ratio = mouse.x / width - let seekPosition = ratio * currentPlayer.length - currentPlayer.position = seekPosition - currentPosition = seekPosition - } + let ratio = mouse.x / width + MusicManager.seekByRatio(ratio) } onPositionChanged: function(mouse) { - if (pressed && currentPlayer && currentPlayer.length > 0) { + if (pressed) { let ratio = Math.max(0, Math.min(1, mouse.x / width)) - let seekPosition = ratio * currentPlayer.length - currentPlayer.position = seekPosition - currentPosition = seekPosition + MusicManager.seekByRatio(ratio) } } } @@ -326,8 +247,8 @@ Rectangle { id: previousButton anchors.fill: parent hoverEnabled: true - enabled: currentPlayer && currentPlayer.canGoPrevious - onClicked: if (currentPlayer) currentPlayer.previous() + enabled: MusicManager.canGoPrevious + onClicked: MusicManager.previous() } Text { @@ -352,21 +273,13 @@ Rectangle { id: playButton anchors.fill: parent hoverEnabled: true - enabled: currentPlayer && (currentPlayer.canPlay || currentPlayer.canPause) - onClicked: { - if (currentPlayer) { - if (currentPlayer.isPlaying) { - currentPlayer.pause() - } else { - currentPlayer.play() - } - } - } + enabled: MusicManager.canPlay || MusicManager.canPause + onClicked: MusicManager.playPause() } Text { anchors.centerIn: parent - text: currentPlayer && currentPlayer.isPlaying ? "pause" : "play_arrow" + text: MusicManager.isPlaying ? "pause" : "play_arrow" font.family: "Material Symbols Outlined" font.pixelSize: Theme.fontSizeBody color: playButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) @@ -386,8 +299,8 @@ Rectangle { id: nextButton anchors.fill: parent hoverEnabled: true - enabled: currentPlayer && currentPlayer.canGoNext - onClicked: if (currentPlayer) currentPlayer.next() + enabled: MusicManager.canGoNext + onClicked: MusicManager.next() } Text { @@ -401,10 +314,4 @@ Rectangle { } } } - - // Audio Visualizer (Cava) - Cava { - id: cava - count: 64 - } } \ No newline at end of file diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index df631ff..27df543 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -10,7 +10,7 @@ import qs.Components PanelWindow { id: panelPopup implicitWidth: 500 - implicitHeight: 750 + implicitHeight: 800 visible: false color: "transparent" screen: modelData @@ -30,7 +30,6 @@ PanelWindow { slideAnim.from = width; slideAnim.to = 0; slideAnim.running = true; - if (systemMonitor) systemMonitor.startMonitoring(); if (weather) weather.startWeatherFetch(); if (systemWidget) systemWidget.panelVisible = true; if (quickAccessWidget) quickAccessWidget.panelVisible = true; @@ -56,7 +55,6 @@ PanelWindow { if (panelPopup.slideOffset === panelPopup.width) { panelPopup.visible = false; // Stop monitoring and background tasks when hidden - if (systemMonitor) systemMonitor.stopMonitoring(); if (weather) weather.stopWeatherFetch(); if (systemWidget) systemWidget.panelVisible = false; if (quickAccessWidget) quickAccessWidget.panelVisible = false; diff --git a/Widgets/Sidebar/Panel/QuickAccess.qml b/Widgets/Sidebar/Panel/QuickAccess.qml index 8aa9bb7..efe071b 100644 --- a/Widgets/Sidebar/Panel/QuickAccess.qml +++ b/Widgets/Sidebar/Panel/QuickAccess.qml @@ -4,7 +4,7 @@ import QtQuick.Controls import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Io -import "root:/Settings" as Settings +import qs.Settings Rectangle { id: quickAccessWidget @@ -24,7 +24,7 @@ Rectangle { Rectangle { id: card anchors.fill: parent - color: Settings.Theme.surface + color: Theme.surface radius: 18 RowLayout { @@ -38,8 +38,8 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 44 radius: 12 - color: settingsButtonArea.containsMouse ? Settings.Theme.accentPrimary : "transparent" - border.color: Settings.Theme.accentPrimary + color: settingsButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary border.width: 1 RowLayout { @@ -51,15 +51,15 @@ Rectangle { text: "settings" font.family: settingsButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" font.pixelSize: 16 - color: settingsButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.accentPrimary + color: settingsButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } Text { text: "Settings" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: 14 font.bold: true - color: settingsButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.textPrimary + color: settingsButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } } @@ -80,9 +80,9 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 44 radius: 12 - color: isRecording ? Settings.Theme.accentPrimary : - (recorderButtonArea.containsMouse ? Settings.Theme.accentPrimary : "transparent") - border.color: Settings.Theme.accentPrimary + color: isRecording ? Theme.accentPrimary : + (recorderButtonArea.containsMouse ? Theme.accentPrimary : "transparent") + border.color: Theme.accentPrimary border.width: 1 RowLayout { @@ -94,15 +94,15 @@ Rectangle { text: isRecording ? "radio_button_checked" : "radio_button_unchecked" font.family: (isRecording || recorderButtonArea.containsMouse) ? "Material Symbols Rounded" : "Material Symbols Outlined" font.pixelSize: 16 - color: isRecording || recorderButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.accentPrimary + color: isRecording || recorderButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } Text { text: isRecording ? "End" : "Record" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: 14 font.bold: true - color: isRecording || recorderButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.textPrimary + color: isRecording || recorderButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } } @@ -127,8 +127,8 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 44 radius: 12 - color: wallpaperButtonArea.containsMouse ? Settings.Theme.accentPrimary : "transparent" - border.color: Settings.Theme.accentPrimary + color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary border.width: 1 RowLayout { @@ -140,15 +140,15 @@ Rectangle { text: "image" font.family: "Material Symbols Outlined" font.pixelSize: 16 - color: wallpaperButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.accentPrimary + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } Text { text: "Wallpaper" - font.family: Settings.Theme.fontFamily + font.family: Theme.fontFamily font.pixelSize: 14 font.bold: true - color: wallpaperButtonArea.containsMouse ? Settings.Theme.onAccent : Settings.Theme.textPrimary + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } } diff --git a/Widgets/Sidebar/Panel/SystemMonitor.qml b/Widgets/Sidebar/Panel/SystemMonitor.qml index c63c27a..ba2ebf5 100644 --- a/Widgets/Sidebar/Panel/SystemMonitor.qml +++ b/Widgets/Sidebar/Panel/SystemMonitor.qml @@ -2,116 +2,22 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Controls 2.15 import Quickshell.Io -import "root:/Settings" as Settings -import "root:/Components" as Components +import qs.Components +import qs.Services +import qs.Settings Rectangle { id: systemMonitor width: 70 - height: 200 + height: 250 color: "transparent" - property real cpuUsage: 0 - property real memoryUsage: 0 - property real diskUsage: 0 property bool isVisible: false - Timer { - id: cpuTimer - interval: 2000 - repeat: true - running: isVisible - onTriggered: cpuInfo.running = true - } - - Timer { - id: memoryTimer - interval: 3000 - repeat: true - running: isVisible - onTriggered: memoryInfo.running = true - } - - Timer { - id: diskTimer - interval: 5000 - repeat: true - running: isVisible - onTriggered: diskInfo.running = true - } - - // Process for getting CPU usage - Process { - id: cpuInfo - command: ["sh", "-c", "top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | awk -F'%' '{print $1}'"] - running: false - - stdout: SplitParser { - onRead: data => { - let usage = parseFloat(data.trim()) - if (!isNaN(usage)) { - systemMonitor.cpuUsage = usage - } - cpuInfo.running = false - } - } - } - - // Process for getting memory usage - Process { - id: memoryInfo - command: ["sh", "-c", "free | grep Mem | awk '{print int($3/$2 * 100)}'"] - running: false - - stdout: SplitParser { - onRead: data => { - let usage = parseFloat(data.trim()) - if (!isNaN(usage)) { - systemMonitor.memoryUsage = usage - } - memoryInfo.running = false - } - } - } - - // Process for getting disk usage - Process { - id: diskInfo - command: ["sh", "-c", "df / | tail -1 | awk '{print int($5)}'"] - running: false - - stdout: SplitParser { - onRead: data => { - let usage = parseFloat(data.trim()) - if (!isNaN(usage)) { - systemMonitor.diskUsage = usage - } - diskInfo.running = false - } - } - } - - // Function to start monitoring - function startMonitoring() { - isVisible = true - // Trigger initial readings - cpuInfo.running = true - memoryInfo.running = true - diskInfo.running = true - } - - // Function to stop monitoring - function stopMonitoring() { - isVisible = false - cpuInfo.running = false - memoryInfo.running = false - diskInfo.running = false - } - Rectangle { id: card anchors.fill: parent - color: Settings.Theme.surface + color: Theme.surface radius: 18 ColumnLayout { @@ -121,8 +27,8 @@ Rectangle { Layout.alignment: Qt.AlignVCenter // CPU Usage - Components.CircularProgressBar { - progress: cpuUsage / 100 + CircularProgressBar { + progress: Sysinfo.cpuUsage / 100 size: 50 strokeWidth: 4 hasNotch: true @@ -131,9 +37,21 @@ Rectangle { Layout.alignment: Qt.AlignHCenter } + // Cpu Temp + CircularProgressBar { + progress: Sysinfo.cpuTemp / 100 + size: 50 + strokeWidth: 4 + hasNotch: true + units: "°C" + notchIcon: "thermometer" + notchIconSize: 14 + Layout.alignment: Qt.AlignHCenter + } + // Memory Usage - Components.CircularProgressBar { - progress: memoryUsage / 100 + CircularProgressBar { + progress: Sysinfo.memoryUsagePer / 100 size: 50 strokeWidth: 4 hasNotch: true @@ -143,8 +61,8 @@ Rectangle { } // Disk Usage - Components.CircularProgressBar { - progress: diskUsage / 100 + CircularProgressBar { + progress: Sysinfo.diskUsage / 100 size: 50 strokeWidth: 4 hasNotch: true diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml index 46c9fbb..f881d54 100644 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ b/Widgets/Sidebar/Panel/WallpaperPanel.qml @@ -10,7 +10,7 @@ import qs.Services PanelWindow { id: wallpaperPanelModal implicitWidth: 480 - implicitHeight: 720 + implicitHeight: 800 visible: false color: "transparent" anchors.top: true diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index d2b474d..da28eb0 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -2,7 +2,7 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Controls 2.15 import qs.Settings -import "root:/Helpers/Weather.js" as WeatherHelper +import "../../../Helpers/Weather.js" as WeatherHelper Rectangle { id: weatherRoot diff --git a/Widgets/Sidebar/Panel/WifiPanel.qml b/Widgets/Sidebar/Panel/WifiPanel.qml index ca82650..7ddbc5d 100644 --- a/Widgets/Sidebar/Panel/WifiPanel.qml +++ b/Widgets/Sidebar/Panel/WifiPanel.qml @@ -335,7 +335,7 @@ Item { PanelWindow { id: wifiPanelModal implicitWidth: 480 - implicitHeight: 720 + implicitHeight: 800 visible: false color: "transparent" anchors.top: true diff --git a/shell.qml b/shell.qml index 0b94c67..5a9a270 100644 --- a/shell.qml +++ b/shell.qml @@ -15,6 +15,13 @@ Scope { property alias appLauncherPanel: appLauncherPanel + function updateVolume(vol) { + volume = vol; + if (defaultAudioSink && defaultAudioSink.audio) { + defaultAudioSink.audio.volume = vol / 100; + } + } + Component.onCompleted: { Quickshell.shell = root; } @@ -48,7 +55,7 @@ Scope { } property var defaultAudioSink: Pipewire.defaultAudioSink - property int volume: defaultAudioSink && defaultAudioSink.audio ? Math.round(defaultAudioSink.audio.volume * 100) : 0 + property int volume: defaultAudioSink && defaultAudioSink.audio && defaultAudioSink.audio.volume ? Math.round(defaultAudioSink.audio.volume * 100) : 0 PwObjectTracker { objects: [Pipewire.defaultAudioSink]