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 var recentlyUsedApps: []
// New global preferences
property bool use24HourClock: true
property bool useFahrenheit: false
property bool nightModeEnabled: false
Component.onCompleted: loadSettings()
@@ -49,6 +54,9 @@ Singleton {
topBarTransparency = settings.topBarTransparency !== undefined ?
(settings.topBarTransparency > 1 ? settings.topBarTransparency / 100.0 : settings.topBarTransparency) : 0.75
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)
applyStoredTheme()
@@ -68,7 +76,10 @@ Singleton {
themeIsDynamic,
isLightMode,
topBarTransparency,
recentlyUsedApps
recentlyUsedApps,
use24HourClock,
useFahrenheit,
nightModeEnabled
}, null, 2))
console.log("Saving settings - themeIndex:", themeIndex, "isDynamic:", themeIsDynamic, "lightMode:", isLightMode, "transparency:", topBarTransparency, "recentApps:", recentlyUsedApps.length)
}
@@ -133,4 +144,23 @@ Singleton {
function getRecentApps() {
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

@@ -13,3 +13,4 @@ singleton PreferencesService 1.0 PreferencesService.qml
singleton LauncherService 1.0 LauncherService.qml
singleton NiriWorkspaceService 1.0 NiriWorkspaceService.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 bool hasActiveMedia: root.hasActiveMedia
property var weather: root.weather
property bool useFahrenheit: false
property bool showMediaPlayer: hasActiveMedia || hideMediaTimer.running
@@ -202,7 +201,6 @@ PanelWindow {
height: weather ? 140 : 80
theme: centerCommandCenter.theme
weather: centerCommandCenter.weather
useFahrenheit: centerCommandCenter.useFahrenheit
}
}

View File

@@ -237,10 +237,11 @@ Rectangle {
if (modelData.allDay) {
return "All day"
} 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() ||
modelData.start.getTime() !== modelData.end.getTime()) {
return startTime + " " + Qt.formatTime(modelData.end, "h:mm AP")
return startTime + " " + Qt.formatTime(modelData.end, timeFormat)
}
return startTime
}

View File

@@ -9,7 +9,6 @@ Rectangle {
property var theme: Theme
property var weather
property bool useFahrenheit: false
width: parent.width
height: parent.height
@@ -80,7 +79,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
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
color: theme.surfaceText
font.weight: Font.Light
@@ -89,7 +88,7 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: if (weather) useFahrenheit = !useFahrenheit
onClicked: if (weather) Prefs.setTemperatureUnit(!Prefs.useFahrenheit)
enabled: weather !== null
}
}

View File

@@ -1,5 +1,6 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
@@ -29,7 +30,7 @@ PanelWindow {
}
property int currentTab: 0 // 0: Network, 1: Audio, 2: Bluetooth, 3: Display
property bool nightModeEnabled: false
property bool powerOptionsExpanded: false
Rectangle {
width: Math.min(600, parent.width - Theme.spacingL * 2)
@@ -63,23 +64,403 @@ PanelWindow {
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
// Header with tabs
// Elegant User Header
Column {
width: parent.width
spacing: Theme.spacingM
spacing: Theme.spacingL
// User Info Section - Jony Ive inspired
Rectangle {
width: parent.width
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
Row {
anchors.left: parent.left
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: 32
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: "Control Center"
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
@@ -166,10 +547,32 @@ PanelWindow {
// Tab content area
Rectangle {
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
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
NetworkTab {
anchors.fill: parent
@@ -228,13 +631,6 @@ PanelWindow {
anchors.margins: Theme.spacingM
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
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 {
width: parent.width
@@ -79,28 +64,28 @@ ScrollView {
width: (parent.width - Theme.spacingM) / 2
height: 50
radius: Theme.cornerRadius
color: displayTab.nightModeEnabled ?
color: Prefs.nightModeEnabled ?
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))
border.color: displayTab.nightModeEnabled ? Theme.primary : "transparent"
border.width: displayTab.nightModeEnabled ? 1 : 0
border.color: Prefs.nightModeEnabled ? Theme.primary : "transparent"
border.width: Prefs.nightModeEnabled ? 1 : 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
Text {
text: displayTab.nightModeEnabled ? "nightlight" : "dark_mode"
text: Prefs.nightModeEnabled ? "nightlight" : "dark_mode"
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: displayTab.nightModeEnabled ? Theme.primary : Theme.surfaceText
color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: "Night Mode"
font.pixelSize: Theme.fontSizeSmall
color: displayTab.nightModeEnabled ? Theme.primary : Theme.surfaceText
color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -113,14 +98,14 @@ ScrollView {
cursorShape: Qt.PointingHandCursor
onClicked: {
if (displayTab.nightModeEnabled) {
if (Prefs.nightModeEnabled) {
// Disable night mode - kill any running color temperature processes
nightModeDisableProcess.running = true
displayTab.nightModeEnabled = false
Prefs.setNightModeEnabled(false)
} else {
// Enable night mode using wlsunset or redshift
nightModeEnableProcess.running = true
displayTab.nightModeEnabled = true
Prefs.setNightModeEnabled(true)
}
}
}
@@ -194,7 +179,7 @@ ScrollView {
CustomSlider {
width: parent.width - (Theme.spacingM * 2)
anchors.horizontalCenter: parent.horizontalCenter
value: Math.round(displayTab.topBarTransparency * 100)
value: Math.round(Prefs.topBarTransparency * 100)
minimum: 0
maximum: 100
leftIcon: "opacity"
@@ -204,7 +189,7 @@ ScrollView {
onSliderValueChanged: (newValue) => {
let transparencyValue = newValue / 100.0
displayTab.topBarTransparency = transparencyValue
// Update live preview
}
onSliderDragFinished: (finalValue) => {
@@ -229,7 +214,7 @@ ScrollView {
spacing: Theme.spacingS
Text {
text: "Theme"
text: "Theme Color"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
@@ -251,7 +236,7 @@ ScrollView {
onExited: (exitCode) => {
if (exitCode !== 0) {
console.warn("Failed to enable night mode")
displayTab.nightModeEnabled = false
Prefs.setNightModeEnabled(false)
}
}
}

View File

@@ -170,8 +170,7 @@ PanelWindow {
let command = []
switch(action) {
case "logout":
// Try multiple logout commands for different environments
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"]
command = ["niri", "msg", "action", "quit", "-s"]
break
case "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
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
color: Theme.surfaceText
font.weight: Font.Medium

View File

@@ -36,7 +36,6 @@ PanelWindow {
property string weatherCode: ""
property int weatherTemp: 0
property int weatherTempF: 0
property bool useFahrenheit: false
property string osLogo: ""
property string networkStatus: "disconnected"
property string wifiSignalStrength: "good"
@@ -204,7 +203,6 @@ PanelWindow {
weatherCode: topBar.weatherCode
weatherTemp: topBar.weatherTemp
weatherTempF: topBar.weatherTempF
useFahrenheit: topBar.useFahrenheit
onClicked: {
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 int weatherTemp: 0
property int weatherTempF: 0
property bool useFahrenheit: false
signal clicked()
@@ -49,7 +48,7 @@ Rectangle {
}
Text {
text: (useFahrenheit ? weatherTempF : weatherTemp) + "°"
text: (Prefs.useFahrenheit ? weatherTempF : weatherTemp) + "°" + (Prefs.useFahrenheit ? "F" : "C")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium

View File

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