diff --git a/Widgets/LockScreen.qml b/Widgets/LockScreen.qml index b906649..6e361e5 100644 --- a/Widgets/LockScreen.qml +++ b/Widgets/LockScreen.qml @@ -27,15 +27,13 @@ WlSessionLock { property double currentTemp: 0 locked: false - // On component completed, request to fetch weather data, with a little delay. - // Without this delay the city name is not loaded yet. + // Request to fetch weather with a little delay to ensure weatherCity is properly loaded. Component.onCompleted: { Qt.callLater(function () { fetchWeatherData(); }) } - // Weather fetching function function fetchWeatherData() { WeatherHelper.fetchCityWeather(weatherCity, function (result) { weatherData = result.weather; @@ -65,7 +63,6 @@ WlSessionLock { return "cloud"; } - // Authentication function function unlockAttempt() { console.log("Unlock attempt started"); if (!pamAvailable) { @@ -129,9 +126,8 @@ WlSessionLock { console.log("PAM start result:", started); } - // Lock surface WlSessionLockSurface { - // Blurred wallpaper background + // Wallpaper image to blur Image { id: lockBgImage anchors.fill: parent @@ -139,28 +135,23 @@ WlSessionLock { source: WallpaperManager.currentWallpaper !== "" ? WallpaperManager.currentWallpaper : "" cache: true smooth: false - visible: true // Show the original for FastBlur input + visible: true // source for MultiEffect } - FastBlur { + + MultiEffect { + id: lockBgBlur anchors.fill: parent source: lockBgImage - radius: 22 // Adjust blur strength as needed - transparentBorder: true + blurEnabled: true + blur: 0.48 // controls blur strength (0 to 1) + blurMax: 128 // max blur radius in pixels } - Rectangle { - anchors.fill: parent - color: Qt.rgba( - Theme.backgroundPrimary.r, - Theme.backgroundPrimary.g, - Theme.backgroundPrimary.b, 0.6) - } - // Main content container (moved up, Rectangle removed) + ColumnLayout { anchors.centerIn: parent spacing: 30 width: Math.min(parent.width * 0.8, 400) - // User avatar/icon Rectangle { Layout.alignment: Qt.AlignHCenter width: 80 @@ -174,7 +165,7 @@ WlSessionLock { anchors.margins: 4 source: Settings.settings.profileImage fillMode: Image.PreserveAspectCrop - visible: false // Only show the masked version + visible: false asynchronous: true } OpacityMask { @@ -188,7 +179,6 @@ WlSessionLock { } visible: Settings.settings.profileImage !== "" } - // Fallback icon Text { anchors.centerIn: parent text: "person" @@ -197,7 +187,6 @@ WlSessionLock { color: Theme.onAccent visible: Settings.settings.profileImage === "" } - // Glow effect layer.enabled: true layer.effect: Glow { color: Theme.accentPrimary @@ -206,7 +195,6 @@ WlSessionLock { } } - // Username Text { Layout.alignment: Qt.AlignHCenter text: Quickshell.env("USER") @@ -216,7 +204,6 @@ WlSessionLock { color: Theme.textPrimary } - // Password input container Rectangle { Layout.fillWidth: true height: 50 @@ -242,7 +229,6 @@ WlSessionLock { text: lock.password onTextChanged: lock.password = text - // Placeholder text Text { anchors.centerIn: parent text: "Enter password..." @@ -252,7 +238,6 @@ WlSessionLock { visible: !passwordInput.text && !passwordInput.activeFocus } - // Handle Enter key Keys.onPressed: function (event) { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { lock.unlockAttempt(); @@ -265,7 +250,6 @@ WlSessionLock { } } - // Error message Rectangle { id: errorMessageRect Layout.alignment: Qt.AlignHCenter @@ -274,7 +258,7 @@ WlSessionLock { color: Theme.overlay radius: 22 visible: lock.errorMessage !== "" - + Text { anchors.centerIn: parent text: lock.errorMessage @@ -286,7 +270,6 @@ WlSessionLock { } } - // Unlock button Rectangle { Layout.alignment: Qt.AlignHCenter width: 120 @@ -335,7 +318,6 @@ WlSessionLock { offsetX: screen.width / 2 + 30 offsetY: 0 anchors.top: parent.top - //anchors.horizontalCenter: parent.horizontalCenter visible: Settings.settings.showCorners z: 50 } @@ -348,11 +330,10 @@ WlSessionLock { offsetX: - Screen.width / 2 - 30 offsetY: 0 anchors.top: parent.top - //anchors.horizontalCenter: parent.horizontalCenter visible: Settings.settings.showCorners z: 51 } - + Rectangle { width: infoColumn.width + 32 height: infoColumn.height + 8 @@ -360,15 +341,14 @@ WlSessionLock { anchors.horizontalCenter: parent.horizontalCenter bottomLeftRadius: 20 bottomRightRadius: 20 - - // Top-center info panel (clock + weather) + ColumnLayout { id: infoColumn anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 0 spacing: 8 - // Clock + Text { id: timeText text: Qt.formatDateTime(new Date(), "HH:mm") @@ -389,12 +369,13 @@ WlSessionLock { horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } - // Weather info (centered, no city) + RowLayout { spacing: 6 Layout.alignment: Qt.AlignHCenter anchors.horizontalCenter: parent.horizontalCenter visible: weatherData && weatherData.current_weather + Text { text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" font.family: "Material Symbols Outlined" @@ -402,15 +383,16 @@ WlSessionLock { color: Theme.accentPrimary verticalAlignment: Text.AlignVCenter } + Text { - text: weatherData && weatherData.current_weather ? (Settings.settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.settings.useFahrenheit ? "--°F" : "--°C") + text: weatherData && weatherData.current_weather ? ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? "--°F" : "--°C") font.family: Theme.fontFamily font.pixelSize: 18 color: Theme.textSecondary verticalAlignment: Text.AlignVCenter } } - // Weather error + Text { text: weatherError color: Theme.error @@ -423,7 +405,6 @@ WlSessionLock { } } - // Update clock every second Timer { interval: 1000 running: true @@ -434,7 +415,6 @@ WlSessionLock { } } - // Update weather every 10 minutes Timer { interval: 600000 // 10 minutes running: true @@ -444,13 +424,12 @@ WlSessionLock { } } - // System control buttons (bottom right) ColumnLayout { anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 32 spacing: 12 - // Shutdown + Rectangle { width: 48 height: 48 @@ -458,6 +437,7 @@ WlSessionLock { color: shutdownArea.containsMouse ? Theme.error : "transparent" border.color: Theme.error border.width: 1 + MouseArea { id: shutdownArea anchors.fill: parent @@ -466,6 +446,7 @@ WlSessionLock { Qt.createQmlObject('import Quickshell.Io; Process { command: ["shutdown", "-h", "now"]; running: true }', lock); } } + Text { anchors.centerIn: parent text: "power_settings_new" @@ -474,7 +455,7 @@ WlSessionLock { color: shutdownArea.containsMouse ? Theme.onAccent : Theme.error } } - // Reboot + Rectangle { width: 48 height: 48 @@ -482,6 +463,7 @@ WlSessionLock { color: rebootArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary border.width: 1 + MouseArea { id: rebootArea anchors.fill: parent @@ -490,6 +472,7 @@ WlSessionLock { Qt.createQmlObject('import Quickshell.Io; Process { command: ["reboot"]; running: true }', lock); } } + Text { anchors.centerIn: parent text: "refresh" @@ -498,7 +481,7 @@ WlSessionLock { color: rebootArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } } - // Logout + Rectangle { width: 48 height: 48 @@ -506,6 +489,7 @@ WlSessionLock { color: logoutArea.containsMouse ? Theme.accentSecondary : "transparent" border.color: Theme.accentSecondary border.width: 1 + MouseArea { id: logoutArea anchors.fill: parent @@ -514,6 +498,7 @@ WlSessionLock { Qt.createQmlObject('import Quickshell.Io; Process { command: ["loginctl", "terminate-user", "' + Quickshell.env("USER") + '"]; running: true }', lock); } } + Text { anchors.centerIn: parent text: "exit_to_app" diff --git a/Widgets/Sidebar/Panel/System.qml b/Widgets/Sidebar/Panel/System.qml index fe095a6..9266958 100644 --- a/Widgets/Sidebar/Panel/System.qml +++ b/Widgets/Sidebar/Panel/System.qml @@ -155,7 +155,7 @@ Rectangle { Rectangle { width: 160 - height: 180 + height: 220 color: Theme.surface radius: 8 border.color: Theme.outline @@ -395,34 +395,54 @@ Rectangle { command: ["shutdown", "-h", "now"] running: false } + Process { id: rebootProcess command: ["reboot"] running: false } - Process { - id: logoutProcess - command: ["niri", "msg", "action", "quit", "--skip-confirmation"] - running: false - } + Process { id: suspendProcess command: ["systemctl", "suspend"] running: false } + Process { + id: logoutProcessNiri + command: ["niri", "msg", "action", "quit", "--skip-confirmation"] + running: false + } + + Process { + id: logoutProcessHyprland + command: ["hyprctl", "dispatch", "exit"] + running: false + } + + function logout() { + if (WorkspaceManager.isNiri) { + logoutProcessNiri.running = true; + } else if (WorkspaceManager.isHyprland) { + logoutProcessHyprland.running = true; + } else { + // fallback or error + console.warn("No supported compositor detected for logout"); + } + } + function suspend() { suspendProcess.running = true; } + function shutdown() { shutdownProcess.running = true; } + function reboot() { rebootProcess.running = true; } - function logout() { - logoutProcess.running = true; - } + property bool panelVisible: false @@ -453,4 +473,4 @@ Rectangle { LockScreen { id: lockScreen } -} +} \ No newline at end of file diff --git a/shell.qml b/shell.qml index 7459e94..066b599 100644 --- a/shell.qml +++ b/shell.qml @@ -26,13 +26,20 @@ Scope { return Math.round(value / step) * step; } + // Volume property reflecting current audio volume in 0-100 + // Will be kept in sync dynamically below + property int volume: (defaultAudioSink && defaultAudioSink.audio && !defaultAudioSink.audio.muted) + ? Math.round(defaultAudioSink.audio.volume * 100) + : 0 + + // Function to update volume with clamping, stepping, and applying to audio sink function updateVolume(vol) { var clamped = Math.max(0, Math.min(100, vol)); var stepped = roundToStep(clamped, 5); - volume = stepped; if (defaultAudioSink && defaultAudioSink.audio) { defaultAudioSink.audio.volume = stepped / 100; } + volume = stepped; } Component.onCompleted: { @@ -94,8 +101,8 @@ Scope { id: notificationHistoryWin } + // Reference to the default audio sink from Pipewire property var defaultAudioSink: Pipewire.defaultAudioSink - property int volume: defaultAudioSink && defaultAudioSink.audio && defaultAudioSink.audio.volume && !defaultAudioSink.audio.muted ? Math.round(defaultAudioSink.audio.volume * 100) : 0 PwObjectTracker { objects: [Pipewire.defaultAudioSink] @@ -137,4 +144,27 @@ Scope { } } } + + // --- NEW: Keep volume property in sync with actual Pipewire audio sink volume --- + + Connections { + target: defaultAudioSink.audio + onVolumeChanged: { + if (defaultAudioSink.audio && !defaultAudioSink.audio.muted) { + volume = Math.round(defaultAudioSink.audio.volume * 100); + console.log("Volume changed externally to:", volume); + } + } + onMutedChanged: { + if (defaultAudioSink.audio) { + if (defaultAudioSink.audio.muted) { + volume = 0; + console.log("Audio muted, volume set to 0"); + } else { + volume = Math.round(defaultAudioSink.audio.volume * 100); + console.log("Audio unmuted, volume restored to:", volume); + } + } + } + } }