From 7a40156893a612bbaa146136035d68ed66a2f23d Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 17 Jul 2025 15:21:37 -0400 Subject: [PATCH] meta: large-scale refactor progress --- Common/Colors.qml | 18 +- Common/Theme.qml | 18 +- Services/ToastService.qml | 83 ++++++++ Services/WifiService.qml | 15 ++ Widgets/BatteryControlPopup.qml | 14 +- .../CenterCommandCenter/CalendarWidget.qml | 71 ++++--- .../CenterCommandCenter.qml | 64 +++--- Widgets/CenterCommandCenter/EventsWidget.qml | 99 +++++----- .../CenterCommandCenter/MediaPlayerWidget.qml | 77 ++++---- Widgets/CenterCommandCenter/WeatherWidget.qml | 91 +++++---- Widgets/ControlCenter/ControlCenterPopup.qml | 114 ++++++----- Widgets/ControlCenter/NetworkTab.qml | 15 +- Widgets/CpuMonitorWidget.qml | 11 +- Widgets/NotificationCenter.qml | 7 +- Widgets/PowerConfirmDialog.qml | 27 +-- Widgets/PowerMenuPopup.qml | 22 ++- Widgets/ProcessListDropdown.qml | 18 +- Widgets/RamMonitorWidget.qml | 11 +- Widgets/ThemePicker.qml | 23 +-- Widgets/ToastWidget.qml | 125 ++++++++++++ Widgets/TopBar/TopBar.qml | 79 +++----- Widgets/TrayMenuPopup.qml | 24 ++- Widgets/WifiPasswordDialog.qml | 40 ++-- shell.qml | 187 +++--------------- 24 files changed, 663 insertions(+), 590 deletions(-) create mode 100644 Services/ToastService.qml create mode 100644 Widgets/ToastWidget.qml diff --git a/Common/Colors.qml b/Common/Colors.qml index e540b118..84950171 100644 --- a/Common/Colors.qml +++ b/Common/Colors.qml @@ -2,7 +2,8 @@ pragma Singleton import QtQuick import Quickshell import Quickshell.Io -import Qt.labs.platform // ← gives us StandardPaths +import Qt.labs.platform +import qs.Services Singleton { id: root @@ -53,7 +54,8 @@ Singleton { if (!matugenAvailable) { console.warn("Matugen missing → dynamic theme disabled") - Theme.rootObj.wallpaperErrorStatus = "matugen_missing" + ToastService.wallpaperErrorStatus = "matugen_missing" + ToastService.showWarning("matugen not found - dynamic theming disabled") return } @@ -74,7 +76,8 @@ Singleton { } else { console.error("code", code) console.error("Wallpaper not found:", wallpaperPath) - Theme.rootObj.showWallpaperError() + ToastService.wallpaperErrorStatus = "error" + ToastService.showError("Wallpaper processing failed") } } } @@ -91,17 +94,20 @@ Singleton { const out = matugenCollector.text if (!out.length) { console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr) - Theme.rootObj.showWallpaperError() + ToastService.wallpaperErrorStatus = "error" + ToastService.showError("Wallpaper processing failed") return } try { root.matugenJson = out root.matugenColors = JSON.parse(out) root.colorsUpdated() - Theme.rootObj.wallpaperErrorStatus = "" + ToastService.clearWallpaperError() + ToastService.showInfo("Dynamic theme colors updated") } catch (e) { console.error("JSON parse failed:", e) - Theme.rootObj.showWallpaperError() + ToastService.wallpaperErrorStatus = "error" + ToastService.showError("Wallpaper processing failed") } } } diff --git a/Common/Theme.qml b/Common/Theme.qml index e388d1dd..8e041b66 100644 --- a/Common/Theme.qml +++ b/Common/Theme.qml @@ -7,9 +7,6 @@ import Quickshell.Io Singleton { id: root - // Reference to the main shell root for calling functions - property var rootObj: null - // Initialize theme system Component.onCompleted: { console.log("Theme Component.onCompleted") @@ -609,4 +606,19 @@ Singleton { default: return "Custom power profile" } } + + // Wallpaper IPC handler + IpcHandler { + target: "wallpaper" + + function refresh() { + console.log("Wallpaper IPC: refresh() called") + // Trigger color extraction if using dynamic theme + if (typeof Theme !== "undefined" && Theme.isDynamicTheme) { + console.log("Triggering color extraction due to wallpaper IPC") + Colors.extractColors() + } + return "WALLPAPER_REFRESH_SUCCESS" + } + } } \ No newline at end of file diff --git a/Services/ToastService.qml b/Services/ToastService.qml new file mode 100644 index 00000000..1cfeaf02 --- /dev/null +++ b/Services/ToastService.qml @@ -0,0 +1,83 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtQuick +import Quickshell + +Singleton { + id: root + + readonly property int levelInfo: 0 + readonly property int levelWarn: 1 + readonly property int levelError: 2 + + property string currentMessage: "" + property int currentLevel: levelInfo + property bool toastVisible: false + property var toastQueue: [] + + property string wallpaperErrorStatus: "" + + Timer { + id: toastTimer + interval: 5000 + running: false + repeat: false + onTriggered: hideToast() + } + + Timer { + id: queueTimer + interval: 500 + running: false + repeat: false + onTriggered: processQueue() + } + + function showToast(message, level = levelInfo) { + toastQueue.push({ message, level }) + if (!toastVisible) { + processQueue() + } + } + + function showInfo(message) { + showToast(message, levelInfo) + } + + function showWarning(message) { + showToast(message, levelWarn) + } + + function showError(message) { + showToast(message, levelError) + } + + function hideToast() { + toastVisible = false + currentMessage = "" + currentLevel = levelInfo + toastTimer.stop() + if (toastQueue.length > 0) { + queueTimer.start() + } + } + + function processQueue() { + if (toastQueue.length === 0) return + + const toast = toastQueue.shift() + currentMessage = toast.message + currentLevel = toast.level + toastVisible = true + + toastTimer.interval = + toast.level === levelError ? 8000 : + toast.level === levelWarn ? 6000 : 5000 + toastTimer.start() + } + + function clearWallpaperError() { + wallpaperErrorStatus = "" + } +} \ No newline at end of file diff --git a/Services/WifiService.qml b/Services/WifiService.qml index 2e931d8f..ea0de249 100644 --- a/Services/WifiService.qml +++ b/Services/WifiService.qml @@ -263,6 +263,21 @@ Singleton { } } + // Auto-refresh timer for when control center is open + property bool autoRefreshEnabled: false + + Timer { + id: autoRefreshTimer + interval: 20000 + running: root.autoRefreshEnabled + repeat: true + onTriggered: { + if (root.autoRefreshEnabled) { + root.scanWifi() + } + } + } + function updateCurrentWifiInfo() { console.log("Updating current WiFi info...") currentWifiInfo.running = true diff --git a/Widgets/BatteryControlPopup.qml b/Widgets/BatteryControlPopup.qml index c1a88ed4..c0c29392 100644 --- a/Widgets/BatteryControlPopup.qml +++ b/Widgets/BatteryControlPopup.qml @@ -8,9 +8,11 @@ import qs.Services import Quickshell.Services.UPower PanelWindow { - id: batteryControlPopup + id: root + + property bool batteryPopupVisible: false - visible: root.batteryPopupVisible + visible: batteryPopupVisible implicitWidth: 400 implicitHeight: 300 @@ -32,7 +34,7 @@ PanelWindow { MouseArea { anchors.fill: parent onClicked: { - root.batteryPopupVisible = false + batteryPopupVisible = false } } @@ -46,8 +48,8 @@ PanelWindow { border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 - opacity: root.batteryPopupVisible ? 1.0 : 0.0 - scale: root.batteryPopupVisible ? 1.0 : 0.85 + opacity: batteryPopupVisible ? 1.0 : 0.0 + scale: batteryPopupVisible ? 1.0 : 0.85 // Prevent click-through to background MouseArea { @@ -114,7 +116,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.batteryPopupVisible = false + batteryPopupVisible = false } } } diff --git a/Widgets/CenterCommandCenter/CalendarWidget.qml b/Widgets/CenterCommandCenter/CalendarWidget.qml index f230d15c..a847a14b 100644 --- a/Widgets/CenterCommandCenter/CalendarWidget.qml +++ b/Widgets/CenterCommandCenter/CalendarWidget.qml @@ -7,11 +7,10 @@ import qs.Services Column { id: calendarWidget - property var theme: Theme property date displayDate: new Date() property date selectedDate: new Date() - spacing: theme.spacingM + spacing: Theme.spacingM // Load events when display date changes onDisplayDateChanged: { @@ -61,16 +60,16 @@ Column { Rectangle { width: 40 height: 40 - radius: theme.cornerRadius - color: prevMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent" + radius: Theme.cornerRadius + color: prevMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" Text { anchors.centerIn: parent text: "chevron_left" - font.family: theme.iconFont - font.pixelSize: theme.iconSize - color: theme.primary - font.weight: theme.iconFontWeight + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + color: Theme.primary + font.weight: Theme.iconFontWeight } MouseArea { @@ -91,8 +90,8 @@ Column { width: parent.width - 80 height: 40 text: Qt.formatDate(displayDate, "MMMM yyyy") - font.pixelSize: theme.fontSizeLarge - color: theme.surfaceText + font.pixelSize: Theme.fontSizeLarge + color: Theme.surfaceText font.weight: Font.Medium horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -101,16 +100,16 @@ Column { Rectangle { width: 40 height: 40 - radius: theme.cornerRadius - color: nextMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent" + radius: Theme.cornerRadius + color: nextMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" Text { anchors.centerIn: parent text: "chevron_right" - font.family: theme.iconFont - font.pixelSize: theme.iconSize - color: theme.primary - font.weight: theme.iconFontWeight + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + color: Theme.primary + font.weight: Theme.iconFontWeight } MouseArea { @@ -144,8 +143,8 @@ Column { Text { anchors.centerIn: parent text: modelData - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6) + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) font.weight: Font.Medium } } @@ -183,20 +182,20 @@ Column { property bool isToday: dayDate.toDateString() === new Date().toDateString() property bool isSelected: dayDate.toDateString() === selectedDate.toDateString() - color: isSelected ? theme.primary : - isToday ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : - dayArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.08) : "transparent" + color: isSelected ? Theme.primary : + isToday ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : + dayArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent" - radius: theme.cornerRadiusSmall + radius: Theme.cornerRadiusSmall Text { anchors.centerIn: parent text: dayDate.getDate() - font.pixelSize: theme.fontSizeMedium - color: isSelected ? theme.surface : - isToday ? theme.primary : - isCurrentMonth ? theme.surfaceText : - Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.4) + font.pixelSize: Theme.fontSizeMedium + color: isSelected ? Theme.surface : + isToday ? Theme.primary : + isCurrentMonth ? Theme.surfaceText : + Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4) font.weight: isToday || isSelected ? Font.Medium : Font.Normal } @@ -215,11 +214,11 @@ Column { color: { if (isSelected) { // Use a lighter tint of primary for selected state - return Qt.lighter(theme.primary, 1.3) + return Qt.lighter(Theme.primary, 1.3) } else if (isToday) { - return theme.primary + return Theme.primary } else { - return theme.primary + return Theme.primary } } @@ -238,22 +237,22 @@ Column { Behavior on scale { NumberAnimation { - duration: theme.shortDuration - easing.type: theme.standardEasing + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } Behavior on color { ColorAnimation { - duration: theme.shortDuration - easing.type: theme.standardEasing + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } Behavior on opacity { NumberAnimation { - duration: theme.shortDuration - easing.type: theme.standardEasing + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } } diff --git a/Widgets/CenterCommandCenter/CenterCommandCenter.qml b/Widgets/CenterCommandCenter/CenterCommandCenter.qml index c21dfe01..4534f94b 100644 --- a/Widgets/CenterCommandCenter/CenterCommandCenter.qml +++ b/Widgets/CenterCommandCenter/CenterCommandCenter.qml @@ -9,12 +9,12 @@ import qs.Common import qs.Services PanelWindow { - id: centerCommandCenter + id: root - property var theme: Theme readonly property bool hasActiveMedia: MprisController.activePlayer !== null + property bool calendarVisible: false - visible: root.calendarVisible + visible: calendarVisible implicitWidth: 480 implicitHeight: 600 @@ -48,15 +48,15 @@ PanelWindow { } function calculateHeight() { - let contentHeight = theme.spacingM * 2 // margins + let contentHeight = Theme.spacingM * 2 // margins // Main row with widgets and calendar let widgetHeight = 160 // Media widget always present - widgetHeight += 140 + theme.spacingM // Weather widget always present + widgetHeight += 140 + Theme.spacingM // Weather widget always present let calendarHeight = 300 let mainRowHeight = Math.max(widgetHeight, calendarHeight) - contentHeight += mainRowHeight + theme.spacingM // Add spacing between main row and events + contentHeight += mainRowHeight + Theme.spacingM // Add events widget height - use calculated height instead of actual if (CalendarService && CalendarService.khalAvailable) { @@ -68,9 +68,9 @@ PanelWindow { return Math.min(contentHeight, parent.height * 0.9) } - color: theme.surfaceContainer - radius: theme.cornerRadiusLarge - border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08) + color: Theme.surfaceContainer + radius: Theme.cornerRadiusLarge + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 layer.enabled: true @@ -85,27 +85,27 @@ PanelWindow { Rectangle { anchors.fill: parent - color: Qt.rgba(theme.surfaceTint.r, theme.surfaceTint.g, theme.surfaceTint.b, 0.04) + color: Qt.rgba(Theme.surfaceTint.r, Theme.surfaceTint.g, Theme.surfaceTint.b, 0.04) radius: parent.radius SequentialAnimation on opacity { - running: root.calendarVisible + running: calendarVisible loops: Animation.Infinite NumberAnimation { to: 0.08 - duration: theme.extraLongDuration - easing.type: theme.standardEasing + duration: Theme.extraLongDuration + easing.type: Theme.standardEasing } NumberAnimation { to: 0.02 - duration: theme.extraLongDuration - easing.type: theme.standardEasing + duration: Theme.extraLongDuration + easing.type: Theme.standardEasing } } } - opacity: root.calendarVisible ? 1.0 : 0.0 - scale: root.calendarVisible ? 1.0 : 0.92 + opacity: calendarVisible ? 1.0 : 0.0 + scale: calendarVisible ? 1.0 : 0.92 // Update height when calendar service events change Connections { @@ -130,47 +130,47 @@ PanelWindow { Behavior on opacity { NumberAnimation { - duration: theme.longDuration - easing.type: theme.emphasizedEasing + duration: Theme.longDuration + easing.type: Theme.emphasizedEasing } } Behavior on scale { NumberAnimation { - duration: theme.longDuration - easing.type: theme.emphasizedEasing + duration: Theme.longDuration + easing.type: Theme.emphasizedEasing } } Behavior on height { NumberAnimation { - duration: theme.mediumDuration - easing.type: theme.standardEasing + duration: Theme.mediumDuration + easing.type: Theme.standardEasing } } Column { anchors.fill: parent - anchors.margins: theme.spacingM - spacing: theme.spacingM + anchors.margins: Theme.spacingM + spacing: Theme.spacingM // Main row with widgets and calendar Row { width: parent.width height: { let widgetHeight = 160 // Media widget always present - widgetHeight += 140 + theme.spacingM // Weather widget always present + widgetHeight += 140 + Theme.spacingM // Weather widget always present let calendarHeight = 300 return Math.max(widgetHeight, calendarHeight) } - spacing: theme.spacingM + spacing: Theme.spacingM // Left section for widgets Column { id: leftWidgets width: hasAnyWidgets ? parent.width * 0.45 : 0 height: childrenRect.height - spacing: theme.spacingM + spacing: Theme.spacingM visible: hasAnyWidgets anchors.top: parent.top @@ -180,23 +180,20 @@ PanelWindow { visible: true // Always visible - shows placeholder when no media width: parent.width height: 160 - theme: centerCommandCenter.theme } WeatherWidget { visible: true // Always visible - shows placeholder when no weather width: parent.width height: 140 - theme: centerCommandCenter.theme } } // Right section for calendar CalendarWidget { id: calendarWidget - width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - theme.spacingL : parent.width + width: leftWidgets.hasAnyWidgets ? parent.width * 0.55 - Theme.spacingL : parent.width height: parent.height - theme: centerCommandCenter.theme } } @@ -204,7 +201,6 @@ PanelWindow { EventsWidget { id: eventsWidget width: parent.width - theme: centerCommandCenter.theme selectedDate: calendarWidget.selectedDate } @@ -215,7 +211,7 @@ PanelWindow { anchors.fill: parent z: -1 onClicked: { - root.calendarVisible = false + calendarVisible = false } } } \ No newline at end of file diff --git a/Widgets/CenterCommandCenter/EventsWidget.qml b/Widgets/CenterCommandCenter/EventsWidget.qml index 79cb0b7c..995a200c 100644 --- a/Widgets/CenterCommandCenter/EventsWidget.qml +++ b/Widgets/CenterCommandCenter/EventsWidget.qml @@ -8,7 +8,6 @@ import qs.Services Rectangle { id: eventsWidget - property var theme: Theme property date selectedDate: new Date() property var selectedDateEvents: [] property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0 @@ -21,9 +20,9 @@ Rectangle { width: parent.width height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0 - radius: theme.cornerRadiusLarge - color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) - border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08) + radius: Theme.cornerRadiusLarge + color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 visible: shouldShow @@ -40,8 +39,8 @@ Rectangle { Behavior on height { NumberAnimation { - duration: theme.mediumDuration - easing.type: theme.emphasizedEasing + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } @@ -81,14 +80,14 @@ Rectangle { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - anchors.margins: theme.spacingL - spacing: theme.spacingS + anchors.margins: Theme.spacingL + spacing: Theme.spacingS Text { text: "event" - font.family: theme.iconFont - font.pixelSize: theme.iconSize - 2 - color: theme.primary + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize - 2 + color: Theme.primary anchors.verticalCenter: parent.verticalCenter } @@ -97,8 +96,8 @@ Rectangle { (Qt.formatDate(selectedDate, "MMM d") + " • " + (selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) : Qt.formatDate(selectedDate, "MMM d") - font.pixelSize: theme.fontSizeMedium - color: theme.surfaceText + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } @@ -107,21 +106,21 @@ Rectangle { // No events placeholder - centered in entire widget (not just content area) Column { anchors.centerIn: parent - spacing: theme.spacingXS + spacing: Theme.spacingXS visible: !hasEvents Text { text: "event_busy" - font.family: theme.iconFont - font.pixelSize: theme.iconSize + 8 - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.3) + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + 8 + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3) anchors.horizontalCenter: parent.horizontalCenter } Text { text: "No events" - font.pixelSize: theme.fontSizeMedium - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) + font.pixelSize: Theme.fontSizeMedium + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5) font.weight: Font.Normal anchors.horizontalCenter: parent.horizontalCenter } @@ -134,12 +133,12 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.margins: theme.spacingL - anchors.topMargin: theme.spacingM + anchors.margins: Theme.spacingL + anchors.topMargin: Theme.spacingM visible: opacity > 0 opacity: hasEvents ? 1.0 : 0.0 clip: true - spacing: theme.spacingS + spacing: Theme.spacingS boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds @@ -149,28 +148,28 @@ Rectangle { Behavior on opacity { NumberAnimation { - duration: theme.mediumDuration - easing.type: theme.emphasizedEasing + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } delegate: Rectangle { width: eventsList.width - height: eventContent.implicitHeight + theme.spacingM - radius: theme.cornerRadius + height: eventContent.implicitHeight + Theme.spacingM + radius: Theme.cornerRadius color: { if (modelData.url && eventMouseArea.containsMouse) { - return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) + return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) } else if (eventMouseArea.containsMouse) { - return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.06) + return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) } - return Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.06) + return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.06) } border.color: { if (modelData.url && eventMouseArea.containsMouse) { - return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.3) + return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) } else if (eventMouseArea.containsMouse) { - return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.15) + return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) } return "transparent" } @@ -184,7 +183,7 @@ Rectangle { anchors.leftMargin: 4 anchors.verticalCenter: parent.verticalCenter radius: 2 - color: theme.primary + color: Theme.primary opacity: 0.8 } @@ -193,15 +192,15 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: theme.spacingL + 4 - anchors.rightMargin: theme.spacingM + anchors.leftMargin: Theme.spacingL + 4 + anchors.rightMargin: Theme.spacingM spacing: 6 Text { width: parent.width text: modelData.title - font.pixelSize: theme.fontSizeMedium - color: theme.surfaceText + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText font.weight: Font.Medium elide: Text.ElideRight wrapMode: Text.Wrap @@ -220,9 +219,9 @@ Rectangle { Text { text: "schedule" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) anchors.verticalCenter: parent.verticalCenter } @@ -240,8 +239,8 @@ Rectangle { return startTime } } - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) font.weight: Font.Normal anchors.verticalCenter: parent.verticalCenter } @@ -256,16 +255,16 @@ Rectangle { Text { text: "location_on" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) anchors.verticalCenter: parent.verticalCenter } Text { text: modelData.location - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) elide: Text.ElideRight anchors.verticalCenter: parent.verticalCenter maximumLineCount: 1 @@ -293,15 +292,15 @@ Rectangle { Behavior on color { ColorAnimation { - duration: theme.shortDuration - easing.type: theme.standardEasing + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } Behavior on border.color { ColorAnimation { - duration: theme.shortDuration - easing.type: theme.standardEasing + duration: Theme.shortDuration + easing.type: Theme.standardEasing } } } diff --git a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml index 70266e2d..946edea8 100644 --- a/Widgets/CenterCommandCenter/MediaPlayerWidget.qml +++ b/Widgets/CenterCommandCenter/MediaPlayerWidget.qml @@ -10,7 +10,6 @@ Rectangle { id: mediaPlayerWidget property MprisPlayer activePlayer: MprisController.activePlayer - property var theme: Theme property string lastValidTitle: "" property string lastValidArtist: "" @@ -40,9 +39,9 @@ Rectangle { width: parent.width height: parent.height - radius: theme.cornerRadiusLarge - color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.4) - border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08) + radius: Theme.cornerRadiusLarge + color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4) + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 layer.enabled: true @@ -96,26 +95,26 @@ Rectangle { Item { anchors.fill: parent - anchors.margins: theme.spacingS + anchors.margins: Theme.spacingS // Placeholder when no media - centered in entire widget Column { anchors.centerIn: parent - spacing: theme.spacingS + spacing: Theme.spacingS visible: (!activePlayer && !lastValidTitle) || (activePlayer && activePlayer.trackTitle === "" && lastValidTitle === "") Text { text: "music_note" - font.family: theme.iconFont - font.pixelSize: theme.iconSize + 8 - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + 8 + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5) anchors.horizontalCenter: parent.horizontalCenter } Text { text: "No Media Playing" - font.pixelSize: theme.fontSizeMedium - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.pixelSize: Theme.fontSizeMedium + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) anchors.horizontalCenter: parent.horizontalCenter } } @@ -123,21 +122,21 @@ Rectangle { // Active content in a column Column { anchors.fill: parent - spacing: theme.spacingS + spacing: Theme.spacingS visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== "" // Normal media info when playing Row { width: parent.width height: 60 - spacing: theme.spacingM + spacing: Theme.spacingM // Album Art Rectangle { width: 60 height: 60 - radius: theme.cornerRadius - color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3) + radius: Theme.cornerRadius + color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) Item { anchors.fill: parent @@ -164,9 +163,9 @@ Rectangle { Text { anchors.centerIn: parent text: "album" - font.family: theme.iconFont + font.family: Theme.iconFont font.pixelSize: 28 - color: theme.surfaceVariantText + color: Theme.surfaceVariantText } } } @@ -174,9 +173,9 @@ Rectangle { // Track Info Column { - width: parent.width - 60 - theme.spacingM + width: parent.width - 60 - Theme.spacingM height: parent.height - spacing: theme.spacingXS + spacing: Theme.spacingXS Text { text: activePlayer?.trackTitle || lastValidTitle || "Unknown Track" @@ -185,9 +184,9 @@ Rectangle { lastValidTitle = activePlayer.trackTitle; } } - font.pixelSize: theme.fontSizeMedium + font.pixelSize: Theme.fontSizeMedium font.weight: Font.Bold - color: theme.surfaceText + color: Theme.surfaceText width: parent.width elide: Text.ElideRight } @@ -199,8 +198,8 @@ Rectangle { lastValidArtist = activePlayer.trackArtist; } } - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.8) + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8) width: parent.width elide: Text.ElideRight } @@ -212,8 +211,8 @@ Rectangle { lastValidAlbum = activePlayer.trackAlbum; } } - font.pixelSize: theme.fontSizeSmall - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6) + font.pixelSize: Theme.fontSizeSmall + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6) width: parent.width elide: Text.ElideRight visible: text.length > 0 @@ -232,7 +231,7 @@ Rectangle { width: parent.width height: 6 radius: 3 - color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3) + color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) visible: activePlayer !== null anchors.verticalCenter: parent.verticalCenter @@ -240,7 +239,7 @@ Rectangle { id: progressFill height: parent.height radius: parent.radius - color: theme.primary + color: Theme.primary width: parent.width * ratio() @@ -255,8 +254,8 @@ Rectangle { width: 12 height: 12 radius: 6 - color: theme.primary - border.color: Qt.lighter(theme.primary, 1.3) + 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)) @@ -346,7 +345,7 @@ Rectangle { Row { anchors.horizontalCenter: parent.horizontalCenter - spacing: theme.spacingM + spacing: Theme.spacingM height: parent.height // Previous button @@ -354,14 +353,14 @@ Rectangle { width: 28 height: 28 radius: 14 - color: prevBtnArea.containsMouse ? Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) : "transparent" + color: prevBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent" Text { anchors.centerIn: parent text: "skip_previous" - font.family: theme.iconFont + font.family: Theme.iconFont font.pixelSize: 16 - color: theme.surfaceText + color: Theme.surfaceText } MouseArea { @@ -388,14 +387,14 @@ Rectangle { width: 32 height: 32 radius: 16 - color: theme.primary + color: Theme.primary Text { anchors.centerIn: parent text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow" - font.family: theme.iconFont + font.family: Theme.iconFont font.pixelSize: 20 - color: theme.background + color: Theme.background } MouseArea { @@ -411,14 +410,14 @@ Rectangle { width: 28 height: 28 radius: 14 - color: nextBtnArea.containsMouse ? Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) : "transparent" + color: nextBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent" Text { anchors.centerIn: parent text: "skip_next" - font.family: theme.iconFont + font.family: Theme.iconFont font.pixelSize: 16 - color: theme.surfaceText + color: Theme.surfaceText } MouseArea { diff --git a/Widgets/CenterCommandCenter/WeatherWidget.qml b/Widgets/CenterCommandCenter/WeatherWidget.qml index 65adbaeb..f5a7997a 100644 --- a/Widgets/CenterCommandCenter/WeatherWidget.qml +++ b/Widgets/CenterCommandCenter/WeatherWidget.qml @@ -7,13 +7,12 @@ import qs.Services Rectangle { id: weatherWidget - property var theme: Theme width: parent.width height: parent.height - radius: theme.cornerRadiusLarge - color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.4) - border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08) + radius: Theme.cornerRadiusLarge + color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.4) + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 layer.enabled: true @@ -29,21 +28,21 @@ Rectangle { // Placeholder when no weather - centered in entire widget Column { anchors.centerIn: parent - spacing: theme.spacingS + spacing: Theme.spacingS visible: !WeatherService.weather.available || WeatherService.weather.temp === 0 Text { text: "cloud_off" - font.family: theme.iconFont - font.pixelSize: theme.iconSize + 8 - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + 8 + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5) anchors.horizontalCenter: parent.horizontalCenter } Text { text: "No Weather Data" - font.pixelSize: theme.fontSizeMedium - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.pixelSize: Theme.fontSizeMedium + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) anchors.horizontalCenter: parent.horizontalCenter } } @@ -51,8 +50,8 @@ Rectangle { // Weather content when available - original Column structure Column { anchors.fill: parent - anchors.margins: theme.spacingL - spacing: theme.spacingS + anchors.margins: Theme.spacingL + spacing: Theme.spacingS visible: WeatherService.weather.available && WeatherService.weather.temp !== 0 // Weather header info @@ -62,25 +61,25 @@ Rectangle { Row { anchors.centerIn: parent - spacing: theme.spacingL + spacing: Theme.spacingL // Weather icon Text { text: WeatherService.getWeatherIcon(WeatherService.weather.wCode) - font.family: theme.iconFont - font.pixelSize: theme.iconSize + 8 - color: theme.primary + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + 8 + color: Theme.primary anchors.verticalCenter: parent.verticalCenter } Column { - spacing: theme.spacingXS + spacing: Theme.spacingXS anchors.verticalCenter: parent.verticalCenter Text { text: (Prefs.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C") - font.pixelSize: theme.fontSizeXLarge - color: theme.surfaceText + font.pixelSize: Theme.fontSizeXLarge + color: Theme.surfaceText font.weight: Font.Light MouseArea { @@ -94,8 +93,8 @@ Rectangle { Text { text: WeatherService.weather.city || "" - font.pixelSize: theme.fontSizeMedium - color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) + font.pixelSize: Theme.fontSizeMedium + color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) visible: text.length > 0 } } @@ -105,73 +104,73 @@ Rectangle { // Weather details grid Grid { columns: 2 - spacing: theme.spacingM + spacing: Theme.spacingM anchors.horizontalCenter: parent.horizontalCenter Row { - spacing: theme.spacingXS + spacing: Theme.spacingXS Text { text: "humidity_low" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--" - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } Row { - spacing: theme.spacingXS + spacing: Theme.spacingXS Text { text: "air" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: WeatherService.weather.wind || "--" - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } Row { - spacing: theme.spacingXS + spacing: Theme.spacingXS Text { text: "wb_twilight" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: WeatherService.weather.sunrise || "--" - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } Row { - spacing: theme.spacingXS + spacing: Theme.spacingXS Text { text: "bedtime" - font.family: theme.iconFont - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.family: Theme.iconFont + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: WeatherService.weather.sunset || "--" - font.pixelSize: theme.fontSizeSmall - color: theme.surfaceText + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } } diff --git a/Widgets/ControlCenter/ControlCenterPopup.qml b/Widgets/ControlCenter/ControlCenterPopup.qml index 4bb11979..8c772ced 100644 --- a/Widgets/ControlCenter/ControlCenterPopup.qml +++ b/Widgets/ControlCenter/ControlCenterPopup.qml @@ -9,9 +9,16 @@ import qs.Common import qs.Services PanelWindow { - id: controlCenterPopup + id: root + + property bool controlCenterVisible: false - visible: root.controlCenterVisible + visible: controlCenterVisible + + onVisibleChanged: { + // Enable/disable WiFi auto-refresh based on control center visibility + WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled + } implicitWidth: 600 implicitHeight: 500 @@ -34,7 +41,7 @@ PanelWindow { Rectangle { width: Math.min(600, Screen.width - Theme.spacingL * 2) - height: controlCenterPopup.powerOptionsExpanded ? 570 : 500 + height: root.powerOptionsExpanded ? 570 : 500 x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL) y: Theme.barHeight + Theme.spacingXS color: Theme.popupBackground() @@ -46,15 +53,15 @@ PanelWindow { transform: [ Scale { id: scaleTransform - origin.x: parent.width // Scale from top-right corner + origin.x: 600 // Use fixed width since popup is max 600px wide origin.y: 0 - xScale: root.controlCenterVisible ? 1.0 : 0.95 - yScale: root.controlCenterVisible ? 1.0 : 0.8 + xScale: controlCenterVisible ? 1.0 : 0.95 + yScale: controlCenterVisible ? 1.0 : 0.8 }, Translate { id: translateTransform - x: root.controlCenterVisible ? 0 : 15 // Slide slightly left when hidden - y: root.controlCenterVisible ? 0 : -30 + x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden + y: controlCenterVisible ? 0 : -30 } ] @@ -62,13 +69,13 @@ PanelWindow { states: [ State { name: "visible" - when: root.controlCenterVisible + when: controlCenterVisible PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 } PropertyChanges { target: translateTransform; x: 0; y: 0 } }, State { name: "hidden" - when: !root.controlCenterVisible + when: !controlCenterVisible PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 } PropertyChanges { target: translateTransform; x: 15; y: -30 } } @@ -96,7 +103,7 @@ PanelWindow { } ] - opacity: root.controlCenterVisible ? 1.0 : 0.0 + opacity: controlCenterVisible ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { @@ -253,7 +260,7 @@ PanelWindow { width: 40 height: 40 radius: 20 - color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ? + color: powerButton.containsMouse || root.powerOptionsExpanded ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) @@ -267,10 +274,10 @@ PanelWindow { Text { anchors.centerIn: parent - text: controlCenterPopup.powerOptionsExpanded ? "expand_less" : "power_settings_new" + text: root.powerOptionsExpanded ? "expand_less" : "power_settings_new" font.family: Theme.iconFont font.pixelSize: Theme.iconSize - 2 - color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ? Theme.error : Theme.surfaceText + color: powerButton.containsMouse || root.powerOptionsExpanded ? Theme.error : Theme.surfaceText Behavior on text { // Smooth icon transition @@ -302,7 +309,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - controlCenterPopup.powerOptionsExpanded = !controlCenterPopup.powerOptionsExpanded + root.powerOptionsExpanded = !root.powerOptionsExpanded } } @@ -338,8 +345,8 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - root.controlCenterVisible = false - root.settingsVisible = true + controlCenterVisible = false + settingsPopup.settingsVisible = true } } @@ -356,12 +363,12 @@ PanelWindow { // Animated Collapsible Power Options (optimized) Rectangle { width: parent.width - height: controlCenterPopup.powerOptionsExpanded ? 60 : 0 + height: root.powerOptionsExpanded ? 60 : 0 radius: Theme.cornerRadius color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) - border.width: controlCenterPopup.powerOptionsExpanded ? 1 : 0 - opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0 + border.width: root.powerOptionsExpanded ? 1 : 0 + opacity: root.powerOptionsExpanded ? 1.0 : 0.0 clip: true // Single coordinated animation for power options @@ -382,7 +389,7 @@ PanelWindow { Row { anchors.centerIn: parent spacing: Theme.spacingL - visible: controlCenterPopup.powerOptionsExpanded + visible: root.powerOptionsExpanded // Logout Rectangle { @@ -421,11 +428,13 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - controlCenterPopup.powerOptionsExpanded = false - root.powerConfirmAction = "logout" - root.powerConfirmTitle = "Logout" - root.powerConfirmMessage = "Are you sure you want to logout?" - root.powerConfirmVisible = true + root.powerOptionsExpanded = false + if (typeof root !== "undefined" && root.powerConfirmDialog) { + root.powerConfirmDialog.powerConfirmAction = "logout" + root.powerConfirmDialog.powerConfirmTitle = "Logout" + root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?" + root.powerConfirmDialog.powerConfirmVisible = true + } } } @@ -474,11 +483,13 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - controlCenterPopup.powerOptionsExpanded = false - root.powerConfirmAction = "reboot" - root.powerConfirmTitle = "Restart" - root.powerConfirmMessage = "Are you sure you want to restart?" - root.powerConfirmVisible = true + root.powerOptionsExpanded = false + if (typeof root !== "undefined" && root.powerConfirmDialog) { + root.powerConfirmDialog.powerConfirmAction = "reboot" + root.powerConfirmDialog.powerConfirmTitle = "Restart" + root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?" + root.powerConfirmDialog.powerConfirmVisible = true + } } } @@ -527,11 +538,13 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - controlCenterPopup.powerOptionsExpanded = false - root.powerConfirmAction = "poweroff" - root.powerConfirmTitle = "Shutdown" - root.powerConfirmMessage = "Are you sure you want to shutdown?" - root.powerConfirmVisible = true + root.powerOptionsExpanded = false + if (typeof root !== "undefined" && root.powerConfirmDialog) { + root.powerConfirmDialog.powerConfirmAction = "poweroff" + root.powerConfirmDialog.powerConfirmTitle = "Shutdown" + root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to shutdown?" + root.powerConfirmDialog.powerConfirmVisible = true + } } } @@ -579,7 +592,7 @@ PanelWindow { width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount height: 40 radius: Theme.cornerRadius - color: controlCenterPopup.currentTab === modelData.id ? + color: root.currentTab === modelData.id ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent" @@ -591,15 +604,15 @@ PanelWindow { text: modelData.icon font.family: Theme.iconFont font.pixelSize: Theme.iconSize - 4 - color: controlCenterPopup.currentTab === modelData.id ? Theme.primary : Theme.surfaceText + color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: modelData.name font.pixelSize: Theme.fontSizeSmall - color: controlCenterPopup.currentTab === modelData.id ? Theme.primary : Theme.surfaceText - font.weight: controlCenterPopup.currentTab === modelData.id ? Font.Medium : Font.Normal + color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText + font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal anchors.verticalCenter: parent.verticalCenter } } @@ -611,7 +624,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - controlCenterPopup.currentTab = modelData.id + root.currentTab = modelData.id } } @@ -629,7 +642,7 @@ PanelWindow { // Tab content area Rectangle { width: parent.width - height: controlCenterPopup.powerOptionsExpanded ? 240 : 300 + height: root.powerOptionsExpanded ? 240 : 300 radius: Theme.cornerRadius color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1) @@ -644,36 +657,29 @@ PanelWindow { NetworkTab { anchors.fill: parent anchors.margins: Theme.spacingM - visible: controlCenterPopup.currentTab === "network" + visible: root.currentTab === "network" - wifiPasswordSSID: root.wifiPasswordSSID - wifiPasswordInput: root.wifiPasswordInput - wifiPasswordDialogVisible: root.wifiPasswordDialogVisible - - onWifiAutoRefreshEnabledChanged: { - root.wifiAutoRefreshEnabled = wifiAutoRefreshEnabled - } } // Audio Tab AudioTab { anchors.fill: parent anchors.margins: Theme.spacingM - visible: controlCenterPopup.currentTab === "audio" + visible: root.currentTab === "audio" } // Bluetooth Tab BluetoothTab { anchors.fill: parent anchors.margins: Theme.spacingM - visible: BluetoothService.available && controlCenterPopup.currentTab === "bluetooth" + visible: BluetoothService.available && root.currentTab === "bluetooth" } // Display Tab DisplayTab { anchors.fill: parent anchors.margins: Theme.spacingM - visible: controlCenterPopup.currentTab === "display" + visible: root.currentTab === "display" } } @@ -685,7 +691,7 @@ PanelWindow { anchors.fill: parent z: -1 onClicked: { - root.controlCenterVisible = false + controlCenterVisible = false } } } \ No newline at end of file diff --git a/Widgets/ControlCenter/NetworkTab.qml b/Widgets/ControlCenter/NetworkTab.qml index 6c2c1dd7..9330e317 100644 --- a/Widgets/ControlCenter/NetworkTab.qml +++ b/Widgets/ControlCenter/NetworkTab.qml @@ -16,11 +16,6 @@ Item { else return 1 // Default to WiFi when nothing is connected } - // Expose properties that the parent needs to bind to - property bool wifiAutoRefreshEnabled: false - property string wifiPasswordSSID: "" - property string wifiPasswordInput: "" - property bool wifiPasswordDialogVisible: false Column { anchors.fill: parent @@ -67,7 +62,7 @@ Item { cursorShape: Qt.PointingHandCursor onClicked: { networkTab.networkSubTab = 0 - networkTab.wifiAutoRefreshEnabled = false + WifiService.autoRefreshEnabled = false } } } @@ -108,7 +103,7 @@ Item { cursorShape: Qt.PointingHandCursor onClicked: { networkTab.networkSubTab = 1 - networkTab.wifiAutoRefreshEnabled = true + WifiService.autoRefreshEnabled = true if (NetworkService.wifiEnabled) { WifiService.scanWifi() } @@ -795,9 +790,9 @@ Item { WifiService.connectToWifi(modelData.ssid) } else if (modelData.secured) { // Secured network, need password - use root dialog - root.wifiPasswordSSID = modelData.ssid - root.wifiPasswordInput = "" - root.wifiPasswordDialogVisible = true + wifiPasswordDialog.wifiPasswordSSID = modelData.ssid + wifiPasswordDialog.wifiPasswordInput = "" + wifiPasswordDialog.wifiPasswordDialogVisible = true } else { // Open network, connect directly WifiService.connectToWifi(modelData.ssid) diff --git a/Widgets/CpuMonitorWidget.qml b/Widgets/CpuMonitorWidget.qml index 64400b2f..e4161fbe 100644 --- a/Widgets/CpuMonitorWidget.qml +++ b/Widgets/CpuMonitorWidget.qml @@ -9,7 +9,6 @@ Rectangle { property bool showPercentage: true property bool showIcon: true - property var processDropdown: null width: 55 height: 30 @@ -18,10 +17,6 @@ Rectangle { Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08) - Component.onCompleted: { - // CPU widget initialized - } - MouseArea { id: cpuArea anchors.fill: parent @@ -29,10 +24,8 @@ Rectangle { cursorShape: Qt.PointingHandCursor onClicked: { - if (processDropdown) { - ProcessMonitorService.setSortBy("cpu") - processDropdown.toggle() - } + ProcessMonitorService.setSortBy("cpu") + processListDropdown.toggle() } } diff --git a/Widgets/NotificationCenter.qml b/Widgets/NotificationCenter.qml index d7e1e86a..2fe0409c 100644 --- a/Widgets/NotificationCenter.qml +++ b/Widgets/NotificationCenter.qml @@ -8,10 +8,9 @@ import qs.Common import qs.Services PanelWindow { - id: notificationHistoryPopup + id: root property bool notificationHistoryVisible: false - signal closeRequested() visible: notificationHistoryVisible @@ -35,7 +34,7 @@ PanelWindow { MouseArea { anchors.fill: parent onClicked: { - closeRequested() + notificationHistoryVisible = false } } @@ -53,7 +52,7 @@ PanelWindow { transform: [ Scale { id: scaleTransform - origin.x: parent.width + origin.x: 400 // Use fixed width since popup is 400px wide origin.y: 0 xScale: notificationHistoryVisible ? 1.0 : 0.95 yScale: notificationHistoryVisible ? 1.0 : 0.8 diff --git a/Widgets/PowerConfirmDialog.qml b/Widgets/PowerConfirmDialog.qml index 9c838626..939360ee 100644 --- a/Widgets/PowerConfirmDialog.qml +++ b/Widgets/PowerConfirmDialog.qml @@ -7,9 +7,14 @@ import Quickshell.Io import qs.Common PanelWindow { - id: powerConfirmDialog + id: root - visible: root.powerConfirmVisible + property bool powerConfirmVisible: false + property string powerConfirmAction: "" + property string powerConfirmTitle: "" + property string powerConfirmMessage: "" + + visible: powerConfirmVisible implicitWidth: 400 implicitHeight: 300 @@ -43,8 +48,8 @@ PanelWindow { border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: 1 - opacity: root.powerConfirmVisible ? 1.0 : 0.0 - scale: root.powerConfirmVisible ? 1.0 : 0.9 + opacity: powerConfirmVisible ? 1.0 : 0.0 + scale: powerConfirmVisible ? 1.0 : 0.9 Behavior on opacity { NumberAnimation { @@ -67,10 +72,10 @@ PanelWindow { // Title Text { - text: root.powerConfirmTitle + text: powerConfirmTitle font.pixelSize: Theme.fontSizeLarge color: { - switch(root.powerConfirmAction) { + switch(powerConfirmAction) { case "poweroff": return Theme.error case "reboot": return Theme.warning default: return Theme.surfaceText @@ -83,7 +88,7 @@ PanelWindow { // Message Text { - text: root.powerConfirmMessage + text: powerConfirmMessage font.pixelSize: Theme.fontSizeMedium color: Theme.surfaceText width: parent.width @@ -119,7 +124,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerConfirmVisible = false + powerConfirmVisible = false } } } @@ -131,7 +136,7 @@ PanelWindow { radius: Theme.cornerRadius color: { let baseColor - switch(root.powerConfirmAction) { + switch(powerConfirmAction) { case "poweroff": baseColor = Theme.error; break case "reboot": baseColor = Theme.warning; break default: baseColor = Theme.primary; break @@ -155,8 +160,8 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerConfirmVisible = false - executePowerAction(root.powerConfirmAction) + powerConfirmVisible = false + executePowerAction(powerConfirmAction) } } } diff --git a/Widgets/PowerMenuPopup.qml b/Widgets/PowerMenuPopup.qml index e2f21e0d..7729cef8 100644 --- a/Widgets/PowerMenuPopup.qml +++ b/Widgets/PowerMenuPopup.qml @@ -7,9 +7,11 @@ import Quickshell.Io import qs.Common PanelWindow { - id: powerMenuPopup + id: root - visible: root.powerMenuVisible + property bool powerMenuVisible: false + + visible: powerMenuVisible implicitWidth: 400 implicitHeight: 320 @@ -31,7 +33,7 @@ PanelWindow { MouseArea { anchors.fill: parent onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false } } @@ -45,8 +47,8 @@ PanelWindow { border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 - opacity: root.powerMenuVisible ? 1.0 : 0.0 - scale: root.powerMenuVisible ? 1.0 : 0.85 + opacity: powerMenuVisible ? 1.0 : 0.0 + scale: powerMenuVisible ? 1.0 : 0.85 Behavior on opacity { NumberAnimation { @@ -109,7 +111,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false } } } @@ -156,7 +158,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false root.powerConfirmAction = "logout" root.powerConfirmTitle = "Log Out" root.powerConfirmMessage = "Are you sure you want to log out?" @@ -201,7 +203,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false root.powerConfirmAction = "suspend" root.powerConfirmTitle = "Suspend" root.powerConfirmMessage = "Are you sure you want to suspend the system?" @@ -246,7 +248,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false root.powerConfirmAction = "reboot" root.powerConfirmTitle = "Reboot" root.powerConfirmMessage = "Are you sure you want to reboot the system?" @@ -291,7 +293,7 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.powerMenuVisible = false + powerMenuVisible = false root.powerConfirmAction = "poweroff" root.powerConfirmTitle = "Power Off" root.powerConfirmMessage = "Are you sure you want to power off the system?" diff --git a/Widgets/ProcessListDropdown.qml b/Widgets/ProcessListDropdown.qml index 9a3a8fb4..9b16f15a 100644 --- a/Widgets/ProcessListDropdown.qml +++ b/Widgets/ProcessListDropdown.qml @@ -10,7 +10,7 @@ import qs.Common import qs.Services PanelWindow { - id: processDropdown + id: processListDropdown property bool isVisible: false property var parentWidget: null @@ -41,7 +41,7 @@ PanelWindow { // Click outside to close MouseArea { anchors.fill: parent - onClicked: processDropdown.hide() + onClicked: processListDropdown.hide() } Rectangle { @@ -63,9 +63,9 @@ PanelWindow { id: scaleTransform origin.x: parent.width * 0.85 // Scale from top-right origin.y: 0 - xScale: processDropdown.isVisible ? 1.0 : 0.95 - yScale: processDropdown.isVisible ? 1.0 : 0.8 - + xScale: processListDropdown.isVisible ? 1.0 : 0.95 + yScale: processListDropdown.isVisible ? 1.0 : 0.8 + Behavior on xScale { NumberAnimation { duration: Theme.mediumDuration @@ -82,8 +82,8 @@ PanelWindow { }, Translate { id: translateTransform - x: processDropdown.isVisible ? 0 : 20 - y: processDropdown.isVisible ? 0 : -30 + x: processListDropdown.isVisible ? 0 : 20 + y: processListDropdown.isVisible ? 0 : -30 Behavior on x { NumberAnimation { @@ -101,7 +101,7 @@ PanelWindow { } ] - opacity: processDropdown.isVisible ? 1.0 : 0.0 + opacity: processListDropdown.isVisible ? 1.0 : 0.0 // Add shadow effect layer.enabled: true @@ -111,7 +111,7 @@ PanelWindow { shadowVerticalOffset: 8 shadowBlur: 1.0 shadowColor: Qt.rgba(0, 0, 0, 0.15) - shadowOpacity: processDropdown.isVisible ? 0.15 : 0 + shadowOpacity: processListDropdown.isVisible ? 0.15 : 0 } Behavior on opacity { diff --git a/Widgets/RamMonitorWidget.qml b/Widgets/RamMonitorWidget.qml index f2fa0fc6..d35607ef 100644 --- a/Widgets/RamMonitorWidget.qml +++ b/Widgets/RamMonitorWidget.qml @@ -9,7 +9,6 @@ Rectangle { property bool showPercentage: true property bool showIcon: true - property var processDropdown: null width: 55 height: 30 @@ -18,10 +17,6 @@ Rectangle { Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08) - Component.onCompleted: { - // RAM widget initialized - } - MouseArea { id: ramArea anchors.fill: parent @@ -29,10 +24,8 @@ Rectangle { cursorShape: Qt.PointingHandCursor onClicked: { - if (processDropdown) { - ProcessMonitorService.setSortBy("memory") - processDropdown.toggle() - } + ProcessMonitorService.setSortBy("memory") + processListDropdown.toggle() } } diff --git a/Widgets/ThemePicker.qml b/Widgets/ThemePicker.qml index 446de083..6c357603 100644 --- a/Widgets/ThemePicker.qml +++ b/Widgets/ThemePicker.qml @@ -1,5 +1,6 @@ import QtQuick import qs.Common +import qs.Services Column { id: themePicker @@ -198,7 +199,7 @@ Column { anchors.horizontalCenter: parent.horizontalCenter color: { - if (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") { + if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") { return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) } else { return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) @@ -206,7 +207,7 @@ Column { } border.color: { - if (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") { + if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") { return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5) } else if (Theme.isDynamicTheme) { return Theme.primary @@ -223,13 +224,13 @@ Column { Text { text: { - if (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") return "error" + if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return "error" else return "palette" } font.family: Theme.iconFont font.pixelSize: 16 color: { - if (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") return Theme.error + if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return Theme.error else return Theme.surfaceText } font.weight: Theme.iconFontWeight @@ -238,13 +239,13 @@ Column { Text { text: { - if (root.wallpaperErrorStatus === "error") return "Error" - else if (root.wallpaperErrorStatus === "matugen_missing") return "No matugen" + if (ToastService.wallpaperErrorStatus === "error") return "Error" + else if (ToastService.wallpaperErrorStatus === "matugen_missing") return "No matugen" else return "Auto" } font.pixelSize: Theme.fontSizeMedium color: { - if (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") return Theme.error + if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") return Theme.error else return Theme.surfaceText } font.weight: Font.Medium @@ -297,21 +298,21 @@ Column { anchors.bottom: parent.top anchors.bottomMargin: Theme.spacingS anchors.horizontalCenter: parent.horizontalCenter - visible: autoMouseArea.containsMouse && (!Theme.isDynamicTheme || root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") + visible: autoMouseArea.containsMouse && (!Theme.isDynamicTheme || ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") Text { id: autoTooltipText text: { - if (root.wallpaperErrorStatus === "error") { + if (ToastService.wallpaperErrorStatus === "error") { return "Wallpaper symlink missing at ~/quickshell/current_wallpaper" - } else if (root.wallpaperErrorStatus === "matugen_missing") { + } else if (ToastService.wallpaperErrorStatus === "matugen_missing") { return "Install matugen package for dynamic themes" } else { return "Dynamic wallpaper-based colors" } } font.pixelSize: Theme.fontSizeSmall - color: (root.wallpaperErrorStatus === "error" || root.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceText + color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceText anchors.centerIn: parent wrapMode: Text.WordWrap width: Math.min(implicitWidth, 250) diff --git a/Widgets/ToastWidget.qml b/Widgets/ToastWidget.qml new file mode 100644 index 00000000..64a6bbd0 --- /dev/null +++ b/Widgets/ToastWidget.qml @@ -0,0 +1,125 @@ +import QtQuick +import QtQuick.Effects +import Quickshell +import Quickshell.Widgets +import Quickshell.Wayland +import qs.Common +import qs.Services + +PanelWindow { + id: root + + visible: ToastService.toastVisible + + WlrLayershell.layer: WlrLayershell.Overlay + WlrLayershell.exclusiveZone: -1 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.None + + color: "transparent" + + anchors { + top: true + left: true + right: true + bottom: true + } + + // Makes the background transparent to mouse events + mask: Region { + item: toast + } + + Rectangle { + id: toast + width: Math.min(400, Screen.width - Theme.spacingL * 2) + height: toastContent.height + Theme.spacingL * 2 + + anchors.horizontalCenter: parent.horizontalCenter + y: Theme.barHeight + Theme.spacingL + + color: { + switch (ToastService.currentLevel) { + case ToastService.levelError: return Theme.error + case ToastService.levelWarn: return Theme.warning + case ToastService.levelInfo: return Theme.primary + default: return Theme.primary + } + } + + radius: Theme.cornerRadiusLarge + + layer.enabled: true + layer.effect: MultiEffect { + shadowEnabled: true + shadowHorizontalOffset: 0 + shadowVerticalOffset: 4 + shadowBlur: 0.8 + shadowColor: Qt.rgba(0, 0, 0, 0.3) + shadowOpacity: 0.3 + } + + opacity: ToastService.toastVisible ? 0.9 : 0.0 + scale: ToastService.toastVisible ? 1.0 : 0.9 + + transform: Translate { + y: ToastService.toastVisible ? 0 : -20 + } + + Behavior on opacity { + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing + } + } + + Behavior on scale { + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing + } + } + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } + } + + Row { + id: toastContent + anchors.centerIn: parent + spacing: Theme.spacingM + + Text { + text: { + switch (ToastService.currentLevel) { + case ToastService.levelError: return "error" + case ToastService.levelWarn: return "warning" + case ToastService.levelInfo: return "info" + default: return "info" + } + } + font.family: Theme.iconFont + font.pixelSize: Theme.iconSize + color: Theme.background + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: ToastService.currentMessage + font.pixelSize: Theme.fontSizeMedium + color: Theme.background + font.weight: Font.Medium + anchors.verticalCenter: parent.verticalCenter + width: Math.min(implicitWidth, 300) + elide: Text.ElideRight + } + } + + MouseArea { + anchors.fill: parent + onClicked: ToastService.hideToast() + } + } +} \ No newline at end of file diff --git a/Widgets/TopBar/TopBar.qml b/Widgets/TopBar/TopBar.qml index caaad92e..f891f531 100644 --- a/Widgets/TopBar/TopBar.qml +++ b/Widgets/TopBar/TopBar.qml @@ -14,7 +14,7 @@ import qs.Widgets import "../../Common/Utilities.js" as Utils PanelWindow { - id: topBar + id: root property var modelData screen: modelData @@ -26,31 +26,16 @@ PanelWindow { Connections { target: Prefs function onTopBarTransparencyChanged() { - topBar.backgroundTransparency = Prefs.topBarTransparency + root.backgroundTransparency = Prefs.topBarTransparency } } - // Properties exposed to shell - - // Shell reference to access root properties directly - property var shellRoot: null // Notification properties - property int notificationCount: 0 - - // Process dropdown reference - property var processDropdown: null + readonly property int notificationCount: NotificationService.notifications.length - // Clipboard properties - signal clipboardRequested() - // Tray menu properties - property bool showTrayMenu: false - property var currentTrayMenu: null - property var currentTrayItem: null - property real trayMenuX: 0 - property real trayMenuY: 0 @@ -83,7 +68,7 @@ PanelWindow { Rectangle { anchors.fill: parent radius: Theme.cornerRadiusXLarge - color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, topBar.backgroundTransparency) + color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, root.backgroundTransparency) layer.enabled: true layer.effect: MultiEffect { @@ -146,7 +131,7 @@ PanelWindow { WorkspaceSwitcher { anchors.verticalCenter: parent.verticalCenter - screenName: topBar.screenName + screenName: root.screenName } FocusedAppWidget { @@ -160,9 +145,7 @@ PanelWindow { anchors.centerIn: parent onClockClicked: { - if (topBar.shellRoot) { - topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible - } + centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible } } @@ -173,9 +156,7 @@ PanelWindow { visible: Prefs.showMusic && MprisController.activePlayer onClicked: { - if (topBar.shellRoot) { - topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible - } + centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible } } @@ -188,9 +169,7 @@ PanelWindow { visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0 onClicked: { - if (topBar.shellRoot) { - topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible - } + centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible } } @@ -205,13 +184,11 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter visible: Prefs.showSystemTray onMenuRequested: (menu, item, x, y) => { - if (topBar.shellRoot) { - topBar.shellRoot.currentTrayMenu = menu - topBar.shellRoot.currentTrayItem = item - topBar.shellRoot.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL - topBar.shellRoot.trayMenuY = Theme.barHeight - Theme.spacingXS - topBar.shellRoot.showTrayMenu = true - } + trayMenuPopup.currentTrayMenu = menu + trayMenuPopup.currentTrayItem = item + trayMenuPopup.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL + trayMenuPopup.trayMenuY = Theme.barHeight - Theme.spacingXS + trayMenuPopup.showTrayMenu = true menu.menuVisible = true } } @@ -240,7 +217,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { - topBar.clipboardRequested() + clipboardHistoryPopup.toggle() } } @@ -256,48 +233,42 @@ PanelWindow { CpuMonitorWidget { anchors.verticalCenter: parent.verticalCenter visible: Prefs.showSystemResources - processDropdown: topBar.processDropdown } RamMonitorWidget { anchors.verticalCenter: parent.verticalCenter visible: Prefs.showSystemResources - processDropdown: topBar.processDropdown } NotificationCenterButton { anchors.verticalCenter: parent.verticalCenter - hasUnread: topBar.notificationCount > 0 - isActive: topBar.shellRoot ? topBar.shellRoot.notificationHistoryVisible : false + hasUnread: root.notificationCount > 0 + isActive: notificationCenter.notificationHistoryVisible onClicked: { - if (topBar.shellRoot) { - topBar.shellRoot.notificationHistoryVisible = !topBar.shellRoot.notificationHistoryVisible - } + notificationCenter.notificationHistoryVisible = !notificationCenter.notificationHistoryVisible } } // Battery Widget BatteryWidget { anchors.verticalCenter: parent.verticalCenter - batteryPopupVisible: topBar.shellRoot.batteryPopupVisible + batteryPopupVisible: batteryControlPopup.batteryPopupVisible onToggleBatteryPopup: { - topBar.shellRoot.batteryPopupVisible = !topBar.shellRoot.batteryPopupVisible + batteryControlPopup.batteryPopupVisible = !batteryControlPopup.batteryPopupVisible } } ControlCenterButton { anchors.verticalCenter: parent.verticalCenter - isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false + isActive: controlCenterPopup.controlCenterVisible onClicked: { - if (topBar.shellRoot) { - topBar.shellRoot.controlCenterVisible = !topBar.shellRoot.controlCenterVisible - if (topBar.shellRoot.controlCenterVisible) { - if (NetworkService.wifiEnabled) { - WifiService.scanWifi() - } - // Bluetooth devices are automatically updated via signals + controlCenterPopup.controlCenterVisible = !controlCenterPopup.controlCenterVisible + if (controlCenterPopup.controlCenterVisible) { + if (NetworkService.wifiEnabled) { + WifiService.scanWifi() } + // Bluetooth devices are automatically updated via signals } } } diff --git a/Widgets/TrayMenuPopup.qml b/Widgets/TrayMenuPopup.qml index 36255204..93ab6c6f 100644 --- a/Widgets/TrayMenuPopup.qml +++ b/Widgets/TrayMenuPopup.qml @@ -6,9 +6,15 @@ import Quickshell.Wayland import qs.Common PanelWindow { - id: trayMenuPopup + id: root - visible: root.showTrayMenu + property bool showTrayMenu: false + property real trayMenuX: 0 + property real trayMenuY: 0 + property var currentTrayMenu: null + property var currentTrayItem: null + + visible: showTrayMenu WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.exclusiveZone: -1 @@ -25,8 +31,8 @@ PanelWindow { Rectangle { id: menuContainer - x: root.trayMenuX - y: root.trayMenuY + x: trayMenuX + y: trayMenuY width: Math.max(180, Math.min(300, menuList.maxTextWidth + Theme.spacingL * 2)) height: Math.max(60, menuList.contentHeight + Theme.spacingS * 2) color: Theme.popupBackground() @@ -47,8 +53,8 @@ PanelWindow { } // Material 3 animations - opacity: root.showTrayMenu ? 1.0 : 0.0 - scale: root.showTrayMenu ? 1.0 : 0.85 + opacity: showTrayMenu ? 1.0 : 0.0 + scale: showTrayMenu ? 1.0 : 0.85 Behavior on opacity { NumberAnimation { @@ -70,7 +76,7 @@ PanelWindow { QsMenuOpener { id: menuOpener - menu: root.currentTrayItem ? root.currentTrayItem.menu : null + menu: currentTrayItem ? currentTrayItem.menu : null } // Custom menu styling using ListView @@ -151,7 +157,7 @@ PanelWindow { if (modelData.triggered) { modelData.triggered() } - root.showTrayMenu = false + showTrayMenu = false } } @@ -171,7 +177,7 @@ PanelWindow { anchors.fill: parent z: -1 onClicked: { - root.showTrayMenu = false + showTrayMenu = false } } } \ No newline at end of file diff --git a/Widgets/WifiPasswordDialog.qml b/Widgets/WifiPasswordDialog.qml index f53fcc16..81f69b68 100644 --- a/Widgets/WifiPasswordDialog.qml +++ b/Widgets/WifiPasswordDialog.qml @@ -7,9 +7,13 @@ import qs.Common import qs.Services PanelWindow { - id: wifiPasswordDialog + id: root - visible: root.wifiPasswordDialogVisible + property bool wifiPasswordDialogVisible: false + property string wifiPasswordSSID: "" + property string wifiPasswordInput: "" + + visible: wifiPasswordDialogVisible anchors { top: true left: true @@ -19,7 +23,7 @@ PanelWindow { WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.exclusiveZone: -1 - WlrLayershell.keyboardFocus: root.wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None color: "transparent" @@ -32,7 +36,7 @@ PanelWindow { Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.5) - opacity: root.wifiPasswordDialogVisible ? 1.0 : 0.0 + opacity: wifiPasswordDialogVisible ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { @@ -44,8 +48,8 @@ PanelWindow { MouseArea { anchors.fill: parent onClicked: { - root.wifiPasswordDialogVisible = false - root.wifiPasswordInput = "" + wifiPasswordDialogVisible = false + wifiPasswordInput = "" } } } @@ -59,8 +63,8 @@ PanelWindow { border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: 1 - opacity: root.wifiPasswordDialogVisible ? 1.0 : 0.0 - scale: root.wifiPasswordDialogVisible ? 1.0 : 0.9 + opacity: wifiPasswordDialogVisible ? 1.0 : 0.0 + scale: wifiPasswordDialogVisible ? 1.0 : 0.9 Behavior on opacity { NumberAnimation { @@ -97,7 +101,7 @@ PanelWindow { } Text { - text: "Enter password for \"" + root.wifiPasswordSSID + "\"" + text: "Enter password for \"" + wifiPasswordSSID + "\"" font.pixelSize: Theme.fontSizeMedium color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) width: parent.width @@ -125,8 +129,8 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.wifiPasswordDialogVisible = false - root.wifiPasswordInput = "" + wifiPasswordDialogVisible = false + wifiPasswordInput = "" } } } @@ -162,15 +166,15 @@ PanelWindow { } onTextChanged: { - root.wifiPasswordInput = text + wifiPasswordInput = text } onAccepted: { - WifiService.connectToWifiWithPassword(root.wifiPasswordSSID, root.wifiPasswordInput) + WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput) } Component.onCompleted: { - if (root.wifiPasswordDialogVisible) { + if (wifiPasswordDialogVisible) { forceActiveFocus() } } @@ -260,8 +264,8 @@ PanelWindow { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - root.wifiPasswordDialogVisible = false - root.wifiPasswordInput = "" + wifiPasswordDialogVisible = false + wifiPasswordInput = "" } } } @@ -271,7 +275,7 @@ PanelWindow { height: 36 radius: Theme.cornerRadius color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary - enabled: root.wifiPasswordInput.length > 0 + enabled: wifiPasswordInput.length > 0 opacity: enabled ? 1.0 : 0.5 Text { @@ -290,7 +294,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor enabled: parent.enabled onClicked: { - WifiService.connectToWifiWithPassword(root.wifiPasswordSSID, root.wifiPasswordInput) + WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput) } } diff --git a/shell.qml b/shell.qml index 00717059..3807abd7 100644 --- a/shell.qml +++ b/shell.qml @@ -1,161 +1,51 @@ //@ pragma UseQApplication -import QtQuick -import QtQuick.Controls -import QtQuick.Effects import Quickshell -import Quickshell.Widgets -import Quickshell.Wayland -import Quickshell.Io -import Quickshell.Services.SystemTray -import Quickshell.Services.Notifications -import Quickshell.Services.Mpris -import qs.Services import qs.Widgets import qs.Widgets.CenterCommandCenter import qs.Widgets.ControlCenter import qs.Widgets.TopBar -import qs.Common -import "./Common/Utilities.js" as Utils ShellRoot { id: root - Component.onCompleted: { - // Make root accessible to Theme singleton for error handling - Theme.rootObj = root - - // Initialize service monitoring states based on preferences - SystemMonitorService.enableTopBarMonitoring(Prefs.showSystemResources) - ProcessMonitorService.enableMonitoring(false) // Start disabled, enable when process dropdown is opened - // Audio service auto-updates devices, no manual scanning needed - } - - property bool calendarVisible: false - property bool showTrayMenu: false - property real trayMenuX: 0 - property real trayMenuY: 0 - property var currentTrayMenu: null - property var currentTrayItem: null - property bool notificationHistoryVisible: false - property bool mediaPlayerVisible: false - property bool hasActiveMedia: MprisController.active && (MprisController.active.trackTitle || MprisController.active.trackArtist) - property bool controlCenterVisible: false - - property bool batteryPopupVisible: false - property bool powerMenuVisible: false - property bool powerConfirmVisible: false - property string powerConfirmAction: "" - property string powerConfirmTitle: "" - property string powerConfirmMessage: "" - property bool settingsVisible: false - - - // WiFi password dialog - property bool wifiPasswordDialogVisible: false - property string wifiPasswordSSID: "" - property string wifiPasswordInput: "" - property bool wifiAutoRefreshEnabled: false - - // Wallpaper error status - property string wallpaperErrorStatus: "" - - - // Screen size breakpoints for responsive design - property real screenWidth: Screen.width - property bool isSmallScreen: screenWidth < 1200 - property bool isMediumScreen: screenWidth >= 1200 && screenWidth < 1600 - property bool isLargeScreen: screenWidth >= 1600 - - - // Weather configuration - - - Timer { - id: wifiAutoRefreshTimer - interval: 20000 - running: root.wifiAutoRefreshEnabled && root.controlCenterVisible - repeat: true - onTriggered: { - if (root.wifiAutoRefreshEnabled && root.controlCenterVisible && NetworkService.wifiEnabled) { - WifiService.scanWifi() - } - } - } - - // WiFi Connection Status Timer - Timer { - id: wifiConnectionStatusTimer - interval: 3000 // 3 seconds - running: false - repeat: false - onTriggered: { - root.wifiConnectionStatus = "" - } - } - - // Wallpaper Error Status Timer - Timer { - id: wallpaperErrorTimer - interval: 5000 // 5 seconds - running: false - repeat: false - onTriggered: { - root.wallpaperErrorStatus = "" - } - } - - // Function to show wallpaper error - function showWallpaperError() { - console.log("showWallpaperError called - setting error status") - root.wallpaperErrorStatus = "error" - wallpaperErrorTimer.restart() - } - - // Multi-monitor support using Variants Variants { model: Quickshell.screens delegate: TopBar { modelData: item - - // Connect shell properties - shellRoot: root - notificationCount: NotificationService.notifications.length - processDropdown: processListDropdown - - // Connect tray menu properties - showTrayMenu: root.showTrayMenu - currentTrayMenu: root.currentTrayMenu - currentTrayItem: root.currentTrayItem - trayMenuX: root.trayMenuX - trayMenuY: root.trayMenuY - - // Connect clipboard - onClipboardRequested: { - clipboardHistoryPopup.toggle() - } } } // Global popup windows - CenterCommandCenter {} - TrayMenuPopup {} + CenterCommandCenter { + id: centerCommandCenter + } + TrayMenuPopup { + id: trayMenuPopup + } NotificationInit {} NotificationCenter { - notificationHistoryVisible: root.notificationHistoryVisible - onCloseRequested: { - root.notificationHistoryVisible = false - } + id: notificationCenter + } + ControlCenterPopup { + id: controlCenterPopup + } + WifiPasswordDialog { + id: wifiPasswordDialog } - ControlCenterPopup {} - WifiPasswordDialog {} InputDialog { id: globalInputDialog } - BatteryControlPopup {} - PowerMenuPopup {} - PowerConfirmDialog {} + BatteryControlPopup { + id: batteryControlPopup + } + PowerMenuPopup { + id: powerMenuPopup + } + PowerConfirmDialog { + id: powerConfirmDialog + } ProcessListDropdown { id: processListDropdown @@ -163,24 +53,6 @@ ShellRoot { SettingsPopup { id: settingsPopup - settingsVisible: root.settingsVisible - - // Use a more direct approach for two-way binding - onSettingsVisibleChanged: { - if (settingsVisible !== root.settingsVisible) { - root.settingsVisible = settingsVisible - } - } - - // Also listen to root changes - Connections { - target: root - function onSettingsVisibleChanged() { - if (settingsPopup.settingsVisible !== root.settingsVisible) { - settingsPopup.settingsVisible = root.settingsVisible - } - } - } } // Application and clipboard components @@ -200,17 +72,8 @@ ShellRoot { id: clipboardHistoryPopup } - IpcHandler { - target: "wallpaper" - - function refresh() { - console.log("Wallpaper IPC: refresh() called") - // Trigger color extraction if using dynamic theme - if (typeof Theme !== "undefined" && Theme.isDynamicTheme) { - console.log("Triggering color extraction due to wallpaper IPC") - Colors.extractColors() - } - return "WALLPAPER_REFRESH_SUCCESS" - } + ToastWidget { + id: toastWidget } + } \ No newline at end of file