1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 22:15:38 -05:00

meta: large-scale refactor progress

This commit is contained in:
bbedward
2025-07-17 15:21:37 -04:00
parent 77cc9c288b
commit 7a40156893
24 changed files with 663 additions and 590 deletions

View File

@@ -2,7 +2,8 @@ pragma Singleton
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Qt.labs.platform // ← gives us StandardPaths import Qt.labs.platform
import qs.Services
Singleton { Singleton {
id: root id: root
@@ -53,7 +54,8 @@ Singleton {
if (!matugenAvailable) { if (!matugenAvailable) {
console.warn("Matugen missing → dynamic theme disabled") 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 return
} }
@@ -74,7 +76,8 @@ Singleton {
} else { } else {
console.error("code", code) console.error("code", code)
console.error("Wallpaper not found:", wallpaperPath) 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 const out = matugenCollector.text
if (!out.length) { if (!out.length) {
console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr) console.error("matugen produced zero bytes\nstderr:", matugenProcess.stderr)
Theme.rootObj.showWallpaperError() ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Wallpaper processing failed")
return return
} }
try { try {
root.matugenJson = out root.matugenJson = out
root.matugenColors = JSON.parse(out) root.matugenColors = JSON.parse(out)
root.colorsUpdated() root.colorsUpdated()
Theme.rootObj.wallpaperErrorStatus = "" ToastService.clearWallpaperError()
ToastService.showInfo("Dynamic theme colors updated")
} catch (e) { } catch (e) {
console.error("JSON parse failed:", e) console.error("JSON parse failed:", e)
Theme.rootObj.showWallpaperError() ToastService.wallpaperErrorStatus = "error"
ToastService.showError("Wallpaper processing failed")
} }
} }
} }

View File

@@ -7,9 +7,6 @@ import Quickshell.Io
Singleton { Singleton {
id: root id: root
// Reference to the main shell root for calling functions
property var rootObj: null
// Initialize theme system // Initialize theme system
Component.onCompleted: { Component.onCompleted: {
console.log("Theme Component.onCompleted") console.log("Theme Component.onCompleted")
@@ -609,4 +606,19 @@ Singleton {
default: return "Custom power profile" 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"
}
}
} }

83
Services/ToastService.qml Normal file
View File

@@ -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 = ""
}
}

View File

@@ -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() { function updateCurrentWifiInfo() {
console.log("Updating current WiFi info...") console.log("Updating current WiFi info...")
currentWifiInfo.running = true currentWifiInfo.running = true

View File

@@ -8,9 +8,11 @@ import qs.Services
import Quickshell.Services.UPower import Quickshell.Services.UPower
PanelWindow { PanelWindow {
id: batteryControlPopup id: root
visible: root.batteryPopupVisible property bool batteryPopupVisible: false
visible: batteryPopupVisible
implicitWidth: 400 implicitWidth: 400
implicitHeight: 300 implicitHeight: 300
@@ -32,7 +34,7 @@ PanelWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
opacity: root.batteryPopupVisible ? 1.0 : 0.0 opacity: batteryPopupVisible ? 1.0 : 0.0
scale: root.batteryPopupVisible ? 1.0 : 0.85 scale: batteryPopupVisible ? 1.0 : 0.85
// Prevent click-through to background // Prevent click-through to background
MouseArea { MouseArea {
@@ -114,7 +116,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.batteryPopupVisible = false batteryPopupVisible = false
} }
} }
} }

View File

@@ -7,11 +7,10 @@ import qs.Services
Column { Column {
id: calendarWidget id: calendarWidget
property var theme: Theme
property date displayDate: new Date() property date displayDate: new Date()
property date selectedDate: new Date() property date selectedDate: new Date()
spacing: theme.spacingM spacing: Theme.spacingM
// Load events when display date changes // Load events when display date changes
onDisplayDateChanged: { onDisplayDateChanged: {
@@ -61,16 +60,16 @@ Column {
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: theme.cornerRadius radius: Theme.cornerRadius
color: prevMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent" color: prevMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "chevron_left" text: "chevron_left"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize font.pixelSize: Theme.iconSize
color: theme.primary color: Theme.primary
font.weight: theme.iconFontWeight font.weight: Theme.iconFontWeight
} }
MouseArea { MouseArea {
@@ -91,8 +90,8 @@ Column {
width: parent.width - 80 width: parent.width - 80
height: 40 height: 40
text: Qt.formatDate(displayDate, "MMMM yyyy") text: Qt.formatDate(displayDate, "MMMM yyyy")
font.pixelSize: theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
color: theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
@@ -101,16 +100,16 @@ Column {
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: theme.cornerRadius radius: Theme.cornerRadius
color: nextMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent" color: nextMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "chevron_right" text: "chevron_right"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize font.pixelSize: Theme.iconSize
color: theme.primary color: Theme.primary
font.weight: theme.iconFontWeight font.weight: Theme.iconFontWeight
} }
MouseArea { MouseArea {
@@ -144,8 +143,8 @@ Column {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData text: modelData
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
font.weight: Font.Medium font.weight: Font.Medium
} }
} }
@@ -183,20 +182,20 @@ Column {
property bool isToday: dayDate.toDateString() === new Date().toDateString() property bool isToday: dayDate.toDateString() === new Date().toDateString()
property bool isSelected: dayDate.toDateString() === selectedDate.toDateString() property bool isSelected: dayDate.toDateString() === selectedDate.toDateString()
color: isSelected ? theme.primary : color: isSelected ? Theme.primary :
isToday ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : 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" dayArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: theme.cornerRadiusSmall radius: Theme.cornerRadiusSmall
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: dayDate.getDate() text: dayDate.getDate()
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: isSelected ? theme.surface : color: isSelected ? Theme.surface :
isToday ? theme.primary : isToday ? Theme.primary :
isCurrentMonth ? theme.surfaceText : isCurrentMonth ? Theme.surfaceText :
Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.4) Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
font.weight: isToday || isSelected ? Font.Medium : Font.Normal font.weight: isToday || isSelected ? Font.Medium : Font.Normal
} }
@@ -215,11 +214,11 @@ Column {
color: { color: {
if (isSelected) { if (isSelected) {
// Use a lighter tint of primary for selected state // 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) { } else if (isToday) {
return theme.primary return Theme.primary
} else { } else {
return theme.primary return Theme.primary
} }
} }
@@ -238,22 +237,22 @@ Column {
Behavior on scale { Behavior on scale {
NumberAnimation { NumberAnimation {
duration: theme.shortDuration duration: Theme.shortDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: theme.shortDuration duration: Theme.shortDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
duration: theme.shortDuration duration: Theme.shortDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }

View File

@@ -9,12 +9,12 @@ import qs.Common
import qs.Services import qs.Services
PanelWindow { PanelWindow {
id: centerCommandCenter id: root
property var theme: Theme
readonly property bool hasActiveMedia: MprisController.activePlayer !== null readonly property bool hasActiveMedia: MprisController.activePlayer !== null
property bool calendarVisible: false
visible: root.calendarVisible visible: calendarVisible
implicitWidth: 480 implicitWidth: 480
implicitHeight: 600 implicitHeight: 600
@@ -48,15 +48,15 @@ PanelWindow {
} }
function calculateHeight() { function calculateHeight() {
let contentHeight = theme.spacingM * 2 // margins let contentHeight = Theme.spacingM * 2 // margins
// Main row with widgets and calendar // Main row with widgets and calendar
let widgetHeight = 160 // Media widget always present 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 calendarHeight = 300
let mainRowHeight = Math.max(widgetHeight, calendarHeight) 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 // Add events widget height - use calculated height instead of actual
if (CalendarService && CalendarService.khalAvailable) { if (CalendarService && CalendarService.khalAvailable) {
@@ -68,9 +68,9 @@ PanelWindow {
return Math.min(contentHeight, parent.height * 0.9) return Math.min(contentHeight, parent.height * 0.9)
} }
color: theme.surfaceContainer color: Theme.surfaceContainer
radius: theme.cornerRadiusLarge radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
layer.enabled: true layer.enabled: true
@@ -85,27 +85,27 @@ PanelWindow {
Rectangle { Rectangle {
anchors.fill: parent 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 radius: parent.radius
SequentialAnimation on opacity { SequentialAnimation on opacity {
running: root.calendarVisible running: calendarVisible
loops: Animation.Infinite loops: Animation.Infinite
NumberAnimation { NumberAnimation {
to: 0.08 to: 0.08
duration: theme.extraLongDuration duration: Theme.extraLongDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
NumberAnimation { NumberAnimation {
to: 0.02 to: 0.02
duration: theme.extraLongDuration duration: Theme.extraLongDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
opacity: root.calendarVisible ? 1.0 : 0.0 opacity: calendarVisible ? 1.0 : 0.0
scale: root.calendarVisible ? 1.0 : 0.92 scale: calendarVisible ? 1.0 : 0.92
// Update height when calendar service events change // Update height when calendar service events change
Connections { Connections {
@@ -130,47 +130,47 @@ PanelWindow {
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
duration: theme.longDuration duration: Theme.longDuration
easing.type: theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
Behavior on scale { Behavior on scale {
NumberAnimation { NumberAnimation {
duration: theme.longDuration duration: Theme.longDuration
easing.type: theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
duration: theme.mediumDuration duration: Theme.mediumDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Column { Column {
anchors.fill: parent anchors.fill: parent
anchors.margins: theme.spacingM anchors.margins: Theme.spacingM
spacing: theme.spacingM spacing: Theme.spacingM
// Main row with widgets and calendar // Main row with widgets and calendar
Row { Row {
width: parent.width width: parent.width
height: { height: {
let widgetHeight = 160 // Media widget always present 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 calendarHeight = 300
return Math.max(widgetHeight, calendarHeight) return Math.max(widgetHeight, calendarHeight)
} }
spacing: theme.spacingM spacing: Theme.spacingM
// Left section for widgets // Left section for widgets
Column { Column {
id: leftWidgets id: leftWidgets
width: hasAnyWidgets ? parent.width * 0.45 : 0 width: hasAnyWidgets ? parent.width * 0.45 : 0
height: childrenRect.height height: childrenRect.height
spacing: theme.spacingM spacing: Theme.spacingM
visible: hasAnyWidgets visible: hasAnyWidgets
anchors.top: parent.top anchors.top: parent.top
@@ -180,23 +180,20 @@ PanelWindow {
visible: true // Always visible - shows placeholder when no media visible: true // Always visible - shows placeholder when no media
width: parent.width width: parent.width
height: 160 height: 160
theme: centerCommandCenter.theme
} }
WeatherWidget { WeatherWidget {
visible: true // Always visible - shows placeholder when no weather visible: true // Always visible - shows placeholder when no weather
width: parent.width width: parent.width
height: 140 height: 140
theme: centerCommandCenter.theme
} }
} }
// Right section for calendar // Right section for calendar
CalendarWidget { CalendarWidget {
id: 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 height: parent.height
theme: centerCommandCenter.theme
} }
} }
@@ -204,7 +201,6 @@ PanelWindow {
EventsWidget { EventsWidget {
id: eventsWidget id: eventsWidget
width: parent.width width: parent.width
theme: centerCommandCenter.theme
selectedDate: calendarWidget.selectedDate selectedDate: calendarWidget.selectedDate
} }
@@ -215,7 +211,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
z: -1 z: -1
onClicked: { onClicked: {
root.calendarVisible = false calendarVisible = false
} }
} }
} }

View File

@@ -8,7 +8,6 @@ import qs.Services
Rectangle { Rectangle {
id: eventsWidget id: eventsWidget
property var theme: Theme
property date selectedDate: new Date() property date selectedDate: new Date()
property var selectedDateEvents: [] property var selectedDateEvents: []
property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0 property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0
@@ -21,9 +20,9 @@ Rectangle {
width: parent.width width: parent.width
height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0 height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0
radius: theme.cornerRadiusLarge radius: Theme.cornerRadiusLarge
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
visible: shouldShow visible: shouldShow
@@ -40,8 +39,8 @@ Rectangle {
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
duration: theme.mediumDuration duration: Theme.mediumDuration
easing.type: theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
@@ -81,14 +80,14 @@ Rectangle {
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.margins: theme.spacingL anchors.margins: Theme.spacingL
spacing: theme.spacingS spacing: Theme.spacingS
Text { Text {
text: "event" text: "event"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize - 2 font.pixelSize: Theme.iconSize - 2
color: theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -97,8 +96,8 @@ Rectangle {
(Qt.formatDate(selectedDate, "MMM d") + " • " + (Qt.formatDate(selectedDate, "MMM d") + " • " +
(selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) : (selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) :
Qt.formatDate(selectedDate, "MMM d") Qt.formatDate(selectedDate, "MMM d")
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -107,21 +106,21 @@ Rectangle {
// No events placeholder - centered in entire widget (not just content area) // No events placeholder - centered in entire widget (not just content area)
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: theme.spacingXS spacing: Theme.spacingXS
visible: !hasEvents visible: !hasEvents
Text { Text {
text: "event_busy" text: "event_busy"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize + 8 font.pixelSize: Theme.iconSize + 8
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.3) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
Text { Text {
text: "No events" text: "No events"
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.weight: Font.Normal font.weight: Font.Normal
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
@@ -134,12 +133,12 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.margins: theme.spacingL anchors.margins: Theme.spacingL
anchors.topMargin: theme.spacingM anchors.topMargin: Theme.spacingM
visible: opacity > 0 visible: opacity > 0
opacity: hasEvents ? 1.0 : 0.0 opacity: hasEvents ? 1.0 : 0.0
clip: true clip: true
spacing: theme.spacingS spacing: Theme.spacingS
boundsMovement: Flickable.StopAtBounds boundsMovement: Flickable.StopAtBounds
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
@@ -149,28 +148,28 @@ Rectangle {
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
duration: theme.mediumDuration duration: Theme.mediumDuration
easing.type: theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
delegate: Rectangle { delegate: Rectangle {
width: eventsList.width width: eventsList.width
height: eventContent.implicitHeight + theme.spacingM height: eventContent.implicitHeight + Theme.spacingM
radius: theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (modelData.url && eventMouseArea.containsMouse) { 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) { } 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: { border.color: {
if (modelData.url && eventMouseArea.containsMouse) { 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) { } 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" return "transparent"
} }
@@ -184,7 +183,7 @@ Rectangle {
anchors.leftMargin: 4 anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: 2 radius: 2
color: theme.primary color: Theme.primary
opacity: 0.8 opacity: 0.8
} }
@@ -193,15 +192,15 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: theme.spacingL + 4 anchors.leftMargin: Theme.spacingL + 4
anchors.rightMargin: theme.spacingM anchors.rightMargin: Theme.spacingM
spacing: 6 spacing: 6
Text { Text {
width: parent.width width: parent.width
text: modelData.title text: modelData.title
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.Wrap wrapMode: Text.Wrap
@@ -220,9 +219,9 @@ Rectangle {
Text { Text {
text: "schedule" text: "schedule"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -240,8 +239,8 @@ Rectangle {
return startTime return startTime
} }
} }
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
font.weight: Font.Normal font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@@ -256,16 +255,16 @@ Rectangle {
Text { Text {
text: "location_on" text: "location_on"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: modelData.location text: modelData.location
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
elide: Text.ElideRight elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1 maximumLineCount: 1
@@ -293,15 +292,15 @@ Rectangle {
Behavior on color { Behavior on color {
ColorAnimation { ColorAnimation {
duration: theme.shortDuration duration: Theme.shortDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Behavior on border.color { Behavior on border.color {
ColorAnimation { ColorAnimation {
duration: theme.shortDuration duration: Theme.shortDuration
easing.type: theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }

View File

@@ -10,7 +10,6 @@ Rectangle {
id: mediaPlayerWidget id: mediaPlayerWidget
property MprisPlayer activePlayer: MprisController.activePlayer property MprisPlayer activePlayer: MprisController.activePlayer
property var theme: Theme
property string lastValidTitle: "" property string lastValidTitle: ""
property string lastValidArtist: "" property string lastValidArtist: ""
@@ -40,9 +39,9 @@ Rectangle {
width: parent.width width: parent.width
height: parent.height height: parent.height
radius: theme.cornerRadiusLarge radius: Theme.cornerRadiusLarge
color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.4) 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
layer.enabled: true layer.enabled: true
@@ -96,26 +95,26 @@ Rectangle {
Item { Item {
anchors.fill: parent anchors.fill: parent
anchors.margins: theme.spacingS anchors.margins: Theme.spacingS
// Placeholder when no media - centered in entire widget // Placeholder when no media - centered in entire widget
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: theme.spacingS spacing: Theme.spacingS
visible: (!activePlayer && !lastValidTitle) || (activePlayer && activePlayer.trackTitle === "" && lastValidTitle === "") visible: (!activePlayer && !lastValidTitle) || (activePlayer && activePlayer.trackTitle === "" && lastValidTitle === "")
Text { Text {
text: "music_note" text: "music_note"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize + 8 font.pixelSize: Theme.iconSize + 8
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
Text { Text {
text: "No Media Playing" text: "No Media Playing"
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
} }
@@ -123,21 +122,21 @@ Rectangle {
// Active content in a column // Active content in a column
Column { Column {
anchors.fill: parent anchors.fill: parent
spacing: theme.spacingS spacing: Theme.spacingS
visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== "" visible: activePlayer && activePlayer.trackTitle !== "" || lastValidTitle !== ""
// Normal media info when playing // Normal media info when playing
Row { Row {
width: parent.width width: parent.width
height: 60 height: 60
spacing: theme.spacingM spacing: Theme.spacingM
// Album Art // Album Art
Rectangle { Rectangle {
width: 60 width: 60
height: 60 height: 60
radius: theme.cornerRadius radius: Theme.cornerRadius
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)
Item { Item {
anchors.fill: parent anchors.fill: parent
@@ -164,9 +163,9 @@ Rectangle {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "album" text: "album"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: 28 font.pixelSize: 28
color: theme.surfaceVariantText color: Theme.surfaceVariantText
} }
} }
} }
@@ -174,9 +173,9 @@ Rectangle {
// Track Info // Track Info
Column { Column {
width: parent.width - 60 - theme.spacingM width: parent.width - 60 - Theme.spacingM
height: parent.height height: parent.height
spacing: theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: activePlayer?.trackTitle || lastValidTitle || "Unknown Track" text: activePlayer?.trackTitle || lastValidTitle || "Unknown Track"
@@ -185,9 +184,9 @@ Rectangle {
lastValidTitle = activePlayer.trackTitle; lastValidTitle = activePlayer.trackTitle;
} }
} }
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold font.weight: Font.Bold
color: theme.surfaceText color: Theme.surfaceText
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
} }
@@ -199,8 +198,8 @@ Rectangle {
lastValidArtist = activePlayer.trackArtist; lastValidArtist = activePlayer.trackArtist;
} }
} }
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.8) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
} }
@@ -212,8 +211,8 @@ Rectangle {
lastValidAlbum = activePlayer.trackAlbum; lastValidAlbum = activePlayer.trackAlbum;
} }
} }
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
width: parent.width width: parent.width
elide: Text.ElideRight elide: Text.ElideRight
visible: text.length > 0 visible: text.length > 0
@@ -232,7 +231,7 @@ Rectangle {
width: parent.width width: parent.width
height: 6 height: 6
radius: 3 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 visible: activePlayer !== null
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -240,7 +239,7 @@ Rectangle {
id: progressFill id: progressFill
height: parent.height height: parent.height
radius: parent.radius radius: parent.radius
color: theme.primary color: Theme.primary
width: parent.width * ratio() width: parent.width * ratio()
@@ -255,8 +254,8 @@ Rectangle {
width: 12 width: 12
height: 12 height: 12
radius: 6 radius: 6
color: theme.primary color: Theme.primary
border.color: Qt.lighter(theme.primary, 1.3) border.color: Qt.lighter(Theme.primary, 1.3)
border.width: 1 border.width: 1
x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2)) x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2))
@@ -346,7 +345,7 @@ Rectangle {
Row { Row {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
spacing: theme.spacingM spacing: Theme.spacingM
height: parent.height height: parent.height
// Previous button // Previous button
@@ -354,14 +353,14 @@ Rectangle {
width: 28 width: 28
height: 28 height: 28
radius: 14 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 { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "skip_previous" text: "skip_previous"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: 16 font.pixelSize: 16
color: theme.surfaceText color: Theme.surfaceText
} }
MouseArea { MouseArea {
@@ -388,14 +387,14 @@ Rectangle {
width: 32 width: 32
height: 32 height: 32
radius: 16 radius: 16
color: theme.primary color: Theme.primary
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow" text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: 20 font.pixelSize: 20
color: theme.background color: Theme.background
} }
MouseArea { MouseArea {
@@ -411,14 +410,14 @@ Rectangle {
width: 28 width: 28
height: 28 height: 28
radius: 14 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 { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "skip_next" text: "skip_next"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: 16 font.pixelSize: 16
color: theme.surfaceText color: Theme.surfaceText
} }
MouseArea { MouseArea {

View File

@@ -7,13 +7,12 @@ import qs.Services
Rectangle { Rectangle {
id: weatherWidget id: weatherWidget
property var theme: Theme
width: parent.width width: parent.width
height: parent.height height: parent.height
radius: theme.cornerRadiusLarge radius: Theme.cornerRadiusLarge
color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.4) 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
layer.enabled: true layer.enabled: true
@@ -29,21 +28,21 @@ Rectangle {
// Placeholder when no weather - centered in entire widget // Placeholder when no weather - centered in entire widget
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: theme.spacingS spacing: Theme.spacingS
visible: !WeatherService.weather.available || WeatherService.weather.temp === 0 visible: !WeatherService.weather.available || WeatherService.weather.temp === 0
Text { Text {
text: "cloud_off" text: "cloud_off"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize + 8 font.pixelSize: Theme.iconSize + 8
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
Text { Text {
text: "No Weather Data" text: "No Weather Data"
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
} }
@@ -51,8 +50,8 @@ Rectangle {
// Weather content when available - original Column structure // Weather content when available - original Column structure
Column { Column {
anchors.fill: parent anchors.fill: parent
anchors.margins: theme.spacingL anchors.margins: Theme.spacingL
spacing: theme.spacingS spacing: Theme.spacingS
visible: WeatherService.weather.available && WeatherService.weather.temp !== 0 visible: WeatherService.weather.available && WeatherService.weather.temp !== 0
// Weather header info // Weather header info
@@ -62,25 +61,25 @@ Rectangle {
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: theme.spacingL spacing: Theme.spacingL
// Weather icon // Weather icon
Text { Text {
text: WeatherService.getWeatherIcon(WeatherService.weather.wCode) text: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.iconSize + 8 font.pixelSize: Theme.iconSize + 8
color: theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Column { Column {
spacing: theme.spacingXS spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Text { Text {
text: (Prefs.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C") text: (Prefs.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
font.pixelSize: theme.fontSizeXLarge font.pixelSize: Theme.fontSizeXLarge
color: theme.surfaceText color: Theme.surfaceText
font.weight: Font.Light font.weight: Font.Light
MouseArea { MouseArea {
@@ -94,8 +93,8 @@ Rectangle {
Text { Text {
text: WeatherService.weather.city || "" text: WeatherService.weather.city || ""
font.pixelSize: theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
visible: text.length > 0 visible: text.length > 0
} }
} }
@@ -105,73 +104,73 @@ Rectangle {
// Weather details grid // Weather details grid
Grid { Grid {
columns: 2 columns: 2
spacing: theme.spacingM spacing: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
Row { Row {
spacing: theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: "humidity_low" text: "humidity_low"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--" text: WeatherService.weather.humidity ? WeatherService.weather.humidity + "%" : "--"
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
Row { Row {
spacing: theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: "air" text: "air"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: WeatherService.weather.wind || "--" text: WeatherService.weather.wind || "--"
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
Row { Row {
spacing: theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: "wb_twilight" text: "wb_twilight"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: WeatherService.weather.sunrise || "--" text: WeatherService.weather.sunrise || "--"
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
Row { Row {
spacing: theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: "bedtime" text: "bedtime"
font.family: theme.iconFont font.family: Theme.iconFont
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: WeatherService.weather.sunset || "--" text: WeatherService.weather.sunset || "--"
font.pixelSize: theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }

View File

@@ -9,9 +9,16 @@ import qs.Common
import qs.Services import qs.Services
PanelWindow { PanelWindow {
id: controlCenterPopup id: root
visible: root.controlCenterVisible property bool controlCenterVisible: false
visible: controlCenterVisible
onVisibleChanged: {
// Enable/disable WiFi auto-refresh based on control center visibility
WifiService.autoRefreshEnabled = visible && NetworkService.wifiEnabled
}
implicitWidth: 600 implicitWidth: 600
implicitHeight: 500 implicitHeight: 500
@@ -34,7 +41,7 @@ PanelWindow {
Rectangle { Rectangle {
width: Math.min(600, Screen.width - Theme.spacingL * 2) 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) x: Math.max(Theme.spacingL, Screen.width - width - Theme.spacingL)
y: Theme.barHeight + Theme.spacingXS y: Theme.barHeight + Theme.spacingXS
color: Theme.popupBackground() color: Theme.popupBackground()
@@ -46,15 +53,15 @@ PanelWindow {
transform: [ transform: [
Scale { Scale {
id: scaleTransform 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 origin.y: 0
xScale: root.controlCenterVisible ? 1.0 : 0.95 xScale: controlCenterVisible ? 1.0 : 0.95
yScale: root.controlCenterVisible ? 1.0 : 0.8 yScale: controlCenterVisible ? 1.0 : 0.8
}, },
Translate { Translate {
id: translateTransform id: translateTransform
x: root.controlCenterVisible ? 0 : 15 // Slide slightly left when hidden x: controlCenterVisible ? 0 : 15 // Slide slightly left when hidden
y: root.controlCenterVisible ? 0 : -30 y: controlCenterVisible ? 0 : -30
} }
] ]
@@ -62,13 +69,13 @@ PanelWindow {
states: [ states: [
State { State {
name: "visible" name: "visible"
when: root.controlCenterVisible when: controlCenterVisible
PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 } PropertyChanges { target: scaleTransform; xScale: 1.0; yScale: 1.0 }
PropertyChanges { target: translateTransform; x: 0; y: 0 } PropertyChanges { target: translateTransform; x: 0; y: 0 }
}, },
State { State {
name: "hidden" name: "hidden"
when: !root.controlCenterVisible when: !controlCenterVisible
PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 } PropertyChanges { target: scaleTransform; xScale: 0.95; yScale: 0.8 }
PropertyChanges { target: translateTransform; x: 15; y: -30 } 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 { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -253,7 +260,7 @@ PanelWindow {
width: 40 width: 40
height: 40 height: 40
radius: 20 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.error.r, Theme.error.g, Theme.error.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
@@ -267,10 +274,10 @@ PanelWindow {
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: controlCenterPopup.powerOptionsExpanded ? "expand_less" : "power_settings_new" text: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
font.family: Theme.iconFont font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 2 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 { Behavior on text {
// Smooth icon transition // Smooth icon transition
@@ -302,7 +309,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
controlCenterPopup.powerOptionsExpanded = !controlCenterPopup.powerOptionsExpanded root.powerOptionsExpanded = !root.powerOptionsExpanded
} }
} }
@@ -338,8 +345,8 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.controlCenterVisible = false controlCenterVisible = false
root.settingsVisible = true settingsPopup.settingsVisible = true
} }
} }
@@ -356,12 +363,12 @@ PanelWindow {
// Animated Collapsible Power Options (optimized) // Animated Collapsible Power Options (optimized)
Rectangle { Rectangle {
width: parent.width width: parent.width
height: controlCenterPopup.powerOptionsExpanded ? 60 : 0 height: root.powerOptionsExpanded ? 60 : 0
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.4) 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: controlCenterPopup.powerOptionsExpanded ? 1 : 0 border.width: root.powerOptionsExpanded ? 1 : 0
opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0 opacity: root.powerOptionsExpanded ? 1.0 : 0.0
clip: true clip: true
// Single coordinated animation for power options // Single coordinated animation for power options
@@ -382,7 +389,7 @@ PanelWindow {
Row { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingL spacing: Theme.spacingL
visible: controlCenterPopup.powerOptionsExpanded visible: root.powerOptionsExpanded
// Logout // Logout
Rectangle { Rectangle {
@@ -421,11 +428,13 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
controlCenterPopup.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.powerConfirmAction = "logout" if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmTitle = "Logout" root.powerConfirmDialog.powerConfirmAction = "logout"
root.powerConfirmMessage = "Are you sure you want to logout?" root.powerConfirmDialog.powerConfirmTitle = "Logout"
root.powerConfirmVisible = true root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to logout?"
root.powerConfirmDialog.powerConfirmVisible = true
}
} }
} }
@@ -474,11 +483,13 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
controlCenterPopup.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.powerConfirmAction = "reboot" if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmTitle = "Restart" root.powerConfirmDialog.powerConfirmAction = "reboot"
root.powerConfirmMessage = "Are you sure you want to restart?" root.powerConfirmDialog.powerConfirmTitle = "Restart"
root.powerConfirmVisible = true root.powerConfirmDialog.powerConfirmMessage = "Are you sure you want to restart?"
root.powerConfirmDialog.powerConfirmVisible = true
}
} }
} }
@@ -527,11 +538,13 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
controlCenterPopup.powerOptionsExpanded = false root.powerOptionsExpanded = false
root.powerConfirmAction = "poweroff" if (typeof root !== "undefined" && root.powerConfirmDialog) {
root.powerConfirmTitle = "Shutdown" root.powerConfirmDialog.powerConfirmAction = "poweroff"
root.powerConfirmMessage = "Are you sure you want to shutdown?" root.powerConfirmDialog.powerConfirmTitle = "Shutdown"
root.powerConfirmVisible = true 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 width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
height: 40 height: 40
radius: Theme.cornerRadius 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) : 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" tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
@@ -591,15 +604,15 @@ PanelWindow {
text: modelData.icon text: modelData.icon
font.family: Theme.iconFont font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 4 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 anchors.verticalCenter: parent.verticalCenter
} }
Text { Text {
text: modelData.name text: modelData.name
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: controlCenterPopup.currentTab === modelData.id ? Theme.primary : Theme.surfaceText color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText
font.weight: controlCenterPopup.currentTab === modelData.id ? Font.Medium : Font.Normal font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
@@ -611,7 +624,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
controlCenterPopup.currentTab = modelData.id root.currentTab = modelData.id
} }
} }
@@ -629,7 +642,7 @@ PanelWindow {
// Tab content area // Tab content area
Rectangle { Rectangle {
width: parent.width width: parent.width
height: controlCenterPopup.powerOptionsExpanded ? 240 : 300 height: root.powerOptionsExpanded ? 240 : 300
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1) color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.1)
@@ -644,36 +657,29 @@ PanelWindow {
NetworkTab { NetworkTab {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM 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 // Audio Tab
AudioTab { AudioTab {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingM
visible: controlCenterPopup.currentTab === "audio" visible: root.currentTab === "audio"
} }
// Bluetooth Tab // Bluetooth Tab
BluetoothTab { BluetoothTab {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingM
visible: BluetoothService.available && controlCenterPopup.currentTab === "bluetooth" visible: BluetoothService.available && root.currentTab === "bluetooth"
} }
// Display Tab // Display Tab
DisplayTab { DisplayTab {
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingM
visible: controlCenterPopup.currentTab === "display" visible: root.currentTab === "display"
} }
} }
@@ -685,7 +691,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
z: -1 z: -1
onClicked: { onClicked: {
root.controlCenterVisible = false controlCenterVisible = false
} }
} }
} }

View File

@@ -16,11 +16,6 @@ Item {
else return 1 // Default to WiFi when nothing is connected 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 { Column {
anchors.fill: parent anchors.fill: parent
@@ -67,7 +62,7 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
networkTab.networkSubTab = 0 networkTab.networkSubTab = 0
networkTab.wifiAutoRefreshEnabled = false WifiService.autoRefreshEnabled = false
} }
} }
} }
@@ -108,7 +103,7 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
networkTab.networkSubTab = 1 networkTab.networkSubTab = 1
networkTab.wifiAutoRefreshEnabled = true WifiService.autoRefreshEnabled = true
if (NetworkService.wifiEnabled) { if (NetworkService.wifiEnabled) {
WifiService.scanWifi() WifiService.scanWifi()
} }
@@ -795,9 +790,9 @@ Item {
WifiService.connectToWifi(modelData.ssid) WifiService.connectToWifi(modelData.ssid)
} else if (modelData.secured) { } else if (modelData.secured) {
// Secured network, need password - use root dialog // Secured network, need password - use root dialog
root.wifiPasswordSSID = modelData.ssid wifiPasswordDialog.wifiPasswordSSID = modelData.ssid
root.wifiPasswordInput = "" wifiPasswordDialog.wifiPasswordInput = ""
root.wifiPasswordDialogVisible = true wifiPasswordDialog.wifiPasswordDialogVisible = true
} else { } else {
// Open network, connect directly // Open network, connect directly
WifiService.connectToWifi(modelData.ssid) WifiService.connectToWifi(modelData.ssid)

View File

@@ -9,7 +9,6 @@ Rectangle {
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var processDropdown: null
width: 55 width: 55
height: 30 height: 30
@@ -18,10 +17,6 @@ Rectangle {
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : 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) Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
Component.onCompleted: {
// CPU widget initialized
}
MouseArea { MouseArea {
id: cpuArea id: cpuArea
anchors.fill: parent anchors.fill: parent
@@ -29,10 +24,8 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (processDropdown) {
ProcessMonitorService.setSortBy("cpu") ProcessMonitorService.setSortBy("cpu")
processDropdown.toggle() processListDropdown.toggle()
}
} }
} }

View File

@@ -8,10 +8,9 @@ import qs.Common
import qs.Services import qs.Services
PanelWindow { PanelWindow {
id: notificationHistoryPopup id: root
property bool notificationHistoryVisible: false property bool notificationHistoryVisible: false
signal closeRequested()
visible: notificationHistoryVisible visible: notificationHistoryVisible
@@ -35,7 +34,7 @@ PanelWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
closeRequested() notificationHistoryVisible = false
} }
} }
@@ -53,7 +52,7 @@ PanelWindow {
transform: [ transform: [
Scale { Scale {
id: scaleTransform id: scaleTransform
origin.x: parent.width origin.x: 400 // Use fixed width since popup is 400px wide
origin.y: 0 origin.y: 0
xScale: notificationHistoryVisible ? 1.0 : 0.95 xScale: notificationHistoryVisible ? 1.0 : 0.95
yScale: notificationHistoryVisible ? 1.0 : 0.8 yScale: notificationHistoryVisible ? 1.0 : 0.8

View File

@@ -7,9 +7,14 @@ import Quickshell.Io
import qs.Common import qs.Common
PanelWindow { 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 implicitWidth: 400
implicitHeight: 300 implicitHeight: 300
@@ -43,8 +48,8 @@ PanelWindow {
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1 border.width: 1
opacity: root.powerConfirmVisible ? 1.0 : 0.0 opacity: powerConfirmVisible ? 1.0 : 0.0
scale: root.powerConfirmVisible ? 1.0 : 0.9 scale: powerConfirmVisible ? 1.0 : 0.9
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -67,10 +72,10 @@ PanelWindow {
// Title // Title
Text { Text {
text: root.powerConfirmTitle text: powerConfirmTitle
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
color: { color: {
switch(root.powerConfirmAction) { switch(powerConfirmAction) {
case "poweroff": return Theme.error case "poweroff": return Theme.error
case "reboot": return Theme.warning case "reboot": return Theme.warning
default: return Theme.surfaceText default: return Theme.surfaceText
@@ -83,7 +88,7 @@ PanelWindow {
// Message // Message
Text { Text {
text: root.powerConfirmMessage text: powerConfirmMessage
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
width: parent.width width: parent.width
@@ -119,7 +124,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerConfirmVisible = false powerConfirmVisible = false
} }
} }
} }
@@ -131,7 +136,7 @@ PanelWindow {
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
let baseColor let baseColor
switch(root.powerConfirmAction) { switch(powerConfirmAction) {
case "poweroff": baseColor = Theme.error; break case "poweroff": baseColor = Theme.error; break
case "reboot": baseColor = Theme.warning; break case "reboot": baseColor = Theme.warning; break
default: baseColor = Theme.primary; break default: baseColor = Theme.primary; break
@@ -155,8 +160,8 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerConfirmVisible = false powerConfirmVisible = false
executePowerAction(root.powerConfirmAction) executePowerAction(powerConfirmAction)
} }
} }
} }

View File

@@ -7,9 +7,11 @@ import Quickshell.Io
import qs.Common import qs.Common
PanelWindow { PanelWindow {
id: powerMenuPopup id: root
visible: root.powerMenuVisible property bool powerMenuVisible: false
visible: powerMenuVisible
implicitWidth: 400 implicitWidth: 400
implicitHeight: 320 implicitHeight: 320
@@ -31,7 +33,7 @@ PanelWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { 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.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1 border.width: 1
opacity: root.powerMenuVisible ? 1.0 : 0.0 opacity: powerMenuVisible ? 1.0 : 0.0
scale: root.powerMenuVisible ? 1.0 : 0.85 scale: powerMenuVisible ? 1.0 : 0.85
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -109,7 +111,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerMenuVisible = false powerMenuVisible = false
} }
} }
} }
@@ -156,7 +158,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "logout" root.powerConfirmAction = "logout"
root.powerConfirmTitle = "Log Out" root.powerConfirmTitle = "Log Out"
root.powerConfirmMessage = "Are you sure you want to log out?" root.powerConfirmMessage = "Are you sure you want to log out?"
@@ -201,7 +203,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "suspend" root.powerConfirmAction = "suspend"
root.powerConfirmTitle = "Suspend" root.powerConfirmTitle = "Suspend"
root.powerConfirmMessage = "Are you sure you want to suspend the system?" root.powerConfirmMessage = "Are you sure you want to suspend the system?"
@@ -246,7 +248,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "reboot" root.powerConfirmAction = "reboot"
root.powerConfirmTitle = "Reboot" root.powerConfirmTitle = "Reboot"
root.powerConfirmMessage = "Are you sure you want to reboot the system?" root.powerConfirmMessage = "Are you sure you want to reboot the system?"
@@ -291,7 +293,7 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.powerMenuVisible = false powerMenuVisible = false
root.powerConfirmAction = "poweroff" root.powerConfirmAction = "poweroff"
root.powerConfirmTitle = "Power Off" root.powerConfirmTitle = "Power Off"
root.powerConfirmMessage = "Are you sure you want to power off the system?" root.powerConfirmMessage = "Are you sure you want to power off the system?"

View File

@@ -10,7 +10,7 @@ import qs.Common
import qs.Services import qs.Services
PanelWindow { PanelWindow {
id: processDropdown id: processListDropdown
property bool isVisible: false property bool isVisible: false
property var parentWidget: null property var parentWidget: null
@@ -41,7 +41,7 @@ PanelWindow {
// Click outside to close // Click outside to close
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: processDropdown.hide() onClicked: processListDropdown.hide()
} }
Rectangle { Rectangle {
@@ -63,8 +63,8 @@ PanelWindow {
id: scaleTransform id: scaleTransform
origin.x: parent.width * 0.85 // Scale from top-right origin.x: parent.width * 0.85 // Scale from top-right
origin.y: 0 origin.y: 0
xScale: processDropdown.isVisible ? 1.0 : 0.95 xScale: processListDropdown.isVisible ? 1.0 : 0.95
yScale: processDropdown.isVisible ? 1.0 : 0.8 yScale: processListDropdown.isVisible ? 1.0 : 0.8
Behavior on xScale { Behavior on xScale {
NumberAnimation { NumberAnimation {
@@ -82,8 +82,8 @@ PanelWindow {
}, },
Translate { Translate {
id: translateTransform id: translateTransform
x: processDropdown.isVisible ? 0 : 20 x: processListDropdown.isVisible ? 0 : 20
y: processDropdown.isVisible ? 0 : -30 y: processListDropdown.isVisible ? 0 : -30
Behavior on x { Behavior on x {
NumberAnimation { NumberAnimation {
@@ -101,7 +101,7 @@ PanelWindow {
} }
] ]
opacity: processDropdown.isVisible ? 1.0 : 0.0 opacity: processListDropdown.isVisible ? 1.0 : 0.0
// Add shadow effect // Add shadow effect
layer.enabled: true layer.enabled: true
@@ -111,7 +111,7 @@ PanelWindow {
shadowVerticalOffset: 8 shadowVerticalOffset: 8
shadowBlur: 1.0 shadowBlur: 1.0
shadowColor: Qt.rgba(0, 0, 0, 0.15) shadowColor: Qt.rgba(0, 0, 0, 0.15)
shadowOpacity: processDropdown.isVisible ? 0.15 : 0 shadowOpacity: processListDropdown.isVisible ? 0.15 : 0
} }
Behavior on opacity { Behavior on opacity {

View File

@@ -9,7 +9,6 @@ Rectangle {
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var processDropdown: null
width: 55 width: 55
height: 30 height: 30
@@ -18,10 +17,6 @@ Rectangle {
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : 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) Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
Component.onCompleted: {
// RAM widget initialized
}
MouseArea { MouseArea {
id: ramArea id: ramArea
anchors.fill: parent anchors.fill: parent
@@ -29,10 +24,8 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (processDropdown) {
ProcessMonitorService.setSortBy("memory") ProcessMonitorService.setSortBy("memory")
processDropdown.toggle() processListDropdown.toggle()
}
} }
} }

View File

@@ -1,5 +1,6 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Services
Column { Column {
id: themePicker id: themePicker
@@ -198,7 +199,7 @@ Column {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
color: { 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) return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
} else { } else {
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
@@ -206,7 +207,7 @@ Column {
} }
border.color: { 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) return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5)
} else if (Theme.isDynamicTheme) { } else if (Theme.isDynamicTheme) {
return Theme.primary return Theme.primary
@@ -223,13 +224,13 @@ Column {
Text { Text {
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" else return "palette"
} }
font.family: Theme.iconFont font.family: Theme.iconFont
font.pixelSize: 16 font.pixelSize: 16
color: { 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 else return Theme.surfaceText
} }
font.weight: Theme.iconFontWeight font.weight: Theme.iconFontWeight
@@ -238,13 +239,13 @@ Column {
Text { Text {
text: { text: {
if (root.wallpaperErrorStatus === "error") return "Error" if (ToastService.wallpaperErrorStatus === "error") return "Error"
else if (root.wallpaperErrorStatus === "matugen_missing") return "No matugen" else if (ToastService.wallpaperErrorStatus === "matugen_missing") return "No matugen"
else return "Auto" else return "Auto"
} }
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: { 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 else return Theme.surfaceText
} }
font.weight: Font.Medium font.weight: Font.Medium
@@ -297,21 +298,21 @@ Column {
anchors.bottom: parent.top anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingS anchors.bottomMargin: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter 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 { Text {
id: autoTooltipText id: autoTooltipText
text: { text: {
if (root.wallpaperErrorStatus === "error") { if (ToastService.wallpaperErrorStatus === "error") {
return "Wallpaper symlink missing at ~/quickshell/current_wallpaper" 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" return "Install matugen package for dynamic themes"
} else { } else {
return "Dynamic wallpaper-based colors" return "Dynamic wallpaper-based colors"
} }
} }
font.pixelSize: Theme.fontSizeSmall 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 anchors.centerIn: parent
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
width: Math.min(implicitWidth, 250) width: Math.min(implicitWidth, 250)

125
Widgets/ToastWidget.qml Normal file
View File

@@ -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()
}
}
}

View File

@@ -14,7 +14,7 @@ import qs.Widgets
import "../../Common/Utilities.js" as Utils import "../../Common/Utilities.js" as Utils
PanelWindow { PanelWindow {
id: topBar id: root
property var modelData property var modelData
screen: modelData screen: modelData
@@ -26,31 +26,16 @@ PanelWindow {
Connections { Connections {
target: Prefs target: Prefs
function onTopBarTransparencyChanged() { 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 // Notification properties
property int notificationCount: 0 readonly property int notificationCount: NotificationService.notifications.length
// Process dropdown reference
property var processDropdown: null
// 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 { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadiusXLarge 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.enabled: true
layer.effect: MultiEffect { layer.effect: MultiEffect {
@@ -146,7 +131,7 @@ PanelWindow {
WorkspaceSwitcher { WorkspaceSwitcher {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
screenName: topBar.screenName screenName: root.screenName
} }
FocusedAppWidget { FocusedAppWidget {
@@ -160,9 +145,7 @@ PanelWindow {
anchors.centerIn: parent anchors.centerIn: parent
onClockClicked: { onClockClicked: {
if (topBar.shellRoot) { centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible
}
} }
} }
@@ -173,9 +156,7 @@ PanelWindow {
visible: Prefs.showMusic && MprisController.activePlayer visible: Prefs.showMusic && MprisController.activePlayer
onClicked: { onClicked: {
if (topBar.shellRoot) { centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible
}
} }
} }
@@ -188,9 +169,7 @@ PanelWindow {
visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0 visible: Prefs.showWeather && WeatherService.weather.available && WeatherService.weather.temp > 0 && WeatherService.weather.tempF > 0
onClicked: { onClicked: {
if (topBar.shellRoot) { centerCommandCenter.calendarVisible = !centerCommandCenter.calendarVisible
topBar.shellRoot.calendarVisible = !topBar.shellRoot.calendarVisible
}
} }
} }
@@ -205,13 +184,11 @@ PanelWindow {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemTray visible: Prefs.showSystemTray
onMenuRequested: (menu, item, x, y) => { onMenuRequested: (menu, item, x, y) => {
if (topBar.shellRoot) { trayMenuPopup.currentTrayMenu = menu
topBar.shellRoot.currentTrayMenu = menu trayMenuPopup.currentTrayItem = item
topBar.shellRoot.currentTrayItem = item trayMenuPopup.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL
topBar.shellRoot.trayMenuX = rightSection.x + rightSection.width - 400 - Theme.spacingL trayMenuPopup.trayMenuY = Theme.barHeight - Theme.spacingXS
topBar.shellRoot.trayMenuY = Theme.barHeight - Theme.spacingXS trayMenuPopup.showTrayMenu = true
topBar.shellRoot.showTrayMenu = true
}
menu.menuVisible = true menu.menuVisible = true
} }
} }
@@ -240,7 +217,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
topBar.clipboardRequested() clipboardHistoryPopup.toggle()
} }
} }
@@ -256,43 +233,38 @@ PanelWindow {
CpuMonitorWidget { CpuMonitorWidget {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemResources visible: Prefs.showSystemResources
processDropdown: topBar.processDropdown
} }
RamMonitorWidget { RamMonitorWidget {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: Prefs.showSystemResources visible: Prefs.showSystemResources
processDropdown: topBar.processDropdown
} }
NotificationCenterButton { NotificationCenterButton {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
hasUnread: topBar.notificationCount > 0 hasUnread: root.notificationCount > 0
isActive: topBar.shellRoot ? topBar.shellRoot.notificationHistoryVisible : false isActive: notificationCenter.notificationHistoryVisible
onClicked: { onClicked: {
if (topBar.shellRoot) { notificationCenter.notificationHistoryVisible = !notificationCenter.notificationHistoryVisible
topBar.shellRoot.notificationHistoryVisible = !topBar.shellRoot.notificationHistoryVisible
}
} }
} }
// Battery Widget // Battery Widget
BatteryWidget { BatteryWidget {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
batteryPopupVisible: topBar.shellRoot.batteryPopupVisible batteryPopupVisible: batteryControlPopup.batteryPopupVisible
onToggleBatteryPopup: { onToggleBatteryPopup: {
topBar.shellRoot.batteryPopupVisible = !topBar.shellRoot.batteryPopupVisible batteryControlPopup.batteryPopupVisible = !batteryControlPopup.batteryPopupVisible
} }
} }
ControlCenterButton { ControlCenterButton {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
isActive: topBar.shellRoot ? topBar.shellRoot.controlCenterVisible : false isActive: controlCenterPopup.controlCenterVisible
onClicked: { onClicked: {
if (topBar.shellRoot) { controlCenterPopup.controlCenterVisible = !controlCenterPopup.controlCenterVisible
topBar.shellRoot.controlCenterVisible = !topBar.shellRoot.controlCenterVisible if (controlCenterPopup.controlCenterVisible) {
if (topBar.shellRoot.controlCenterVisible) {
if (NetworkService.wifiEnabled) { if (NetworkService.wifiEnabled) {
WifiService.scanWifi() WifiService.scanWifi()
} }
@@ -304,4 +276,3 @@ PanelWindow {
} }
} }
} }
}

View File

@@ -6,9 +6,15 @@ import Quickshell.Wayland
import qs.Common import qs.Common
PanelWindow { 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.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
@@ -25,8 +31,8 @@ PanelWindow {
Rectangle { Rectangle {
id: menuContainer id: menuContainer
x: root.trayMenuX x: trayMenuX
y: root.trayMenuY y: trayMenuY
width: Math.max(180, Math.min(300, menuList.maxTextWidth + Theme.spacingL * 2)) width: Math.max(180, Math.min(300, menuList.maxTextWidth + Theme.spacingL * 2))
height: Math.max(60, menuList.contentHeight + Theme.spacingS * 2) height: Math.max(60, menuList.contentHeight + Theme.spacingS * 2)
color: Theme.popupBackground() color: Theme.popupBackground()
@@ -47,8 +53,8 @@ PanelWindow {
} }
// Material 3 animations // Material 3 animations
opacity: root.showTrayMenu ? 1.0 : 0.0 opacity: showTrayMenu ? 1.0 : 0.0
scale: root.showTrayMenu ? 1.0 : 0.85 scale: showTrayMenu ? 1.0 : 0.85
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -70,7 +76,7 @@ PanelWindow {
QsMenuOpener { QsMenuOpener {
id: menuOpener id: menuOpener
menu: root.currentTrayItem ? root.currentTrayItem.menu : null menu: currentTrayItem ? currentTrayItem.menu : null
} }
// Custom menu styling using ListView // Custom menu styling using ListView
@@ -151,7 +157,7 @@ PanelWindow {
if (modelData.triggered) { if (modelData.triggered) {
modelData.triggered() modelData.triggered()
} }
root.showTrayMenu = false showTrayMenu = false
} }
} }
@@ -171,7 +177,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
z: -1 z: -1
onClicked: { onClicked: {
root.showTrayMenu = false showTrayMenu = false
} }
} }
} }

View File

@@ -7,9 +7,13 @@ import qs.Common
import qs.Services import qs.Services
PanelWindow { PanelWindow {
id: wifiPasswordDialog id: root
visible: root.wifiPasswordDialogVisible property bool wifiPasswordDialogVisible: false
property string wifiPasswordSSID: ""
property string wifiPasswordInput: ""
visible: wifiPasswordDialogVisible
anchors { anchors {
top: true top: true
left: true left: true
@@ -19,7 +23,7 @@ PanelWindow {
WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: root.wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None WlrLayershell.keyboardFocus: wifiPasswordDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
color: "transparent" color: "transparent"
@@ -32,7 +36,7 @@ PanelWindow {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
color: Qt.rgba(0, 0, 0, 0.5) color: Qt.rgba(0, 0, 0, 0.5)
opacity: root.wifiPasswordDialogVisible ? 1.0 : 0.0 opacity: wifiPasswordDialogVisible ? 1.0 : 0.0
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -44,8 +48,8 @@ PanelWindow {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
root.wifiPasswordDialogVisible = false wifiPasswordDialogVisible = false
root.wifiPasswordInput = "" wifiPasswordInput = ""
} }
} }
} }
@@ -59,8 +63,8 @@ PanelWindow {
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1 border.width: 1
opacity: root.wifiPasswordDialogVisible ? 1.0 : 0.0 opacity: wifiPasswordDialogVisible ? 1.0 : 0.0
scale: root.wifiPasswordDialogVisible ? 1.0 : 0.9 scale: wifiPasswordDialogVisible ? 1.0 : 0.9
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
@@ -97,7 +101,7 @@ PanelWindow {
} }
Text { Text {
text: "Enter password for \"" + root.wifiPasswordSSID + "\"" text: "Enter password for \"" + wifiPasswordSSID + "\""
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7) color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
width: parent.width width: parent.width
@@ -125,8 +129,8 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.wifiPasswordDialogVisible = false wifiPasswordDialogVisible = false
root.wifiPasswordInput = "" wifiPasswordInput = ""
} }
} }
} }
@@ -162,15 +166,15 @@ PanelWindow {
} }
onTextChanged: { onTextChanged: {
root.wifiPasswordInput = text wifiPasswordInput = text
} }
onAccepted: { onAccepted: {
WifiService.connectToWifiWithPassword(root.wifiPasswordSSID, root.wifiPasswordInput) WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput)
} }
Component.onCompleted: { Component.onCompleted: {
if (root.wifiPasswordDialogVisible) { if (wifiPasswordDialogVisible) {
forceActiveFocus() forceActiveFocus()
} }
} }
@@ -260,8 +264,8 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
root.wifiPasswordDialogVisible = false wifiPasswordDialogVisible = false
root.wifiPasswordInput = "" wifiPasswordInput = ""
} }
} }
} }
@@ -271,7 +275,7 @@ PanelWindow {
height: 36 height: 36
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: connectArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary 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 opacity: enabled ? 1.0 : 0.5
Text { Text {
@@ -290,7 +294,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
enabled: parent.enabled enabled: parent.enabled
onClicked: { onClicked: {
WifiService.connectToWifiWithPassword(root.wifiPasswordSSID, root.wifiPasswordInput) WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput)
} }
} }

185
shell.qml
View File

@@ -1,161 +1,51 @@
//@ pragma UseQApplication //@ pragma UseQApplication
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell 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
import qs.Widgets.CenterCommandCenter import qs.Widgets.CenterCommandCenter
import qs.Widgets.ControlCenter import qs.Widgets.ControlCenter
import qs.Widgets.TopBar import qs.Widgets.TopBar
import qs.Common
import "./Common/Utilities.js" as Utils
ShellRoot { ShellRoot {
id: root 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 // Multi-monitor support using Variants
Variants { Variants {
model: Quickshell.screens model: Quickshell.screens
delegate: TopBar { delegate: TopBar {
modelData: item 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 // Global popup windows
CenterCommandCenter {} CenterCommandCenter {
TrayMenuPopup {} id: centerCommandCenter
}
TrayMenuPopup {
id: trayMenuPopup
}
NotificationInit {} NotificationInit {}
NotificationCenter { NotificationCenter {
notificationHistoryVisible: root.notificationHistoryVisible id: notificationCenter
onCloseRequested: {
root.notificationHistoryVisible = false
} }
ControlCenterPopup {
id: controlCenterPopup
}
WifiPasswordDialog {
id: wifiPasswordDialog
} }
ControlCenterPopup {}
WifiPasswordDialog {}
InputDialog { InputDialog {
id: globalInputDialog id: globalInputDialog
} }
BatteryControlPopup {} BatteryControlPopup {
PowerMenuPopup {} id: batteryControlPopup
PowerConfirmDialog {} }
PowerMenuPopup {
id: powerMenuPopup
}
PowerConfirmDialog {
id: powerConfirmDialog
}
ProcessListDropdown { ProcessListDropdown {
id: processListDropdown id: processListDropdown
@@ -163,24 +53,6 @@ ShellRoot {
SettingsPopup { SettingsPopup {
id: 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 // Application and clipboard components
@@ -200,17 +72,8 @@ ShellRoot {
id: clipboardHistoryPopup id: clipboardHistoryPopup
} }
IpcHandler { ToastWidget {
target: "wallpaper" id: toastWidget
}
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"
}
}
} }