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

Settings globally, refactor some menus

This commit is contained in:
bbedward
2025-07-12 14:33:50 -04:00
parent f61ee93484
commit db05496dca
17 changed files with 1100 additions and 70 deletions

View File

@@ -13,6 +13,11 @@ Singleton {
property real topBarTransparency: 0.75 property real topBarTransparency: 0.75
property var recentlyUsedApps: [] property var recentlyUsedApps: []
// New global preferences
property bool use24HourClock: true
property bool useFahrenheit: false
property bool nightModeEnabled: false
Component.onCompleted: loadSettings() Component.onCompleted: loadSettings()
@@ -49,6 +54,9 @@ Singleton {
topBarTransparency = settings.topBarTransparency !== undefined ? topBarTransparency = settings.topBarTransparency !== undefined ?
(settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75 (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75
recentlyUsedApps = settings.recentlyUsedApps || [] recentlyUsedApps = settings.recentlyUsedApps || []
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length) console.log("Loaded settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
applyStoredTheme() applyStoredTheme()
@@ -68,7 +76,10 @@ Singleton {
themeIsDynamic, themeIsDynamic,
isLightMode, isLightMode,
topBarTransparency, topBarTransparency,
recentlyUsedApps recentlyUsedApps,
use24HourClock,
useFahrenheit,
nightModeEnabled
}, null, 2)) }, null, 2))
console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length) console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
} }
@@ -133,4 +144,23 @@ Singleton {
function getRecentApps() { function getRecentApps() {
return recentlyUsedApps return recentlyUsedApps
} }
// New preference setters
function setClockFormat(use24Hour) {
console.log("Prefs setClockFormat called - use24HourClock:", use24Hour)
use24HourClock = use24Hour
saveSettings()
}
function setTemperatureUnit(fahrenheit) {
console.log("Prefs setTemperatureUnit called - useFahrenheit:", fahrenheit)
useFahrenheit = fahrenheit
saveSettings()
}
function setNightModeEnabled(enabled) {
console.log("Prefs setNightModeEnabled called - nightModeEnabled:", enabled)
nightModeEnabled = enabled
saveSettings()
}
} }

View File

@@ -0,0 +1,148 @@
import QtQuick
import Quickshell
import Quickshell.Io
pragma Singleton
pragma ComponentBehavior: Bound
Singleton {
id: root
property string username: ""
property string fullName: ""
property string profilePicture: ""
property string uptime: ""
property string hostname: ""
property bool profileAvailable: false
Component.onCompleted: {
getUserInfo()
getUptime()
// Update uptime every minute
uptimeTimer.start()
}
Timer {
id: uptimeTimer
interval: 60000 // 1 minute
running: false
repeat: true
onTriggered: getUptime()
}
// Get username and full name
Process {
id: userInfoProcess
command: ["bash", "-c", "echo \"$USER|$(getent passwd $USER | cut -d: -f5 | cut -d, -f1)|$(hostname)\""]
running: false
stdout: StdioCollector {
onStreamFinished: {
const parts = text.trim().split("|")
if (parts.length >= 3) {
root.username = parts[0] || ""
root.fullName = parts[1] || parts[0] || ""
root.hostname = parts[2] || ""
console.log("UserInfoService: User info loaded -", root.username, root.fullName, root.hostname)
// Try to find profile picture
getProfilePicture()
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to get user info")
root.username = "User"
root.fullName = "User"
root.hostname = "System"
}
}
}
// Get system uptime
Process {
id: uptimeProcess
command: ["bash", "-c", "uptime -p | sed 's/up //'"]
running: false
stdout: StdioCollector {
onStreamFinished: {
root.uptime = text.trim() || "Unknown"
console.log("UserInfoService: Uptime updated -", root.uptime)
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to get uptime")
root.uptime = "Unknown"
}
}
}
// Look for profile picture in common locations
Process {
id: profilePictureProcess
command: ["bash", "-c", `
# Try common profile picture locations
for path in \
"$HOME/.face" \
"$HOME/.face.icon" \
"/var/lib/AccountsService/icons/$USER" \
"/usr/share/pixmaps/faces/$USER" \
"/usr/share/pixmaps/faces/$USER.png" \
"/usr/share/pixmaps/faces/$USER.jpg"; do
if [ -f "$path" ]; then
echo "$path"
exit 0
fi
done
# Fallback to generic user icon
echo ""
`]
running: false
stdout: StdioCollector {
onStreamFinished: {
const path = text.trim()
if (path && path.length > 0) {
root.profilePicture = "file://" + path
root.profileAvailable = true
console.log("UserInfoService: Profile picture found at", path)
} else {
root.profilePicture = ""
root.profileAvailable = false
console.log("UserInfoService: No profile picture found, using default avatar")
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("UserInfoService: Failed to find profile picture")
root.profilePicture = ""
root.profileAvailable = false
}
}
}
function getUserInfo() {
userInfoProcess.running = true
}
function getUptime() {
uptimeProcess.running = true
}
function getProfilePicture() {
profilePictureProcess.running = true
}
function refreshUserInfo() {
getUserInfo()
getUptime()
getProfilePicture()
}
}

View File

@@ -12,4 +12,5 @@ singleton AppSearchService 1.0 AppSearchService.qml
singleton PreferencesService 1.0 PreferencesService.qml singleton PreferencesService 1.0 PreferencesService.qml
singleton LauncherService 1.0 LauncherService.qml singleton LauncherService 1.0 LauncherService.qml
singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml singleton NiriWorkspaceService 1.0 NiriWorkspaceService.qml
singleton CalendarService 1.0 CalendarService.qml singleton CalendarService 1.0 CalendarService.qml
singleton UserInfoService 1.0 UserInfoService.qml

View File

@@ -14,7 +14,6 @@ PanelWindow {
property var theme: Theme property var theme: Theme
property bool hasActiveMedia: root.hasActiveMedia property bool hasActiveMedia: root.hasActiveMedia
property var weather: root.weather property var weather: root.weather
property bool useFahrenheit: false
property bool showMediaPlayer: hasActiveMedia || hideMediaTimer.running property bool showMediaPlayer: hasActiveMedia || hideMediaTimer.running
@@ -202,7 +201,6 @@ PanelWindow {
height: weather ? 140 : 80 height: weather ? 140 : 80
theme: centerCommandCenter.theme theme: centerCommandCenter.theme
weather: centerCommandCenter.weather weather: centerCommandCenter.weather
useFahrenheit: centerCommandCenter.useFahrenheit
} }
} }

View File

@@ -237,10 +237,11 @@ Rectangle {
if (modelData.allDay) { if (modelData.allDay) {
return "All day" return "All day"
} else { } else {
let startTime = Qt.formatTime(modelData.start, "h:mm AP") let timeFormat = Prefs.use24HourClock ? "H:mm" : "h:mm AP"
let startTime = Qt.formatTime(modelData.start, timeFormat)
if (modelData.start.toDateString() !== modelData.end.toDateString() || if (modelData.start.toDateString() !== modelData.end.toDateString() ||
modelData.start.getTime() !== modelData.end.getTime()) { modelData.start.getTime() !== modelData.end.getTime()) {
return startTime + " " + Qt.formatTime(modelData.end, "h:mm AP") return startTime + " " + Qt.formatTime(modelData.end, timeFormat)
} }
return startTime return startTime
} }

View File

@@ -9,7 +9,6 @@ Rectangle {
property var theme: Theme property var theme: Theme
property var weather property var weather
property bool useFahrenheit: false
width: parent.width width: parent.width
height: parent.height height: parent.height
@@ -80,7 +79,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
Text { Text {
text: weather ? ((useFahrenheit ? weather.tempF : weather.temp) + "°" + (useFahrenheit ? "F" : "C")) : "" text: weather ? ((Prefs.useFahrenheit ? weather.tempF : 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
@@ -89,7 +88,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: if (weather) useFahrenheit = !useFahrenheit onClicked: if (weather) Prefs.setTemperatureUnit(!Prefs.useFahrenheit)
enabled: weather !== null enabled: weather !== null
} }
} }

View File

@@ -1,5 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Wayland import Quickshell.Wayland
@@ -29,7 +30,7 @@ PanelWindow {
} }
property int currentTab: 0 // 0: Network, 1: Audio, 2: Bluetooth, 3: Display property int currentTab: 0 // 0: Network, 1: Audio, 2: Bluetooth, 3: Display
property bool nightModeEnabled: false property bool powerOptionsExpanded: false
Rectangle { Rectangle {
width: Math.min(600, parent.width - Theme.spacingL * 2) width: Math.min(600, parent.width - Theme.spacingL * 2)
@@ -63,23 +64,403 @@ PanelWindow {
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
spacing: Theme.spacingM spacing: Theme.spacingM
// Header with tabs // Elegant User Header
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingL
Row { // User Info Section - Jony Ive inspired
Rectangle {
width: parent.width width: parent.width
height: 32 height: 90
radius: Theme.cornerRadiusLarge
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Text { Row {
text: "Control Center" anchors.left: parent.left
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingL
// Profile Picture
Rectangle {
width: 54
height: 54
radius: 27
color: Theme.primary
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 2
Image {
anchors.fill: parent
anchors.margins: 2
source: UserInfoService.profilePicture
fillMode: Image.PreserveAspectCrop
visible: UserInfoService.profileAvailable
smooth: true
layer.enabled: true
layer.effect: MultiEffect {
maskEnabled: true
maskSource: Rectangle {
width: parent.width
height: parent.height
radius: width / 2
visible: false
}
}
}
// Fallback icon when no profile picture
Text {
anchors.centerIn: parent
text: "person"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize + 4
color: Theme.onPrimary
visible: !UserInfoService.profileAvailable
}
}
// User Info Text
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
Text {
text: UserInfoService.fullName || UserInfoService.username || "User"
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: "Uptime: " + (UserInfoService.uptime || "Unknown")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
font.weight: Font.Normal
}
}
} }
// Action Buttons - Power and Settings
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingS
// Power Button
Rectangle {
width: 40
height: 40
radius: 20
color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ?
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Rectangle {
anchors.centerIn: parent
width: parent.width
height: parent.height
radius: parent.radius
color: "transparent"
clip: true
Text {
anchors.centerIn: parent
text: controlCenterPopup.powerOptionsExpanded ? "expand_less" : "power_settings_new"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 2
color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ? Theme.error : Theme.surfaceText
Behavior on text {
// Smooth icon transition
SequentialAnimation {
NumberAnimation {
target: parent
property: "opacity"
to: 0.0
duration: Theme.shortDuration / 2
easing.type: Theme.standardEasing
}
PropertyAction { target: parent; property: "text" }
NumberAnimation {
target: parent
property: "opacity"
to: 1.0
duration: Theme.shortDuration / 2
easing.type: Theme.standardEasing
}
}
}
}
}
MouseArea {
id: powerButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
controlCenterPopup.powerOptionsExpanded = !controlCenterPopup.powerOptionsExpanded
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Settings Button
Rectangle {
width: 40
height: 40
radius: 20
color: settingsButton.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Text {
anchors.centerIn: parent
text: "settings"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 2
color: Theme.surfaceText
}
MouseArea {
id: settingsButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.controlCenterVisible = false
root.settingsVisible = true
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
// Animated Collapsible Power Options (moved here for better integration)
Rectangle {
width: parent.width
height: controlCenterPopup.powerOptionsExpanded ? 60 : 0
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: controlCenterPopup.powerOptionsExpanded ? 1 : 0
opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0
clip: true
Behavior on height {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on border.width {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Row {
anchors.centerIn: parent
spacing: Theme.spacingL
opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
// Logout
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: logoutButton.containsMouse ?
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "logout"
font.family: Theme.iconFont
font.pixelSize: Theme.fontSizeSmall
color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Logout"
font.pixelSize: Theme.fontSizeSmall
color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: logoutButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
controlCenterPopup.powerOptionsExpanded = false
root.powerConfirmAction = "logout"
root.powerConfirmTitle = "Logout"
root.powerConfirmMessage = "Are you sure you want to logout?"
root.powerConfirmVisible = true
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Reboot
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: rebootButton.containsMouse ?
Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "restart_alt"
font.family: Theme.iconFont
font.pixelSize: Theme.fontSizeSmall
color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Restart"
font.pixelSize: Theme.fontSizeSmall
color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: rebootButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
controlCenterPopup.powerOptionsExpanded = false
root.powerConfirmAction = "reboot"
root.powerConfirmTitle = "Restart"
root.powerConfirmMessage = "Are you sure you want to restart?"
root.powerConfirmVisible = true
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
// Shutdown
Rectangle {
width: 100
height: 34
radius: Theme.cornerRadius
color: shutdownButton.containsMouse ?
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)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: "power_settings_new"
font.family: Theme.iconFont
font.pixelSize: Theme.fontSizeSmall
color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Shutdown"
font.pixelSize: Theme.fontSizeSmall
color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: shutdownButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
controlCenterPopup.powerOptionsExpanded = false
root.powerConfirmAction = "poweroff"
root.powerConfirmTitle = "Shutdown"
root.powerConfirmMessage = "Are you sure you want to shutdown?"
root.powerConfirmVisible = true
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
} }
// Tab buttons // Tab buttons
@@ -166,10 +547,32 @@ PanelWindow {
// Tab content area // Tab content area
Rectangle { Rectangle {
width: parent.width width: parent.width
height: parent.height - 120 height: {
// More generous height calculation - use most of the available space
let baseHeight = parent.height
// Subtract only the essential fixed elements
baseHeight -= 90 + Theme.spacingL // User header + spacing
baseHeight -= 40 + Theme.spacingM // Tab buttons + spacing
baseHeight -= Theme.spacingM // Bottom spacing
// Subtract power options height when expanded
if (controlCenterPopup.powerOptionsExpanded) {
baseHeight -= 60 + Theme.spacingL
}
return Math.max(300, baseHeight) // Higher minimum height for better content display
}
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
Behavior on height {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
// Network Tab // Network Tab
NetworkTab { NetworkTab {
anchors.fill: parent anchors.fill: parent
@@ -228,13 +631,6 @@ PanelWindow {
anchors.margins: Theme.spacingM anchors.margins: Theme.spacingM
visible: controlCenterPopup.currentTab === 3 visible: controlCenterPopup.currentTab === 3
// Bind properties from parent
nightModeEnabled: controlCenterPopup.nightModeEnabled
// Sync night mode state back to parent
onNightModeEnabledChanged: {
controlCenterPopup.nightModeEnabled = nightModeEnabled
}
} }
} }
} }

View File

@@ -11,21 +11,6 @@ ScrollView {
id: displayTab id: displayTab
clip: true clip: true
// These should be bound from parent
property bool nightModeEnabled: false
property real topBarTransparency: Prefs.topBarTransparency // Default transparency value
Component.onCompleted: {
// Sync with stored transparency value on startup
topBarTransparency = Prefs.topBarTransparency
}
Connections {
target: Prefs
function onTopBarTransparencyChanged() {
displayTab.topBarTransparency = Prefs.topBarTransparency
}
}
Column { Column {
width: parent.width width: parent.width
@@ -79,28 +64,28 @@ ScrollView {
width: (parent.width - Theme.spacingM) / 2 width: (parent.width - Theme.spacingM) / 2
height: 50 height: 50
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: displayTab.nightModeEnabled ? color: Prefs.nightModeEnabled ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
(nightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)) (nightModeToggle.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: displayTab.nightModeEnabled ? Theme.primary : "transparent" border.color: Prefs.nightModeEnabled ? Theme.primary : "transparent"
border.width: displayTab.nightModeEnabled ? 1 : 0 border.width: Prefs.nightModeEnabled ? 1 : 0
Column { Column {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
Text { Text {
text: displayTab.nightModeEnabled ? "nightlight" : "dark_mode" text: Prefs.nightModeEnabled ? "nightlight" : "dark_mode"
font.family: Theme.iconFont font.family: Theme.iconFont
font.pixelSize: Theme.iconSize font.pixelSize: Theme.iconSize
color: displayTab.nightModeEnabled ? Theme.primary : Theme.surfaceText color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
Text { Text {
text: "Night Mode" text: "Night Mode"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: displayTab.nightModeEnabled ? Theme.primary : Theme.surfaceText color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
@@ -113,14 +98,14 @@ ScrollView {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (displayTab.nightModeEnabled) { if (Prefs.nightModeEnabled) {
// Disable night mode - kill any running color temperature processes // Disable night mode - kill any running color temperature processes
nightModeDisableProcess.running = true nightModeDisableProcess.running = true
displayTab.nightModeEnabled = false Prefs.setNightModeEnabled(false)
} else { } else {
// Enable night mode using wlsunset or redshift // Enable night mode using wlsunset or redshift
nightModeEnableProcess.running = true nightModeEnableProcess.running = true
displayTab.nightModeEnabled = true Prefs.setNightModeEnabled(true)
} }
} }
} }
@@ -194,7 +179,7 @@ ScrollView {
CustomSlider { CustomSlider {
width: parent.width - (Theme.spacingM * 2) width: parent.width - (Theme.spacingM * 2)
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
value: Math.round(displayTab.topBarTransparency * 100) value: Math.round(Prefs.topBarTransparency * 100)
minimum: 0 minimum: 0
maximum: 100 maximum: 100
leftIcon: "opacity" leftIcon: "opacity"
@@ -204,7 +189,7 @@ ScrollView {
onSliderValueChanged: (newValue) => { onSliderValueChanged: (newValue) => {
let transparencyValue = newValue / 100.0 let transparencyValue = newValue / 100.0
displayTab.topBarTransparency = transparencyValue // Update live preview
} }
onSliderDragFinished: (finalValue) => { onSliderDragFinished: (finalValue) => {
@@ -229,7 +214,7 @@ ScrollView {
spacing: Theme.spacingS spacing: Theme.spacingS
Text { Text {
text: "Theme" text: "Theme Color"
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
@@ -251,7 +236,7 @@ ScrollView {
onExited: (exitCode) => { onExited: (exitCode) => {
if (exitCode !== 0) { if (exitCode !== 0) {
console.warn("Failed to enable night mode") console.warn("Failed to enable night mode")
displayTab.nightModeEnabled = false Prefs.setNightModeEnabled(false)
} }
} }
} }

View File

@@ -170,8 +170,7 @@ PanelWindow {
let command = [] let command = []
switch(action) { switch(action) {
case "logout": case "logout":
// Try multiple logout commands for different environments command = ["niri", "msg", "action", "quit", "-s"]
command = ["bash", "-c", "loginctl terminate-user $USER || pkill -KILL -u $USER || gnome-session-quit --force || xfce4-session-logout --logout || i3-msg exit || swaymsg exit || niri msg quit"]
break break
case "suspend": case "suspend":
command = ["systemctl", "suspend"] command = ["systemctl", "suspend"]

314
Widgets/SettingsPopup.qml Normal file
View File

@@ -0,0 +1,314 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Io
import "../Common"
PanelWindow {
id: settingsPopup
property bool settingsVisible: false
visible: settingsVisible
implicitWidth: 600
implicitHeight: 700
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
// Darkened background
Rectangle {
anchors.fill: parent
color: "black"
opacity: 0.5
MouseArea {
anchors.fill: parent
onClicked: settingsPopup.settingsVisible = false
}
}
// Main settings panel - spotlight-like centered appearance
Rectangle {
id: mainPanel
width: Math.min(600, parent.width - Theme.spacingXL * 2)
height: Math.min(700, parent.height - Theme.spacingXL * 2)
anchors.centerIn: parent
color: Theme.surfaceContainer
radius: Theme.cornerRadiusLarge
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: 1
opacity: settingsPopup.settingsVisible ? 1.0 : 0.0
scale: settingsPopup.settingsVisible ? 1.0 : 0.95
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Column {
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingL
// Header
Row {
width: parent.width
spacing: Theme.spacingM
Text {
text: "settings"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Settings"
font.pixelSize: Theme.fontSizeXLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - 200 // Spacer to push close button to the right
height: 1
}
// Close button
Rectangle {
width: 32
height: 32
radius: Theme.cornerRadius
color: closeButton.containsMouse ?
Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) :
"transparent"
Text {
text: "close"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 4
color: Theme.surfaceText
anchors.centerIn: parent
}
MouseArea {
id: closeButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: settingsPopup.settingsVisible = false
}
}
}
// Settings sections
ScrollView {
width: parent.width
height: parent.height - 80
clip: true
Column {
width: parent.width
spacing: Theme.spacingL
// Clock Settings
SettingsSection {
title: "Clock & Time"
iconName: "schedule"
content: Column {
width: parent.width
spacing: Theme.spacingM
SettingsToggle {
text: "24-Hour Format"
description: "Use 24-hour time format instead of 12-hour AM/PM"
checked: Prefs.use24HourClock
onToggled: (checked) => Prefs.setClockFormat(checked)
}
}
}
// Weather Settings
SettingsSection {
title: "Weather"
iconName: "wb_sunny"
content: Column {
width: parent.width
spacing: Theme.spacingM
SettingsToggle {
text: "Fahrenheit"
description: "Use Fahrenheit instead of Celsius for temperature"
checked: Prefs.useFahrenheit
onToggled: (checked) => Prefs.setTemperatureUnit(checked)
}
}
}
// Display Settings
SettingsSection {
title: "Display & Appearance"
iconName: "palette"
content: Column {
width: parent.width
spacing: Theme.spacingL
SettingsToggle {
text: "Night Mode"
description: "Apply warm color temperature to reduce eye strain"
checked: Prefs.nightModeEnabled
onToggled: (checked) => {
Prefs.setNightModeEnabled(checked)
if (checked) {
nightModeEnableProcess.running = true
} else {
nightModeDisableProcess.running = true
}
}
}
SettingsToggle {
text: "Light Mode"
description: "Use light theme instead of dark theme"
checked: Prefs.isLightMode
onToggled: (checked) => {
Prefs.setLightMode(checked)
Theme.isLightMode = checked
}
}
// Top Bar Transparency
Column {
width: parent.width
spacing: Theme.spacingS
Text {
text: "Top Bar Transparency"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
CustomSlider {
width: parent.width
value: Math.round(Prefs.topBarTransparency * 100)
minimum: 0
maximum: 100
leftIcon: "opacity"
rightIcon: "circle"
unit: "%"
showValue: true
onSliderDragFinished: (finalValue) => {
let transparencyValue = finalValue / 100.0
Prefs.setTopBarTransparency(transparencyValue)
}
}
Text {
text: "Adjust the transparency of the top bar background"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
// Theme Picker
Column {
width: parent.width
spacing: Theme.spacingS
Text {
text: "Theme Color"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
ThemePicker {
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
}
}
}
// Add shadow effect
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
shadowHorizontalOffset: 0
shadowVerticalOffset: 8
shadowBlur: 1.0
shadowColor: Qt.rgba(0, 0, 0, 0.3)
shadowOpacity: 0.3
}
}
// Night mode processes
Process {
id: nightModeEnableProcess
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Failed to enable night mode")
Prefs.setNightModeEnabled(false)
}
}
}
Process {
id: nightModeDisableProcess
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
running: false
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Failed to disable night mode")
}
}
}
// Keyboard focus and shortcuts
FocusScope {
anchors.fill: parent
focus: settingsPopup.settingsVisible
Keys.onEscapePressed: settingsPopup.settingsVisible = false
}
}

View File

@@ -0,0 +1,48 @@
import QtQuick
import "../Common"
Column {
id: root
property string title: ""
property string iconName: ""
property alias content: contentLoader.sourceComponent
width: parent.width
spacing: Theme.spacingM
// Section header
Row {
width: parent.width
spacing: Theme.spacingS
Text {
text: iconName
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize - 2
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: title
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
// Divider
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
}
// Content
Loader {
id: contentLoader
width: parent.width
}
}

112
Widgets/SettingsToggle.qml Normal file
View File

@@ -0,0 +1,112 @@
import QtQuick
import "../Common"
Rectangle {
id: root
property string text: ""
property string description: ""
property bool checked: false
signal toggled(bool checked)
width: parent.width
height: 60
radius: Theme.cornerRadius
color: toggleArea.containsMouse ?
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Row {
anchors.left: parent.left
anchors.right: toggle.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingXS
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
Text {
text: root.text
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
Text {
text: root.description
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: Math.min(implicitWidth, root.width - 120)
visible: root.description.length > 0
}
}
}
// Toggle switch
Rectangle {
id: toggle
width: 48
height: 24
radius: 12
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
color: root.checked ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Rectangle {
id: toggleHandle
width: 20
height: 20
radius: 10
anchors.verticalCenter: parent.verticalCenter
x: root.checked ? parent.width - width - 2 : 2
color: root.checked ? Theme.onPrimary : Theme.surfaceText
Behavior on x {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
MouseArea {
id: toggleArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.checked = !root.checked
root.toggled(root.checked)
}
}
}

View File

@@ -28,7 +28,7 @@ Rectangle {
spacing: Theme.spacingS spacing: Theme.spacingS
Text { Text {
text: Qt.formatTime(root.currentDate, "h:mm AP") text: Prefs.use24HourClock ? Qt.formatTime(root.currentDate, "H:mm") : Qt.formatTime(root.currentDate, "h:mm AP")
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium

View File

@@ -36,7 +36,6 @@ PanelWindow {
property string weatherCode: "" property string weatherCode: ""
property int weatherTemp: 0 property int weatherTemp: 0
property int weatherTempF: 0 property int weatherTempF: 0
property bool useFahrenheit: false
property string osLogo: "" property string osLogo: ""
property string networkStatus: "disconnected" property string networkStatus: "disconnected"
property string wifiSignalStrength: "good" property string wifiSignalStrength: "good"
@@ -204,7 +203,6 @@ PanelWindow {
weatherCode: topBar.weatherCode weatherCode: topBar.weatherCode
weatherTemp: topBar.weatherTemp weatherTemp: topBar.weatherTemp
weatherTempF: topBar.weatherTempF weatherTempF: topBar.weatherTempF
useFahrenheit: topBar.useFahrenheit
onClicked: { onClicked: {
if (topBar.shellRoot) { if (topBar.shellRoot) {
@@ -312,11 +310,6 @@ PanelWindow {
} }
} }
} }
// Power Button
PowerButton {
anchors.verticalCenter: parent.verticalCenter
}
} }
} }
} }

View File

@@ -9,7 +9,6 @@ Rectangle {
property string weatherCode: "" property string weatherCode: ""
property int weatherTemp: 0 property int weatherTemp: 0
property int weatherTempF: 0 property int weatherTempF: 0
property bool useFahrenheit: false
signal clicked() signal clicked()
@@ -49,7 +48,7 @@ Rectangle {
} }
Text { Text {
text: (useFahrenheit ? weatherTempF : weatherTemp) + "°" text: (Prefs.useFahrenheit ? weatherTempF : weatherTemp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium

View File

@@ -15,4 +15,7 @@ PowerConfirmDialog 1.0 PowerConfirmDialog.qml
ThemePicker 1.0 ThemePicker.qml ThemePicker 1.0 ThemePicker.qml
CpuMonitorWidget 1.0 CpuMonitorWidget.qml CpuMonitorWidget 1.0 CpuMonitorWidget.qml
RamMonitorWidget 1.0 RamMonitorWidget.qml RamMonitorWidget 1.0 RamMonitorWidget.qml
SpotlightLauncher 1.0 SpotlightLauncher.qml SpotlightLauncher 1.0 SpotlightLauncher.qml
SettingsPopup 1.0 SettingsPopup.qml
SettingsSection 1.0 SettingsSection.qml
SettingsToggle 1.0 SettingsToggle.qml

View File

@@ -46,6 +46,7 @@ ShellRoot {
property string powerConfirmAction: "" property string powerConfirmAction: ""
property string powerConfirmTitle: "" property string powerConfirmTitle: ""
property string powerConfirmMessage: "" property string powerConfirmMessage: ""
property bool settingsVisible: false
// Network properties from NetworkService // Network properties from NetworkService
property string networkStatus: NetworkService.networkStatus property string networkStatus: NetworkService.networkStatus
@@ -161,7 +162,6 @@ ShellRoot {
property var weather: WeatherService.weather property var weather: WeatherService.weather
// Weather configuration // Weather configuration
property bool useFahrenheit: true // Default to Fahrenheit
// WiFi Auto-refresh Timer // WiFi Auto-refresh Timer
@@ -295,7 +295,6 @@ ShellRoot {
weatherCode: root.weather.wCode weatherCode: root.weather.wCode
weatherTemp: root.weather.temp weatherTemp: root.weather.temp
weatherTempF: root.weather.tempF weatherTempF: root.weather.tempF
useFahrenheit: root.useFahrenheit
osLogo: root.osLogo osLogo: root.osLogo
networkStatus: root.networkStatus networkStatus: root.networkStatus
wifiSignalStrength: root.wifiSignalStrength wifiSignalStrength: root.wifiSignalStrength
@@ -340,6 +339,11 @@ ShellRoot {
PowerMenuPopup {} PowerMenuPopup {}
PowerConfirmDialog {} PowerConfirmDialog {}
SettingsPopup {
id: settingsPopup
settingsVisible: root.settingsVisible
}
// Application and clipboard components // Application and clipboard components
AppLauncher { AppLauncher {
id: appLauncher id: appLauncher