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:
@@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
148
Services/UserInfoService.qml
Normal file
148
Services/UserInfoService.qml
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
314
Widgets/SettingsPopup.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
48
Widgets/SettingsSection.qml
Normal file
48
Widgets/SettingsSection.qml
Normal 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
112
Widgets/SettingsToggle.qml
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user