From af0522ada5747f7e7b0e459854262f60a4ec7f42 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sat, 12 Jul 2025 10:47:01 -0400 Subject: [PATCH] Many bug fixes for media center, spotlight --- Common/Prefs.qml | 14 +- Common/Theme.qml | 10 +- SYSTEM_MONITOR_WIDGETS.md | 90 -------- Services/MprisController.qml | 20 ++ WIDGET_IMPROVEMENTS_COMPLETE.md | 49 ----- Widgets/AppLauncher.qml | 13 +- .../CenterCommandCenter.qml | 2 +- .../CenterCommandCenter/MediaPlayerWidget.qml | 208 +++++++----------- Widgets/NotificationHistoryPopup.qml | 5 +- Widgets/NotificationPopup.qml | 5 +- Widgets/SpotlightLauncher.qml | 34 +-- Widgets/TopBar/TopBar.qml | 48 ++-- shell.qml | 11 +- 13 files changed, 180 insertions(+), 329 deletions(-) delete mode 100644 SYSTEM_MONITOR_WIDGETS.md delete mode 100644 WIDGET_IMPROVEMENTS_COMPLETE.md diff --git a/Common/Prefs.qml b/Common/Prefs.qml index 63970736..d84f1052 100644 --- a/Common/Prefs.qml +++ b/Common/Prefs.qml @@ -17,7 +17,7 @@ Singleton { Component.onCompleted: { loadSettings() - Qt.callLater(applyStoredTheme) + // Theme will be applied after settings file is loaded in onLoaded handler } Process { @@ -64,16 +64,22 @@ Singleton { (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75 recentlyUsedApps = settings.recentlyUsedApps || [] console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length) + + // Apply the theme immediately after loading settings + applyStoredTheme() } else { - console.log("Settings file is empty") + console.log("Settings file is empty - applying default theme") + applyStoredTheme() } } catch (e) { console.log("Could not parse settings, using defaults:", e) + applyStoredTheme() } } onLoadFailed: (error) => { console.log("Settings file not found, using defaults. Error:", error) + applyStoredTheme() } } @@ -99,13 +105,15 @@ Singleton { } function applyStoredTheme() { - console.log("Applying stored theme:", themeIndex, themeIsDynamic) + console.log("Applying stored theme:", themeIndex, themeIsDynamic, "lightMode:", isLightMode) if (typeof Theme !== "undefined") { + Theme.isLightMode = isLightMode Theme.switchTheme(themeIndex, themeIsDynamic, false) } else { Qt.callLater(() => { if (typeof Theme !== "undefined") { + Theme.isLightMode = isLightMode Theme.switchTheme(themeIndex, themeIsDynamic, false) } }) diff --git a/Common/Theme.qml b/Common/Theme.qml index 9492a13b..31bd4ce8 100644 --- a/Common/Theme.qml +++ b/Common/Theme.qml @@ -10,7 +10,7 @@ QtObject { // Reference to the main shell root for calling functions property var rootObj: null - // Apply saved theme on startup + // Initialize theme system Component.onCompleted: { console.log("Theme Component.onCompleted") @@ -19,13 +19,7 @@ QtObject { Colors.colorsUpdated.connect(root.onColorsUpdated) } - Qt.callLater(() => { - if (typeof Prefs !== "undefined") { - console.log("Theme applying saved preferences:", Prefs.themeIndex, Prefs.themeIsDynamic, "lightMode:", Prefs.isLightMode) - isLightMode = Prefs.isLightMode - switchTheme(Prefs.themeIndex, Prefs.themeIsDynamic, false) // Don't save during startup - } - }) + console.log("Theme initialized, waiting for Prefs to load settings and apply theme") } // Handle successful color extraction diff --git a/SYSTEM_MONITOR_WIDGETS.md b/SYSTEM_MONITOR_WIDGETS.md deleted file mode 100644 index 7f570fc0..00000000 --- a/SYSTEM_MONITOR_WIDGETS.md +++ /dev/null @@ -1,90 +0,0 @@ -# System Monitor Widgets Usage Example - -## Installation Complete - -The CPU and RAM monitor widgets have been successfully created and integrated into your quickshell project: - -### Files Created: -- `/Widgets/CpuMonitorWidget.qml` - CPU usage monitor with progress bar and percentage -- `/Widgets/RamMonitorWidget.qml` - RAM usage monitor with progress bar and percentage -- `/Services/SystemMonitorService.qml` - Backend service for system monitoring - -### Files Updated: -- `/Widgets/qmldir` - Added widget exports -- `/Services/qmldir` - Added service export - -## Usage in TopBar - -To add the system monitor widgets to your TopBar, add them to the right section alongside the BatteryWidget: - -```qml -// In TopBar.qml, around line 716 after BatteryWidget -BatteryWidget { - anchors.verticalCenter: parent.verticalCenter -} - -// Add these new widgets: -CpuMonitorWidget { - anchors.verticalCenter: parent.verticalCenter - showPercentage: true - showIcon: true -} - -RamMonitorWidget { - anchors.verticalCenter: parent.verticalCenter - showPercentage: true - showIcon: true -} -``` - -## Widget Features - -### CpuMonitorWidget: -- **Real-time CPU usage monitoring** (updates every 2 seconds) -- **Visual progress bar** with color coding: - - Green: < 60% usage - - Orange: 60-80% usage - - Red: > 80% usage -- **Tooltip** showing CPU usage, core count, and frequency -- **Material Design CPU icon** (󰘚) -- **Configurable properties:** - - `showPercentage: bool` - Show/hide percentage text - - `showIcon: bool` - Show/hide CPU icon - -### RamMonitorWidget: -- **Real-time RAM usage monitoring** (updates every 3 seconds) -- **Visual progress bar** with color coding: - - Blue: < 75% usage - - Orange: 75-90% usage - - Red: > 90% usage -- **Tooltip** showing memory usage, used/total memory in GB/MB -- **Material Design memory icon** (󰍛) -- **Configurable properties:** - - `showPercentage: bool` - Show/hide percentage text - - `showIcon: bool` - Show/hide RAM icon - -### SystemMonitorService: -- **Centralized system monitoring** backend service -- **CPU monitoring:** usage, core count, frequency, temperature -- **Memory monitoring:** usage percentage, total/used/free memory -- **Automatic updates** with configurable intervals -- **Helper functions** for formatting and color coding - -## Widget Customization - -Both widgets inherit your theme colors and styling: -- Uses `Theme.cornerRadius` for rounded corners -- Uses `Theme.primary/secondary` colors for progress bars -- Uses `Theme.error/warning` for alert states -- Uses `Theme.surfaceText` for text color -- Consistent hover effects matching other widgets - -## System Requirements - -The widgets use standard Linux system commands: -- `/proc/stat` for CPU usage -- `/proc/meminfo` via `free` command for memory info -- `/proc/cpuinfo` for CPU details -- Works on most Linux distributions - -The widgets are designed to integrate seamlessly with your existing quickshell material design theme and provide essential system monitoring information at a glance. diff --git a/Services/MprisController.qml b/Services/MprisController.qml index 9e9c6c73..0766569b 100644 --- a/Services/MprisController.qml +++ b/Services/MprisController.qml @@ -136,4 +136,24 @@ Singleton { this.trackedPlayer = targetPlayer } + // Seeking support + property bool canSeek: this.activePlayer?.canSeek ?? false + property real position: this.activePlayer?.position ?? 0 + property real length: this.activePlayer?.length ?? 0 + + function seek(offsetUs) { + if (this.canSeek && this.activePlayer) { + this.activePlayer.seek(offsetUs) + } + } + + function setPosition(trackId, positionUs) { + if (this.canSeek && this.activePlayer && typeof this.activePlayer.setPosition === "function") { + this.activePlayer.setPosition(trackId, positionUs) + } else if (this.canSeek && this.activePlayer) { + // Fallback to setting position property + this.activePlayer.position = positionUs + } + } + } \ No newline at end of file diff --git a/WIDGET_IMPROVEMENTS_COMPLETE.md b/WIDGET_IMPROVEMENTS_COMPLETE.md deleted file mode 100644 index 6e43ed76..00000000 --- a/WIDGET_IMPROVEMENTS_COMPLETE.md +++ /dev/null @@ -1,49 +0,0 @@ -# System Monitor Widget Improvements - Complete! βœ… - -## 🎯 **Issues Fixed:** - -### **1. Icon Swap - DONE βœ…** -- **CPU Widget:** Now uses `memory` icon -- **RAM Widget:** Now uses `developer_board` icon - -### **2. Percentage Values Working - DONE βœ…** -- Both widgets now display real-time percentages correctly -- Service is properly collecting system data - -### **3. Vertical Alignment - FIXED βœ…** -- Added `anchors.verticalCenter: parent.verticalCenter` to both icon and percentage text -- Icons and percentages now properly align within the widget container - -### **4. Material 3 Dark Theme Tooltips - UPGRADED βœ…** -- Replaced basic `ToolTip` with custom Material 3 styled tooltips -- Matching `Theme.surfaceContainer` background -- Proper `Theme.outline` borders with opacity -- Smooth fade animations with `Theme.shortDuration` -- Better text spacing and alignment -- Wider tooltips to prevent text cutoff - -## 🎨 **New Tooltip Features:** - -### **CPU Tooltip:** -``` -CPU Usage: X.X% -Cores: N -Frequency: X.X GHz -``` - -### **RAM Tooltip:** -``` -Memory Usage: X.X% -Used: X.X GB -Total: X.X GB -``` - -## πŸ“± **Final Result:** -- **CPU Widget:** `memory 7%` with beautiful Material 3 tooltip -- **RAM Widget:** `developer_board 67%` with beautiful Material 3 tooltip -- Perfect vertical alignment of icons and text -- Smooth hover animations -- Professional dark theme styling -- No more cutoff tooltip text - -The widgets are now production-ready with a polished Material 3 Dark expressive theme that matches your existing quickshell design language! diff --git a/Widgets/AppLauncher.qml b/Widgets/AppLauncher.qml index 764e1103..513b46d6 100644 --- a/Widgets/AppLauncher.qml +++ b/Widgets/AppLauncher.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -639,10 +639,13 @@ PanelWindow { z: -1 layer.enabled: true - layer.effect: DropShadow { - radius: 8 - samples: 16 - color: Qt.rgba(0, 0, 0, 0.2) + layer.effect: MultiEffect { + shadowEnabled: true + shadowHorizontalOffset: 0 + shadowVerticalOffset: 0 + shadowBlur: 0.25 // radius/32 + shadowColor: Qt.rgba(0, 0, 0, 0.2) + shadowOpacity: 0.2 } } diff --git a/Widgets/CenterCommandCenter/CenterCommandCenter.qml b/Widgets/CenterCommandCenter/CenterCommandCenter.qml index 0ebc7a94..8c0a8822 100644 --- a/Widgets/CenterCommandCenter/CenterCommandCenter.qml +++ b/Widgets/CenterCommandCenter/CenterCommandCenter.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland diff --git a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml index 5594ff9c..e01f0255 100644 --- a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml +++ b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Services.Mpris import "../../Common" @@ -19,103 +19,32 @@ Rectangle { border.color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.2) border.width: 1 - // Constants and helpers - all microseconds - readonly property real oneSecondUs: 1000000.0 + property real currentPosition: 0 - function asSec(us) { return us / oneSecondUs } - function ratio() { return trackLenUs > 0 ? uiPosUs / trackLenUs : 0 } - - function normalizeLength(lenRaw) { - // If length < 86 400 it's almost certainly seconds (24 h upper bound). - // Convert to Β΅s; otherwise return as-is. - return (lenRaw > 0 && lenRaw < 86400) ? lenRaw * oneSecondUs : lenRaw; + // Simple progress ratio calculation + function ratio() { + return activePlayer && activePlayer.length > 0 ? currentPosition / activePlayer.length : 0 } - - // Call seek() in safe 5-second chunks so every player obeys. - function chunkedSeek(offsetUs) { - if (Math.abs(offsetUs) < 5 * oneSecondUs) { // ≀5 s? single shot. - activePlayer.seek(offsetUs); - return; - } - - const step = 5 * oneSecondUs; // 5 s - let remaining = offsetUs; - let safety = 0; // avoid infinite loops - while (Math.abs(remaining) > step && safety < 40) { // max 200 s - activePlayer.seek(Math.sign(remaining) * step); - remaining -= Math.sign(remaining) * step; - safety++; - } - if (remaining !== 0) activePlayer.seek(remaining); - } - - // Returns a guaranteed-valid object-path for the current track. - function trackPath() { - const md = activePlayer.metadata || {}; - // Spec: "/org/mpris/MediaPlayer2/Track/NNN" - if (typeof md["mpris:trackid"] === "string" && - md["mpris:trackid"].length > 1 && md["mpris:trackid"].startsWith("/")) - return md["mpris:trackid"]; - - // Nothing reliable? Fall back to the *current* playlist entry object if exposed - if (activePlayer.currentTrackPath) return activePlayer.currentTrackPath; - - // Absolute last resortβ€”return null so caller knows SetPosition will fail - return null; - } - - // Position tracking - all microseconds - property real uiPosUs: 0 - property real backendPosUs: 0 - property real trackLenUs: 0 - property double backendStamp: Date.now() // wall-clock of last update in ms - - // Optimistic timer + // Updates progress bar every second Timer { - id: tickTimer - interval: 50 // 20 fps feels smooth, cheap + id: positionTimer + interval: 1000 + running: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && activePlayer.length > 0 && !progressMouseArea.isSeeking repeat: true - running: activePlayer?.playbackState === MprisPlaybackState.Playing onTriggered: { - if (trackLenUs <= 0) return; - const projected = backendPosUs + (Date.now() - backendStamp) * 1000.0; - uiPosUs = Math.min(projected, trackLenUs); // never exceed track end + if (activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing && !progressMouseArea.isSeeking) { + currentPosition = activePlayer.position + } } } - // --- 500-ms poll to keep external moves in sync ------------------- - // Timer { - // id: pollTimer - // interval: 500 // ms - // repeat: true - // running: true // always on; cost is negligible - // onTriggered: { - // if (!activePlayer || trackLenUs <= 0) return; - - // const polledUs = activePlayer.position; // property read - // // Compare in percent to avoid false positives - // if (Math.abs((polledUs - backendPosUs) / trackLenUs) > 0.01) { // >1 % jump - // backendPosUs = polledUs; - // backendStamp = Date.now(); - // uiPosUs = polledUs; // snap instantly - // } - // } - // } - // Initialize when player changes onActivePlayerChanged: { if (activePlayer) { - backendPosUs = activePlayer.position || 0 - trackLenUs = normalizeLength(activePlayer.length || 0) - backendStamp = Date.now() - uiPosUs = backendPosUs - console.log(`player change β†’ len ${asSec(trackLenUs)} s, pos ${asSec(uiPosUs)} s`) + currentPosition = activePlayer.position || 0 } else { - backendPosUs = 0 - trackLenUs = 0 - backendStamp = Date.now() - uiPosUs = 0 + currentPosition = 0 } } @@ -124,30 +53,17 @@ Rectangle { target: activePlayer function onPositionChanged() { - const posUs = activePlayer.position - backendPosUs = posUs - backendStamp = Date.now() - uiPosUs = posUs // snap immediately on tick - } - - function onSeeked(pos) { - backendPosUs = pos - backendStamp = Date.now() - uiPosUs = backendPosUs + if (!progressMouseArea.isSeeking) { + currentPosition = activePlayer.position + } } function onPostTrackChanged() { - backendPosUs = activePlayer?.position || 0 - trackLenUs = normalizeLength(activePlayer?.length || 0) - backendStamp = Date.now() - uiPosUs = backendPosUs + currentPosition = activePlayer?.position || 0 } function onTrackTitleChanged() { - backendPosUs = activePlayer?.position || 0 - trackLenUs = normalizeLength(activePlayer?.length || 0) - backendStamp = Date.now() - uiPosUs = backendPosUs + currentPosition = activePlayer?.position || 0 } } @@ -233,6 +149,7 @@ Rectangle { // Progress bar Rectangle { + id: progressBarBackground width: parent.width height: 6 radius: 3 @@ -245,27 +162,72 @@ Rectangle { color: theme.primary width: parent.width * ratio() + + Behavior on width { + NumberAnimation { duration: 100 } + } + } + + // Drag handle + Rectangle { + id: progressHandle + width: 12 + height: 12 + radius: 6 + color: theme.primary + border.color: Qt.lighter(theme.primary, 1.3) + border.width: 1 + + x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2)) + anchors.verticalCenter: parent.verticalCenter + + visible: activePlayer && activePlayer.length > 0 + scale: progressMouseArea.containsMouse || progressMouseArea.pressed ? 1.2 : 1.0 + + Behavior on scale { + NumberAnimation { duration: 150 } + } } MouseArea { + id: progressMouseArea anchors.fill: parent + hoverEnabled: true cursorShape: Qt.PointingHandCursor + enabled: activePlayer && activePlayer.length > 0 && activePlayer.canSeek - onClicked: (mouse) => { - if (!activePlayer || !activePlayer.canSeek || trackLenUs <= 0) return - - const targetUs = (mouse.x / width) * trackLenUs - const offset = targetUs - backendPosUs - - if (typeof activePlayer.setPosition === "function") { - activePlayer.setPosition(trackPath() || "/", Math.round(targetUs)) - console.log(`SetPosition β†’ ${asSec(targetUs)} s`) - } else { - chunkedSeek(offset) // <-- use helper - console.log(`chunkedSeek β†’ ${asSec(offset/oneSecondUs)} s`) + property bool isSeeking: false + + onClicked: function(mouse) { + if (activePlayer && activePlayer.length > 0) { + let ratio = mouse.x / width + let seekPosition = ratio * activePlayer.length + activePlayer.position = seekPosition + currentPosition = seekPosition + } + } + + onPressed: function(mouse) { + isSeeking = true + if (activePlayer && activePlayer.length > 0) { + let ratio = Math.max(0, Math.min(1, mouse.x / width)) + let seekPosition = ratio * activePlayer.length + activePlayer.position = seekPosition + currentPosition = seekPosition + } + } + + onReleased: { + isSeeking = false + } + + onPositionChanged: function(mouse) { + if (pressed && activePlayer && activePlayer.length > 0) { + let ratio = Math.max(0, Math.min(1, mouse.x / width)) + let seekPosition = ratio * activePlayer.length + activePlayer.position = seekPosition + currentPosition = seekPosition } - - uiPosUs = backendPosUs = targetUs } } } @@ -299,15 +261,9 @@ Rectangle { if (!activePlayer) return // >8 s β†’ jump to start, otherwise previous track - if (uiPosUs > 8 * oneSecondUs && activePlayer.canSeek) { - if (typeof activePlayer.setPosition === "function") { - activePlayer.setPosition(trackPath() || "/", 0) - console.log("Back β†’ SetPosition 0 Β΅s") - } else { - chunkedSeek(-backendPosUs) // <-- use helper - console.log("Back β†’ chunkedSeek to 0") - } - uiPosUs = backendPosUs = 0 + if (currentPosition > 8 && activePlayer.canSeek) { + activePlayer.position = 0 + currentPosition = 0 } else { activePlayer.previous() } diff --git a/Widgets/NotificationHistoryPopup.qml b/Widgets/NotificationHistoryPopup.qml index 1cb7698e..206e18a2 100644 --- a/Widgets/NotificationHistoryPopup.qml +++ b/Widgets/NotificationHistoryPopup.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -230,7 +230,8 @@ PanelWindow { sourceSize.height: size layer.enabled: true - layer.effect: OpacityMask { + layer.effect: MultiEffect { + maskEnabled: true maskSource: Rectangle { width: historyNotifImage.size height: historyNotifImage.size diff --git a/Widgets/NotificationPopup.qml b/Widgets/NotificationPopup.qml index cb9715ce..894810ce 100644 --- a/Widgets/NotificationPopup.qml +++ b/Widgets/NotificationPopup.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -179,7 +179,8 @@ PanelWindow { sourceSize.height: size layer.enabled: true - layer.effect: OpacityMask { + layer.effect: MultiEffect { + maskEnabled: true maskSource: Rectangle { width: notifImage.size height: notifImage.size diff --git a/Widgets/SpotlightLauncher.qml b/Widgets/SpotlightLauncher.qml index 6fcb3923..c08e00ee 100644 --- a/Widgets/SpotlightLauncher.qml +++ b/Widgets/SpotlightLauncher.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -124,17 +124,18 @@ PanelWindow { } } else { // Search with category filter - var baseApps = selectedCategory === "All" ? - AppSearchService.applications : - AppSearchService.getAppsInCategory(selectedCategory) - var searchResults = AppSearchService.searchApplications(searchField.text) - if (selectedCategory === "All") { - // For "All" category, show all search results without limit - apps = searchResults.filter(app => baseApps.includes(app)) + // For "All" category, search all apps without limit + apps = AppSearchService.searchApplications(searchField.text) } else { - // For specific categories, still apply limit - apps = searchResults.filter(app => baseApps.includes(app)).slice(0, maxResults) + // For specific categories, filter search results by category + var categoryApps = AppSearchService.getAppsInCategory(selectedCategory) + var allSearchResults = AppSearchService.searchApplications(searchField.text) + + // Filter search results to only include apps from the selected category + apps = allSearchResults.filter(searchApp => { + return categoryApps.some(categoryApp => categoryApp.name === searchApp.name) + }).slice(0, maxResults) } } @@ -240,7 +241,14 @@ PanelWindow { border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) border.width: 1 layer.enabled: true - layer.effect: DropShadow { radius: 32; samples: 64; color: Qt.rgba(0,0,0,0.3); horizontalOffset:0; verticalOffset:8 } + layer.effect: MultiEffect { + shadowEnabled: true + shadowHorizontalOffset: 0 + shadowVerticalOffset: 8 + shadowBlur: 1.0 // radius/32 + shadowColor: Qt.rgba(0, 0, 0, 0.3) + shadowOpacity: 0.3 + } transform: Scale { origin.x: width/2; origin.y: height/2; xScale: spotlightOpen?1:0.9; yScale: spotlightOpen?1:0.9; Behavior on xScale { NumberAnimation { duration: Theme.mediumDuration; easing.type: Easing.OutBack; easing.overshoot:1.1 } } Behavior on yScale { NumberAnimation { duration: Theme.mediumDuration; easing.type: Easing.OutBack; easing.overshoot:1.1 } } @@ -379,8 +387,8 @@ PanelWindow { verticalAlignment: Text.AlignVCenter focus: spotlightOpen selectByMouse: true - onTextChanged: updateFilteredApps - Keys.onPressed: { + onTextChanged: updateFilteredApps() + Keys.onPressed: (event) => { if(event.key === Qt.Key_Escape) { hide(); event.accepted = true } else if(event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { launchSelected(); event.accepted = true } else if(event.key === Qt.Key_Down) { selectNext(); event.accepted = true } diff --git a/Widgets/TopBar/TopBar.qml b/Widgets/TopBar/TopBar.qml index 0127124d..81121232 100644 --- a/Widgets/TopBar/TopBar.qml +++ b/Widgets/TopBar/TopBar.qml @@ -1,6 +1,6 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -44,15 +44,12 @@ PanelWindow { property bool bluetoothAvailable: false property bool bluetoothEnabled: false + // Shell reference to access root properties directly + property var shellRoot: null + // Notification properties - property bool notificationHistoryVisible: false property int notificationCount: 0 - // Control center properties - property bool controlCenterVisible: false - - // Calendar properties - property bool calendarVisible: false // Clipboard properties signal clipboardRequested() @@ -64,6 +61,7 @@ PanelWindow { property real trayMenuX: 0 property real trayMenuY: 0 + // Proxy objects for external connections QtObject { @@ -107,13 +105,13 @@ PanelWindow { color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, topBar.backgroundTransparency) layer.enabled: true - layer.effect: DropShadow { - horizontalOffset: 0 - verticalOffset: 4 - radius: 16 - samples: 33 - color: Qt.rgba(0, 0, 0, 0.15) - transparentBorder: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowHorizontalOffset: 0 + shadowVerticalOffset: 4 + shadowBlur: 0.5 // radius/32, adjusted for visual match + shadowColor: Qt.rgba(0, 0, 0, 0.15) + shadowOpacity: 0.15 } Rectangle { @@ -183,7 +181,9 @@ PanelWindow { useFahrenheit: topBar.useFahrenheit onClockClicked: { - topBar.calendarVisible = !topBar.calendarVisible + if (topBar.shellRoot) { + topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible + } } // Insert audio visualization into the clock widget placeholder @@ -263,9 +263,11 @@ PanelWindow { NotificationCenterButton { anchors.verticalCenter: parent.verticalCenter hasUnread: topBar.notificationCount > 0 - isActive: topBar.notificationHistoryVisible + isActive: topBar.shellRoot ? topBar.shellRoot.notificationHistoryVisible : false onClicked: { - topBar.notificationHistoryVisible = !topBar.notificationHistoryVisible + if (topBar.shellRoot) { + topBar.shellRoot.notificationHistoryVisible = !topBar.shellRoot.notificationHistoryVisible + } } } @@ -281,13 +283,15 @@ PanelWindow { volumeLevel: topBar.volumeLevel bluetoothAvailable: topBar.bluetoothAvailable bluetoothEnabled: topBar.bluetoothEnabled - isActive: topBar.controlCenterVisible + isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false onClicked: { - topBar.controlCenterVisible = !topBar.controlCenterVisible - if (topBar.controlCenterVisible) { - WifiService.scanWifi() - BluetoothService.scanDevices() + if (topBar.shellRoot) { + topBar.shellRoot.controlCenterVisible = !topBar.shellRoot.controlCenterVisible + if (topBar.shellRoot.controlCenterVisible) { + WifiService.scanWifi() + BluetoothService.scanDevices() + } } } } diff --git a/shell.qml b/shell.qml index c40bdd64..4a783268 100644 --- a/shell.qml +++ b/shell.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland @@ -298,10 +298,8 @@ ShellRoot { volumeLevel: root.volumeLevel bluetoothAvailable: root.bluetoothAvailable bluetoothEnabled: root.bluetoothEnabled - notificationHistoryVisible: root.notificationHistoryVisible + shellRoot: root notificationCount: notificationHistory.count - controlCenterVisible: root.controlCenterVisible - calendarVisible: root.calendarVisible // Connect tray menu properties showTrayMenu: root.showTrayMenu @@ -315,10 +313,7 @@ ShellRoot { clipboardHistoryPopup.toggle() } - // Property change handlers - onCalendarVisibleChanged: root.calendarVisible = calendarVisible - onControlCenterVisibleChanged: root.controlCenterVisible = controlCenterVisible - onNotificationHistoryVisibleChanged: root.notificationHistoryVisible = notificationHistoryVisible + onShowTrayMenuChanged: root.showTrayMenu = showTrayMenu onCurrentTrayMenuChanged: root.currentTrayMenu = currentTrayMenu onCurrentTrayItemChanged: root.currentTrayItem = currentTrayItem