mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
Compare commits
215 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a038fa3c9 | ||
|
|
841e55d37f | ||
|
|
287dda5675 | ||
|
|
37120776be | ||
|
|
d67bcb66c9 | ||
|
|
acf6e72f35 | ||
|
|
0964b271f5 | ||
|
|
1f7998fc44 | ||
|
|
7ac59f7bd0 | ||
|
|
bf238640af | ||
|
|
6e638cadb7 | ||
|
|
ab0759f441 | ||
|
|
123ec5c78a | ||
|
|
d262c67832 | ||
|
|
fcb9a2838d | ||
|
|
82d1672741 | ||
|
|
657a8b6094 | ||
|
|
165f7e0720 | ||
|
|
91aba84c29 | ||
|
|
02c4ac1bc7 | ||
|
|
c529959027 | ||
|
|
e875d1a5d7 | ||
|
|
d280505b9f | ||
|
|
fdd9d0000b | ||
|
|
74c793eedf | ||
|
|
bd8976c620 | ||
|
|
a3e2563f9b | ||
|
|
a310e3d8fa | ||
|
|
ae9da35af1 | ||
|
|
b45837ff5c | ||
|
|
3dd9ab8a29 | ||
|
|
5ebd2f6b8e | ||
|
|
977043ac92 | ||
|
|
4c6182b79c | ||
|
|
01125e092c | ||
|
|
99ef447a52 | ||
|
|
4722ff9b97 | ||
|
|
23a19df79a | ||
|
|
97b86c6faa | ||
|
|
e6296f20b9 | ||
|
|
0ee9dcc255 | ||
|
|
c7b4e2c49d | ||
|
|
066d1847e4 | ||
|
|
3155bf24c1 | ||
|
|
92bb5b90aa | ||
|
|
a207ed74ff | ||
|
|
9d224113c4 | ||
|
|
934f4b2210 | ||
|
|
a6a41d4de1 | ||
|
|
185ee20f2d | ||
|
|
778b960130 | ||
|
|
184938d10a | ||
|
|
25565af5f9 | ||
|
|
0bd9a4f860 | ||
|
|
fe64dd1dea | ||
|
|
a4a59fd586 | ||
|
|
7ccd2d9418 | ||
|
|
974dd70a06 | ||
|
|
1690e4f63b | ||
|
|
ec75ef468b | ||
|
|
a57c1e6451 | ||
|
|
bdc79a13a9 | ||
|
|
b893694977 | ||
|
|
9be7d44765 | ||
|
|
3f8d8ca379 | ||
|
|
e50c3cceeb | ||
|
|
14e648911d | ||
|
|
3c42a618d4 | ||
|
|
bed2259944 | ||
|
|
7516d44de9 | ||
|
|
e9a61a4f81 | ||
|
|
7a7d7d053a | ||
|
|
2067e44baf | ||
|
|
8e010478c7 | ||
|
|
ec4f0ff2ed | ||
|
|
c04177e45d | ||
|
|
b9b1737639 | ||
|
|
4468e4c6af | ||
|
|
cf5dec733d | ||
|
|
d9b9da4b3d | ||
|
|
d62ef89bc3 | ||
|
|
1b681e68b9 | ||
|
|
d15ee0c29b | ||
|
|
62b7b30754 | ||
|
|
aa52b586d6 | ||
|
|
ca11735c1d | ||
|
|
78683032aa | ||
|
|
bd5064e567 | ||
|
|
3862f0ced5 | ||
|
|
878c1d9385 | ||
|
|
0a5f635a2d | ||
|
|
6a963ed618 | ||
|
|
35f059b1dc | ||
|
|
b7838e497d | ||
|
|
3edfff388c | ||
|
|
0bfaf2a8f8 | ||
|
|
50f6dfa709 | ||
|
|
897f48d151 | ||
|
|
c43f58e1bf | ||
|
|
de532fdeda | ||
|
|
c21896b0df | ||
|
|
1669be4dd9 | ||
|
|
81550229e8 | ||
|
|
f38ffa1de5 | ||
|
|
66b493fb2b | ||
|
|
ceae254160 | ||
|
|
a3b3e49313 | ||
|
|
8fb17f7fd5 | ||
|
|
c464bb3e47 | ||
|
|
bd72cf3adf | ||
|
|
a734d9b497 | ||
|
|
b18814e086 | ||
|
|
74417a18ea | ||
|
|
227081c5c9 | ||
|
|
c3b3edcae8 | ||
|
|
c8f87085a0 | ||
|
|
118375f004 | ||
|
|
babcc66765 | ||
|
|
64c8e79bf2 | ||
|
|
12e8e72bb2 | ||
|
|
72b79c0f51 | ||
|
|
50e440f212 | ||
|
|
7c1c64fefc | ||
|
|
2e4770f4be | ||
|
|
0b2bf0b83d | ||
|
|
1fbb614aa8 | ||
|
|
55a29d8504 | ||
|
|
ffc13f71b7 | ||
|
|
d4816bd174 | ||
|
|
3cac6e5eea | ||
|
|
50bc3db048 | ||
|
|
2b99e61c65 | ||
|
|
306a0c0e2d | ||
|
|
0ba4ee74b5 | ||
|
|
2b5a5115b7 | ||
|
|
8a7e386adb | ||
|
|
6e6412fffc | ||
|
|
bc540056fc | ||
|
|
0055ddbc8d | ||
|
|
3376dc893d | ||
|
|
4a673fe1b5 | ||
|
|
6639f6ead0 | ||
|
|
2ce32e8bb5 | ||
|
|
4d13a3d909 | ||
|
|
691b6da7a7 | ||
|
|
e6265c2f71 | ||
|
|
de9bb43c26 | ||
|
|
1ae9802866 | ||
|
|
0e887408c5 | ||
|
|
88b7657e34 | ||
|
|
5fcffba73e | ||
|
|
542536455b | ||
|
|
97f0acadb6 | ||
|
|
32f3e579e7 | ||
|
|
bc446dabaf | ||
|
|
d8e2a73e0b | ||
|
|
ae6fd42163 | ||
|
|
cc7363eee1 | ||
|
|
38c4b33d15 | ||
|
|
6c81aa0908 | ||
|
|
102cc3c0b6 | ||
|
|
02300024cf | ||
|
|
344761df00 | ||
|
|
c0edaea716 | ||
|
|
75e660f78e | ||
|
|
be4d9cadba | ||
|
|
fdaac6ac8b | ||
|
|
5c540f826e | ||
|
|
55b3bffccd | ||
|
|
0cb2a96b3e | ||
|
|
9f13ac6963 | ||
|
|
19e148f65c | ||
|
|
7d9334916c | ||
|
|
50dedb55ac | ||
|
|
38f61ceae2 | ||
|
|
fff4b8c817 | ||
|
|
0fe76b7575 | ||
|
|
3c4719d430 | ||
|
|
c04e2ae0ff | ||
|
|
fec921df10 | ||
|
|
5186afce06 | ||
|
|
77ea6a9372 | ||
|
|
afb0435b5c | ||
|
|
903ef0cc72 | ||
|
|
e4f86abda9 | ||
|
|
8ee43de145 | ||
|
|
83b29c5ed9 | ||
|
|
cdf4ca3b3b | ||
|
|
a0192e709e | ||
|
|
59c7246989 | ||
|
|
76ee483b27 | ||
|
|
ba6c7ae28c | ||
|
|
07c1677145 | ||
|
|
3db1a2ccf2 | ||
|
|
6d84553da9 | ||
|
|
77bc129dfc | ||
|
|
2c451b9779 | ||
|
|
3c6bb58088 | ||
|
|
0372c44d98 | ||
|
|
8fc40ab12d | ||
|
|
9e1272029e | ||
|
|
27344a47e2 | ||
|
|
c42681b9b9 | ||
|
|
939f47286e | ||
|
|
c8cb5dc146 | ||
|
|
c3759dc542 | ||
|
|
17b49ad1f9 | ||
|
|
63c54b5611 | ||
|
|
988d5c2fc3 | ||
|
|
7a671b586c | ||
|
|
b36da8eba2 | ||
|
|
5b49e36da4 | ||
|
|
2395274714 | ||
|
|
d0cbe689f8 | ||
|
|
4ae157af35 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -63,6 +63,11 @@ CLAUDE-temp.md
|
||||
niri-colors.generated.kdl
|
||||
ghostty-colors.generated.conf
|
||||
|
||||
# Notepad files (should be in ~/.local/state/DankMaterialShell/)
|
||||
untitled-*.txt
|
||||
file:*
|
||||
notepad-files/
|
||||
|
||||
result
|
||||
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
@@ -96,4 +101,4 @@ go.work.sum
|
||||
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
# .vscode/
|
||||
# .vscode/
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are welcome and encourages.
|
||||
Contributions are welcome and encouraged.
|
||||
|
||||
## Formatting
|
||||
|
||||
@@ -27,4 +27,4 @@ Sometimes it just breaks code though. Like turning `"_\""` into `"_""`, so you m
|
||||
|
||||
## Pull request
|
||||
|
||||
Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting.
|
||||
Include screenshots/video if applicable in your pull request if applicable, to visualize what your change is affecting.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
pragma Singleton
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
@@ -18,6 +18,11 @@ Singleton {
|
||||
property string profileLastPath: ""
|
||||
property bool perMonitorWallpaper: false
|
||||
property var monitorWallpapers: ({})
|
||||
property bool perModeWallpaper: false
|
||||
property string wallpaperPathLight: ""
|
||||
property string wallpaperPathDark: ""
|
||||
property var monitorWallpapersLight: ({})
|
||||
property var monitorWallpapersDark: ({})
|
||||
property bool doNotDisturb: false
|
||||
property bool nightModeEnabled: false
|
||||
property int nightModeTemperature: 4500
|
||||
@@ -26,7 +31,7 @@ Singleton {
|
||||
|
||||
property bool hasTriedDefaultSession: false
|
||||
readonly property string _stateUrl: StandardPaths.writableLocation(StandardPaths.GenericStateLocation)
|
||||
readonly property string _stateDir: _stateUrl.startsWith("file://") ? _stateUrl.substring(7) : _stateUrl
|
||||
readonly property string _stateDir: Paths.strip(_stateUrl)
|
||||
property int nightModeStartHour: 18
|
||||
property int nightModeStartMinute: 0
|
||||
property int nightModeEndHour: 6
|
||||
@@ -43,13 +48,27 @@ Singleton {
|
||||
property string wallpaperCyclingMode: "interval" // "interval" or "time"
|
||||
property int wallpaperCyclingInterval: 300 // seconds (5 minutes)
|
||||
property string wallpaperCyclingTime: "06:00" // HH:mm format
|
||||
property var monitorCyclingSettings: ({})
|
||||
property string lastBrightnessDevice: ""
|
||||
property string notepadContent: ""
|
||||
property string notepadCurrentFileName: ""
|
||||
property string notepadCurrentFileUrl: ""
|
||||
property string notepadLastSavedContent: ""
|
||||
property var notepadTabs: []
|
||||
property int notepadCurrentTabIndex: 0
|
||||
property string launchPrefix: ""
|
||||
property string wallpaperTransition: "fade"
|
||||
readonly property var availableWallpaperTransitions: ["none", "fade", "wipe", "disc", "stripes", "iris bloom", "pixelate", "portal"]
|
||||
property var includedTransitions: availableWallpaperTransitions.filter(t => t !== "none")
|
||||
|
||||
// Power management settings - AC Power
|
||||
property int acMonitorTimeout: 0 // Never
|
||||
property int acLockTimeout: 0 // Never
|
||||
property int acSuspendTimeout: 0 // Never
|
||||
property int acHibernateTimeout: 0 // Never
|
||||
|
||||
// Power management settings - Battery
|
||||
property int batteryMonitorTimeout: 0 // Never
|
||||
property int batteryLockTimeout: 0 // Never
|
||||
property int batterySuspendTimeout: 0 // Never
|
||||
property int batteryHibernateTimeout: 0 // Never
|
||||
|
||||
property bool lockBeforeSuspend: false
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
loadSettings()
|
||||
@@ -69,6 +88,11 @@ Singleton {
|
||||
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : ""
|
||||
perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false
|
||||
monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {}
|
||||
perModeWallpaper = settings.perModeWallpaper !== undefined ? settings.perModeWallpaper : false
|
||||
wallpaperPathLight = settings.wallpaperPathLight !== undefined ? settings.wallpaperPathLight : ""
|
||||
wallpaperPathDark = settings.wallpaperPathDark !== undefined ? settings.wallpaperPathDark : ""
|
||||
monitorWallpapersLight = settings.monitorWallpapersLight !== undefined ? settings.monitorWallpapersLight : {}
|
||||
monitorWallpapersDark = settings.monitorWallpapersDark !== undefined ? settings.monitorWallpapersDark : {}
|
||||
doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
|
||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
|
||||
nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
|
||||
@@ -103,46 +127,26 @@ Singleton {
|
||||
wallpaperCyclingMode = settings.wallpaperCyclingMode !== undefined ? settings.wallpaperCyclingMode : "interval"
|
||||
wallpaperCyclingInterval = settings.wallpaperCyclingInterval !== undefined ? settings.wallpaperCyclingInterval : 300
|
||||
wallpaperCyclingTime = settings.wallpaperCyclingTime !== undefined ? settings.wallpaperCyclingTime : "06:00"
|
||||
monitorCyclingSettings = settings.monitorCyclingSettings !== undefined ? settings.monitorCyclingSettings : {}
|
||||
lastBrightnessDevice = settings.lastBrightnessDevice !== undefined ? settings.lastBrightnessDevice : ""
|
||||
notepadContent = settings.notepadContent !== undefined ? settings.notepadContent : ""
|
||||
launchPrefix = settings.launchPrefix !== undefined ? settings.launchPrefix : ""
|
||||
wallpaperTransition = settings.wallpaperTransition !== undefined ? settings.wallpaperTransition : "fade"
|
||||
includedTransitions = settings.includedTransitions !== undefined ? settings.includedTransitions : availableWallpaperTransitions.filter(t => t !== "none")
|
||||
|
||||
acMonitorTimeout = settings.acMonitorTimeout !== undefined ? settings.acMonitorTimeout : 0
|
||||
acLockTimeout = settings.acLockTimeout !== undefined ? settings.acLockTimeout : 0
|
||||
acSuspendTimeout = settings.acSuspendTimeout !== undefined ? settings.acSuspendTimeout : 0
|
||||
acHibernateTimeout = settings.acHibernateTimeout !== undefined ? settings.acHibernateTimeout : 0
|
||||
batteryMonitorTimeout = settings.batteryMonitorTimeout !== undefined ? settings.batteryMonitorTimeout : 0
|
||||
batteryLockTimeout = settings.batteryLockTimeout !== undefined ? settings.batteryLockTimeout : 0
|
||||
batterySuspendTimeout = settings.batterySuspendTimeout !== undefined ? settings.batterySuspendTimeout : 0
|
||||
batteryHibernateTimeout = settings.batteryHibernateTimeout !== undefined ? settings.batteryHibernateTimeout : 0
|
||||
lockBeforeSuspend = settings.lockBeforeSuspend !== undefined ? settings.lockBeforeSuspend : false
|
||||
|
||||
// Generate system themes but don't override user's theme choice
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
notepadCurrentFileName = settings.notepadCurrentFileName !== undefined ? settings.notepadCurrentFileName : ""
|
||||
notepadCurrentFileUrl = settings.notepadCurrentFileUrl !== undefined ? settings.notepadCurrentFileUrl : ""
|
||||
notepadLastSavedContent = settings.notepadLastSavedContent !== undefined ? settings.notepadLastSavedContent : ""
|
||||
notepadTabs = settings.notepadTabs !== undefined ? settings.notepadTabs : []
|
||||
notepadCurrentTabIndex = settings.notepadCurrentTabIndex !== undefined ? settings.notepadCurrentTabIndex : 0
|
||||
|
||||
// Migrate legacy single notepad to tabs if needed
|
||||
if (notepadTabs.length === 0 && (notepadContent || notepadCurrentFileName)) {
|
||||
notepadTabs = [{
|
||||
id: Date.now(),
|
||||
title: notepadCurrentFileName || "Untitled",
|
||||
content: notepadContent,
|
||||
fileName: notepadCurrentFileName,
|
||||
fileUrl: notepadCurrentFileUrl,
|
||||
lastSavedContent: notepadLastSavedContent,
|
||||
hasUnsavedChanges: false
|
||||
}]
|
||||
notepadCurrentTabIndex = 0
|
||||
}
|
||||
|
||||
// Ensure at least one tab exists
|
||||
if (notepadTabs.length === 0) {
|
||||
notepadTabs = [{
|
||||
id: Date.now(),
|
||||
title: "Untitled",
|
||||
content: "",
|
||||
fileName: "",
|
||||
fileUrl: "",
|
||||
lastSavedContent: "",
|
||||
hasUnsavedChanges: false
|
||||
}]
|
||||
notepadCurrentTabIndex = 0
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -157,6 +161,11 @@ Singleton {
|
||||
"profileLastPath": profileLastPath,
|
||||
"perMonitorWallpaper": perMonitorWallpaper,
|
||||
"monitorWallpapers": monitorWallpapers,
|
||||
"perModeWallpaper": perModeWallpaper,
|
||||
"wallpaperPathLight": wallpaperPathLight,
|
||||
"wallpaperPathDark": wallpaperPathDark,
|
||||
"monitorWallpapersLight": monitorWallpapersLight,
|
||||
"monitorWallpapersDark": monitorWallpapersDark,
|
||||
"doNotDisturb": doNotDisturb,
|
||||
"nightModeEnabled": nightModeEnabled,
|
||||
"nightModeTemperature": nightModeTemperature,
|
||||
@@ -178,21 +187,40 @@ Singleton {
|
||||
"wallpaperCyclingMode": wallpaperCyclingMode,
|
||||
"wallpaperCyclingInterval": wallpaperCyclingInterval,
|
||||
"wallpaperCyclingTime": wallpaperCyclingTime,
|
||||
"monitorCyclingSettings": monitorCyclingSettings,
|
||||
"lastBrightnessDevice": lastBrightnessDevice,
|
||||
"notepadContent": notepadContent,
|
||||
"notepadCurrentFileName": notepadCurrentFileName,
|
||||
"notepadCurrentFileUrl": notepadCurrentFileUrl,
|
||||
"notepadLastSavedContent": notepadLastSavedContent,
|
||||
"notepadTabs": notepadTabs,
|
||||
"notepadCurrentTabIndex": notepadCurrentTabIndex
|
||||
"launchPrefix": launchPrefix,
|
||||
"wallpaperTransition": wallpaperTransition,
|
||||
"includedTransitions": includedTransitions,
|
||||
"acMonitorTimeout": acMonitorTimeout,
|
||||
"acLockTimeout": acLockTimeout,
|
||||
"acSuspendTimeout": acSuspendTimeout,
|
||||
"acHibernateTimeout": acHibernateTimeout,
|
||||
"batteryMonitorTimeout": batteryMonitorTimeout,
|
||||
"batteryLockTimeout": batteryLockTimeout,
|
||||
"batterySuspendTimeout": batterySuspendTimeout,
|
||||
"batteryHibernateTimeout": batteryHibernateTimeout,
|
||||
"lockBeforeSuspend": lockBeforeSuspend
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
function setLightMode(lightMode) {
|
||||
isLightMode = lightMode
|
||||
syncWallpaperForCurrentMode()
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function syncWallpaperForCurrentMode() {
|
||||
if (!perModeWallpaper) return
|
||||
|
||||
if (perMonitorWallpaper) {
|
||||
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
|
||||
return
|
||||
}
|
||||
|
||||
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
|
||||
}
|
||||
|
||||
function setDoNotDisturb(enabled) {
|
||||
doNotDisturb = enabled
|
||||
saveSettings()
|
||||
@@ -263,24 +291,32 @@ Singleton {
|
||||
|
||||
function setWallpaper(imagePath) {
|
||||
wallpaperPath = imagePath
|
||||
if (perModeWallpaper) {
|
||||
if (isLightMode) {
|
||||
wallpaperPathLight = imagePath
|
||||
} else {
|
||||
wallpaperPathDark = imagePath
|
||||
}
|
||||
}
|
||||
saveSettings()
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
if (Theme.currentTheme === Theme.dynamic) {
|
||||
Theme.extractColors()
|
||||
}
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
|
||||
function setWallpaperColor(color) {
|
||||
wallpaperPath = color
|
||||
if (perModeWallpaper) {
|
||||
if (isLightMode) {
|
||||
wallpaperPathLight = color
|
||||
} else {
|
||||
wallpaperPathDark = color
|
||||
}
|
||||
}
|
||||
saveSettings()
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
if (Theme.currentTheme === Theme.dynamic) {
|
||||
Theme.extractColors()
|
||||
}
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
@@ -374,17 +410,102 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function getMonitorCyclingSettings(screenName) {
|
||||
return monitorCyclingSettings[screenName] || {
|
||||
enabled: false,
|
||||
mode: "interval",
|
||||
interval: 300,
|
||||
time: "06:00"
|
||||
}
|
||||
}
|
||||
|
||||
function setMonitorCyclingEnabled(screenName, enabled) {
|
||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||
if (!newSettings[screenName]) {
|
||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||
}
|
||||
newSettings[screenName].enabled = enabled
|
||||
monitorCyclingSettings = newSettings
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setMonitorCyclingMode(screenName, mode) {
|
||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||
if (!newSettings[screenName]) {
|
||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||
}
|
||||
newSettings[screenName].mode = mode
|
||||
monitorCyclingSettings = newSettings
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setMonitorCyclingInterval(screenName, interval) {
|
||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||
if (!newSettings[screenName]) {
|
||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||
}
|
||||
newSettings[screenName].interval = interval
|
||||
monitorCyclingSettings = newSettings
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setMonitorCyclingTime(screenName, time) {
|
||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||
if (!newSettings[screenName]) {
|
||||
newSettings[screenName] = { enabled: false, mode: "interval", interval: 300, time: "06:00" }
|
||||
}
|
||||
newSettings[screenName].time = time
|
||||
monitorCyclingSettings = newSettings
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setPerMonitorWallpaper(enabled) {
|
||||
perMonitorWallpaper = enabled
|
||||
|
||||
// Disable automatic cycling when per-monitor mode is enabled
|
||||
if (enabled && wallpaperCyclingEnabled) {
|
||||
wallpaperCyclingEnabled = false
|
||||
if (enabled && perModeWallpaper) {
|
||||
syncWallpaperForCurrentMode()
|
||||
}
|
||||
saveSettings()
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
|
||||
function setPerModeWallpaper(enabled) {
|
||||
if (enabled && wallpaperCyclingEnabled) {
|
||||
setWallpaperCyclingEnabled(false)
|
||||
}
|
||||
if (enabled && perMonitorWallpaper) {
|
||||
var monitorCyclingAny = false
|
||||
for (var key in monitorCyclingSettings) {
|
||||
if (monitorCyclingSettings[key].enabled) {
|
||||
monitorCyclingAny = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (monitorCyclingAny) {
|
||||
var newSettings = Object.assign({}, monitorCyclingSettings)
|
||||
for (var screenName in newSettings) {
|
||||
newSettings[screenName].enabled = false
|
||||
}
|
||||
monitorCyclingSettings = newSettings
|
||||
}
|
||||
}
|
||||
|
||||
perModeWallpaper = enabled
|
||||
if (enabled) {
|
||||
if (perMonitorWallpaper) {
|
||||
monitorWallpapersLight = Object.assign({}, monitorWallpapers)
|
||||
monitorWallpapersDark = Object.assign({}, monitorWallpapers)
|
||||
} else {
|
||||
wallpaperPathLight = wallpaperPath
|
||||
wallpaperPathDark = wallpaperPath
|
||||
}
|
||||
} else {
|
||||
syncWallpaperForCurrentMode()
|
||||
}
|
||||
|
||||
saveSettings()
|
||||
|
||||
// Refresh dynamic theming when per-monitor mode changes
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
@@ -398,16 +519,32 @@ Singleton {
|
||||
delete newMonitorWallpapers[screenName]
|
||||
}
|
||||
monitorWallpapers = newMonitorWallpapers
|
||||
|
||||
if (perModeWallpaper) {
|
||||
if (isLightMode) {
|
||||
var newLight = Object.assign({}, monitorWallpapersLight)
|
||||
if (path && path !== "") {
|
||||
newLight[screenName] = path
|
||||
} else {
|
||||
delete newLight[screenName]
|
||||
}
|
||||
monitorWallpapersLight = newLight
|
||||
} else {
|
||||
var newDark = Object.assign({}, monitorWallpapersDark)
|
||||
if (path && path !== "") {
|
||||
newDark[screenName] = path
|
||||
} else {
|
||||
delete newDark[screenName]
|
||||
}
|
||||
monitorWallpapersDark = newDark
|
||||
}
|
||||
}
|
||||
|
||||
saveSettings()
|
||||
|
||||
// Trigger dynamic theming if this is the first monitor and dynamic theming is enabled
|
||||
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
|
||||
var screens = Quickshell.screens
|
||||
if (screens.length > 0 && screenName === screens[0].name) {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.wallpaperDynamicTheming) {
|
||||
Theme.switchTheme("dynamic")
|
||||
Theme.extractColors()
|
||||
}
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
@@ -425,6 +562,61 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setLaunchPrefix(prefix) {
|
||||
launchPrefix = prefix
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setWallpaperTransition(transition) {
|
||||
wallpaperTransition = transition
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setAcMonitorTimeout(timeout) {
|
||||
acMonitorTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setAcLockTimeout(timeout) {
|
||||
acLockTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setAcSuspendTimeout(timeout) {
|
||||
acSuspendTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBatteryMonitorTimeout(timeout) {
|
||||
batteryMonitorTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBatteryLockTimeout(timeout) {
|
||||
batteryLockTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBatterySuspendTimeout(timeout) {
|
||||
batterySuspendTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setAcHibernateTimeout(timeout) {
|
||||
acHibernateTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setBatteryHibernateTimeout(timeout) {
|
||||
batteryHibernateTimeout = timeout
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setLockBeforeSuspend(enabled) {
|
||||
lockBeforeSuspend = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: settingsFile
|
||||
|
||||
@@ -594,4 +786,4 @@ Singleton {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,34 @@ import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
enum Position {
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
enum AnimationSpeed {
|
||||
None,
|
||||
Shortest,
|
||||
Short,
|
||||
Medium,
|
||||
Long
|
||||
}
|
||||
|
||||
// Theme settings
|
||||
property string currentThemeName: "blue"
|
||||
property string customThemeFile: ""
|
||||
property real topBarTransparency: 0.75
|
||||
property real topBarWidgetTransparency: 0.85
|
||||
property real popupTransparency: 0.92
|
||||
property string matugenScheme: "scheme-tonal-spot"
|
||||
property real dankBarTransparency: 1.0
|
||||
property real dankBarWidgetTransparency: 1.0
|
||||
property real popupTransparency: 1.0
|
||||
property real dockTransparency: 1
|
||||
property bool use24HourClock: true
|
||||
property bool useFahrenheit: false
|
||||
@@ -44,12 +61,23 @@ Singleton {
|
||||
property bool controlCenterShowNetworkIcon: true
|
||||
property bool controlCenterShowBluetoothIcon: true
|
||||
property bool controlCenterShowAudioIcon: true
|
||||
property var controlCenterWidgets: [
|
||||
{"id": "volumeSlider", "enabled": true, "width": 50},
|
||||
{"id": "brightnessSlider", "enabled": true, "width": 50},
|
||||
{"id": "wifi", "enabled": true, "width": 50},
|
||||
{"id": "bluetooth", "enabled": true, "width": 50},
|
||||
{"id": "audioOutput", "enabled": true, "width": 50},
|
||||
{"id": "audioInput", "enabled": true, "width": 50},
|
||||
{"id": "nightMode", "enabled": true, "width": 50},
|
||||
{"id": "darkMode", "enabled": true, "width": 50}
|
||||
]
|
||||
property bool showWorkspaceIndex: false
|
||||
property bool showWorkspacePadding: false
|
||||
property bool showWorkspaceApps: false
|
||||
property int maxWorkspaceIcons: 3
|
||||
property bool workspacesPerMonitor: true
|
||||
property var workspaceNameIcons: ({})
|
||||
property bool waveProgressEnabled: true
|
||||
property bool clockCompactMode: false
|
||||
property bool focusedWindowCompactMode: false
|
||||
property bool runningAppsCompactMode: true
|
||||
@@ -57,12 +85,13 @@ Singleton {
|
||||
property string clockDateFormat: ""
|
||||
property string lockDateFormat: ""
|
||||
property int mediaSize: 1
|
||||
property var topBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"]
|
||||
property var topBarCenterWidgets: ["music", "clock", "weather"]
|
||||
property var topBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
|
||||
property alias topBarLeftWidgetsModel: leftWidgetsModel
|
||||
property alias topBarCenterWidgetsModel: centerWidgetsModel
|
||||
property alias topBarRightWidgetsModel: rightWidgetsModel
|
||||
property var dankBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"]
|
||||
property var dankBarCenterWidgets: ["music", "clock", "weather"]
|
||||
property var dankBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
|
||||
property var dankBarWidgetOrder: []
|
||||
property alias dankBarLeftWidgetsModel: leftWidgetsModel
|
||||
property alias dankBarCenterWidgetsModel: centerWidgetsModel
|
||||
property alias dankBarRightWidgetsModel: rightWidgetsModel
|
||||
property string appLauncherViewMode: "list"
|
||||
property string spotlightModalViewMode: "list"
|
||||
property string networkPreference: "auto"
|
||||
@@ -76,42 +105,74 @@ Singleton {
|
||||
property string osLogoColorOverride: ""
|
||||
property real osLogoBrightness: 0.5
|
||||
property real osLogoContrast: 1
|
||||
property bool wallpaperDynamicTheming: true
|
||||
property bool weatherEnabled: true
|
||||
property string fontFamily: "Inter Variable"
|
||||
property string monoFontFamily: "Fira Code"
|
||||
property int fontWeight: Font.Normal
|
||||
property real fontScale: 1.0
|
||||
property bool notepadUseMonospace: true
|
||||
property string notepadFontFamily: ""
|
||||
property real notepadFontSize: 14
|
||||
property bool notepadShowLineNumbers: false
|
||||
property real notepadTransparencyOverride: -1
|
||||
property real notepadLastCustomTransparency: 0.7
|
||||
|
||||
onNotepadUseMonospaceChanged: saveSettings()
|
||||
onNotepadFontFamilyChanged: saveSettings()
|
||||
onNotepadFontSizeChanged: saveSettings()
|
||||
onNotepadShowLineNumbersChanged: saveSettings()
|
||||
onNotepadTransparencyOverrideChanged: {
|
||||
if (notepadTransparencyOverride > 0) {
|
||||
notepadLastCustomTransparency = notepadTransparencyOverride
|
||||
}
|
||||
saveSettings()
|
||||
}
|
||||
onNotepadLastCustomTransparencyChanged: saveSettings()
|
||||
property bool gtkThemingEnabled: false
|
||||
property bool qtThemingEnabled: false
|
||||
property bool showDock: false
|
||||
property bool dockAutoHide: false
|
||||
property bool dockGroupByApp: false
|
||||
property bool dockOpenOnOverview: false
|
||||
property int dockPosition: SettingsData.Position.Bottom
|
||||
property real dockSpacing: 4
|
||||
property real dockBottomGap: 0
|
||||
property real cornerRadius: 12
|
||||
property bool notificationOverlayEnabled: false
|
||||
property bool topBarAutoHide: false
|
||||
property bool topBarOpenOnOverview: false
|
||||
property bool topBarVisible: true
|
||||
property real topBarSpacing: 4
|
||||
property real topBarBottomGap: 0
|
||||
property real topBarInnerPadding: 8
|
||||
property bool topBarSquareCorners: false
|
||||
property bool topBarNoBackground: false
|
||||
property bool dankBarAutoHide: false
|
||||
property bool dankBarOpenOnOverview: false
|
||||
property bool dankBarVisible: true
|
||||
property real dankBarSpacing: 4
|
||||
property real dankBarBottomGap: 0
|
||||
property real dankBarInnerPadding: 4
|
||||
property bool dankBarSquareCorners: false
|
||||
property bool dankBarNoBackground: false
|
||||
property bool dankBarGothCornersEnabled: false
|
||||
property int dankBarPosition: SettingsData.Position.Top
|
||||
property bool dankBarIsVertical: dankBarPosition === SettingsData.Position.Left || dankBarPosition === SettingsData.Position.Right
|
||||
property bool lockScreenShowPowerActions: true
|
||||
property bool hideBrightnessSlider: false
|
||||
property string widgetBackgroundColor: "sch"
|
||||
property string surfaceBase: "s"
|
||||
property int notificationTimeoutLow: 5000
|
||||
property int notificationTimeoutNormal: 5000
|
||||
property int notificationTimeoutCritical: 0
|
||||
property int notificationPopupPosition: SettingsData.Position.Top
|
||||
property var screenPreferences: ({})
|
||||
property int animationSpeed: SettingsData.AnimationSpeed.Short
|
||||
readonly property string defaultFontFamily: "Inter Variable"
|
||||
readonly property string defaultMonoFontFamily: "Fira Code"
|
||||
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
|
||||
readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation)
|
||||
readonly property string _configDir: _configUrl.startsWith("file://") ? _configUrl.substring(7) : _configUrl
|
||||
readonly property string _configDir: Paths.strip(_configUrl)
|
||||
|
||||
signal forceTopBarLayoutRefresh
|
||||
signal forceDankBarLayoutRefresh
|
||||
signal forceDockLayoutRefresh
|
||||
signal widgetDataChanged
|
||||
signal workspaceIconsUpdated
|
||||
|
||||
property bool _loading: false
|
||||
|
||||
function getEffectiveTimeFormat() {
|
||||
if (use24HourClock) {
|
||||
return Locale.ShortFormat
|
||||
@@ -136,22 +197,26 @@ Singleton {
|
||||
"enabled": true,
|
||||
"size": 20,
|
||||
"selectedGpuIndex": 0,
|
||||
"pciId": ""
|
||||
"pciId": "",
|
||||
"mountPath": "/"
|
||||
}
|
||||
leftWidgetsModel.append(dummyItem)
|
||||
centerWidgetsModel.append(dummyItem)
|
||||
rightWidgetsModel.append(dummyItem)
|
||||
|
||||
updateListModel(leftWidgetsModel, topBarLeftWidgets)
|
||||
updateListModel(centerWidgetsModel, topBarCenterWidgets)
|
||||
updateListModel(rightWidgetsModel, topBarRightWidgets)
|
||||
updateListModel(leftWidgetsModel, dankBarLeftWidgets)
|
||||
updateListModel(centerWidgetsModel, dankBarCenterWidgets)
|
||||
updateListModel(rightWidgetsModel, dankBarRightWidgets)
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
_loading = true
|
||||
parseSettings(settingsFile.text())
|
||||
_loading = false
|
||||
}
|
||||
|
||||
function parseSettings(content) {
|
||||
_loading = true
|
||||
try {
|
||||
if (content && content.trim()) {
|
||||
var settings = JSON.parse(content)
|
||||
@@ -168,9 +233,10 @@ Singleton {
|
||||
currentThemeName = settings.currentThemeName !== undefined ? settings.currentThemeName : "blue"
|
||||
}
|
||||
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
|
||||
topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75
|
||||
topBarWidgetTransparency = settings.topBarWidgetTransparency !== undefined ? (settings.topBarWidgetTransparency > 1 ? settings.topBarWidgetTransparency / 100 : settings.topBarWidgetTransparency) : 0.85
|
||||
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 0.92
|
||||
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
|
||||
dankBarTransparency = settings.dankBarTransparency !== undefined ? (settings.dankBarTransparency > 1 ? settings.dankBarTransparency / 100 : settings.dankBarTransparency) : (settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 1.0)
|
||||
dankBarWidgetTransparency = settings.dankBarWidgetTransparency !== undefined ? (settings.dankBarWidgetTransparency > 1 ? settings.dankBarWidgetTransparency / 100 : settings.dankBarWidgetTransparency) : (settings.topBarWidgetTransparency !== undefined ? (settings.topBarWidgetTransparency > 1 ? settings.topBarWidgetTransparency / 100 : settings.topBarWidgetTransparency) : 1.0)
|
||||
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 1.0
|
||||
dockTransparency = settings.dockTransparency !== undefined ? (settings.dockTransparency > 1 ? settings.dockTransparency / 100 : settings.dockTransparency) : 1
|
||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
|
||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false
|
||||
@@ -199,12 +265,23 @@ Singleton {
|
||||
controlCenterShowNetworkIcon = settings.controlCenterShowNetworkIcon !== undefined ? settings.controlCenterShowNetworkIcon : true
|
||||
controlCenterShowBluetoothIcon = settings.controlCenterShowBluetoothIcon !== undefined ? settings.controlCenterShowBluetoothIcon : true
|
||||
controlCenterShowAudioIcon = settings.controlCenterShowAudioIcon !== undefined ? settings.controlCenterShowAudioIcon : true
|
||||
controlCenterWidgets = settings.controlCenterWidgets !== undefined ? settings.controlCenterWidgets : [
|
||||
{"id": "volumeSlider", "enabled": true, "width": 50},
|
||||
{"id": "brightnessSlider", "enabled": true, "width": 50},
|
||||
{"id": "wifi", "enabled": true, "width": 50},
|
||||
{"id": "bluetooth", "enabled": true, "width": 50},
|
||||
{"id": "audioOutput", "enabled": true, "width": 50},
|
||||
{"id": "audioInput", "enabled": true, "width": 50},
|
||||
{"id": "nightMode", "enabled": true, "width": 50},
|
||||
{"id": "darkMode", "enabled": true, "width": 50}
|
||||
]
|
||||
showWorkspaceIndex = settings.showWorkspaceIndex !== undefined ? settings.showWorkspaceIndex : false
|
||||
showWorkspacePadding = settings.showWorkspacePadding !== undefined ? settings.showWorkspacePadding : false
|
||||
showWorkspaceApps = settings.showWorkspaceApps !== undefined ? settings.showWorkspaceApps : false
|
||||
maxWorkspaceIcons = settings.maxWorkspaceIcons !== undefined ? settings.maxWorkspaceIcons : 3
|
||||
workspaceNameIcons = settings.workspaceNameIcons !== undefined ? settings.workspaceNameIcons : ({})
|
||||
workspacesPerMonitor = settings.workspacesPerMonitor !== undefined ? settings.workspacesPerMonitor : true
|
||||
waveProgressEnabled = settings.waveProgressEnabled !== undefined ? settings.waveProgressEnabled : true
|
||||
clockCompactMode = settings.clockCompactMode !== undefined ? settings.clockCompactMode : false
|
||||
focusedWindowCompactMode = settings.focusedWindowCompactMode !== undefined ? settings.focusedWindowCompactMode : false
|
||||
runningAppsCompactMode = settings.runningAppsCompactMode !== undefined ? settings.runningAppsCompactMode : true
|
||||
@@ -212,23 +289,24 @@ Singleton {
|
||||
clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : ""
|
||||
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
|
||||
mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1)
|
||||
if (settings.topBarWidgetOrder) {
|
||||
topBarLeftWidgets = settings.topBarWidgetOrder.filter(w => {
|
||||
if (settings.dankBarWidgetOrder || settings.topBarWidgetOrder) {
|
||||
var widgetOrder = settings.dankBarWidgetOrder || settings.topBarWidgetOrder
|
||||
dankBarLeftWidgets = widgetOrder.filter(w => {
|
||||
return ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w)
|
||||
})
|
||||
topBarCenterWidgets = settings.topBarWidgetOrder.filter(w => {
|
||||
dankBarCenterWidgets = widgetOrder.filter(w => {
|
||||
return ["clock", "music", "weather"].includes(w)
|
||||
})
|
||||
topBarRightWidgets = settings.topBarWidgetOrder.filter(w => {
|
||||
dankBarRightWidgets = widgetOrder.filter(w => {
|
||||
return ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w)
|
||||
})
|
||||
} else {
|
||||
var leftWidgets = settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"]
|
||||
var centerWidgets = settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"]
|
||||
var rightWidgets = settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
|
||||
topBarLeftWidgets = leftWidgets
|
||||
topBarCenterWidgets = centerWidgets
|
||||
topBarRightWidgets = rightWidgets
|
||||
var leftWidgets = settings.dankBarLeftWidgets !== undefined ? settings.dankBarLeftWidgets : (settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"])
|
||||
var centerWidgets = settings.dankBarCenterWidgets !== undefined ? settings.dankBarCenterWidgets : (settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"])
|
||||
var rightWidgets = settings.dankBarRightWidgets !== undefined ? settings.dankBarRightWidgets : (settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"])
|
||||
dankBarLeftWidgets = leftWidgets
|
||||
dankBarCenterWidgets = centerWidgets
|
||||
dankBarRightWidgets = rightWidgets
|
||||
updateListModel(leftWidgetsModel, leftWidgets)
|
||||
updateListModel(centerWidgetsModel, centerWidgets)
|
||||
updateListModel(rightWidgetsModel, rightWidgets)
|
||||
@@ -241,31 +319,47 @@ Singleton {
|
||||
osLogoColorOverride = settings.osLogoColorOverride !== undefined ? settings.osLogoColorOverride : ""
|
||||
osLogoBrightness = settings.osLogoBrightness !== undefined ? settings.osLogoBrightness : 0.5
|
||||
osLogoContrast = settings.osLogoContrast !== undefined ? settings.osLogoContrast : 1
|
||||
wallpaperDynamicTheming = settings.wallpaperDynamicTheming !== undefined ? settings.wallpaperDynamicTheming : true
|
||||
fontFamily = settings.fontFamily !== undefined ? settings.fontFamily : defaultFontFamily
|
||||
monoFontFamily = settings.monoFontFamily !== undefined ? settings.monoFontFamily : defaultMonoFontFamily
|
||||
fontWeight = settings.fontWeight !== undefined ? settings.fontWeight : Font.Normal
|
||||
fontScale = settings.fontScale !== undefined ? settings.fontScale : 1.0
|
||||
notepadUseMonospace = settings.notepadUseMonospace !== undefined ? settings.notepadUseMonospace : true
|
||||
notepadFontFamily = settings.notepadFontFamily !== undefined ? settings.notepadFontFamily : ""
|
||||
notepadFontSize = settings.notepadFontSize !== undefined ? settings.notepadFontSize : 14
|
||||
notepadShowLineNumbers = settings.notepadShowLineNumbers !== undefined ? settings.notepadShowLineNumbers : false
|
||||
notepadTransparencyOverride = settings.notepadTransparencyOverride !== undefined ? settings.notepadTransparencyOverride : -1
|
||||
notepadLastCustomTransparency = settings.notepadLastCustomTransparency !== undefined ? settings.notepadLastCustomTransparency : 0.95
|
||||
gtkThemingEnabled = settings.gtkThemingEnabled !== undefined ? settings.gtkThemingEnabled : false
|
||||
qtThemingEnabled = settings.qtThemingEnabled !== undefined ? settings.qtThemingEnabled : false
|
||||
showDock = settings.showDock !== undefined ? settings.showDock : false
|
||||
dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false
|
||||
dockGroupByApp = settings.dockGroupByApp !== undefined ? settings.dockGroupByApp : false
|
||||
dockPosition = settings.dockPosition !== undefined ? settings.dockPosition : SettingsData.Position.Bottom
|
||||
dockSpacing = settings.dockSpacing !== undefined ? settings.dockSpacing : 4
|
||||
dockBottomGap = settings.dockBottomGap !== undefined ? settings.dockBottomGap : 0
|
||||
cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
|
||||
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
|
||||
topBarAutoHide = settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false
|
||||
topBarOpenOnOverview = settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false
|
||||
topBarVisible = settings.topBarVisible !== undefined ? settings.topBarVisible : true
|
||||
dankBarAutoHide = settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : (settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false)
|
||||
dankBarOpenOnOverview = settings.dankBarOpenOnOverview !== undefined ? settings.dankBarOpenOnOverview : (settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false)
|
||||
dankBarVisible = settings.dankBarVisible !== undefined ? settings.dankBarVisible : (settings.topBarVisible !== undefined ? settings.topBarVisible : true)
|
||||
dockOpenOnOverview = settings.dockOpenOnOverview !== undefined ? settings.dockOpenOnOverview : false
|
||||
notificationTimeoutLow = settings.notificationTimeoutLow !== undefined ? settings.notificationTimeoutLow : 5000
|
||||
notificationTimeoutNormal = settings.notificationTimeoutNormal !== undefined ? settings.notificationTimeoutNormal : 5000
|
||||
notificationTimeoutCritical = settings.notificationTimeoutCritical !== undefined ? settings.notificationTimeoutCritical : 0
|
||||
topBarSpacing = settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4
|
||||
topBarBottomGap = settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0
|
||||
topBarInnerPadding = settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 8
|
||||
topBarSquareCorners = settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false
|
||||
topBarNoBackground = settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : false
|
||||
notificationPopupPosition = settings.notificationPopupPosition !== undefined ? settings.notificationPopupPosition : SettingsData.Position.Top
|
||||
dankBarSpacing = settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : (settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4)
|
||||
dankBarBottomGap = settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : (settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0)
|
||||
dankBarInnerPadding = settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : (settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 4)
|
||||
dankBarSquareCorners = settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : (settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false)
|
||||
dankBarNoBackground = settings.dankBarNoBackground !== undefined ? settings.dankBarNoBackground : (settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : false)
|
||||
dankBarGothCornersEnabled = settings.dankBarGothCornersEnabled !== undefined ? settings.dankBarGothCornersEnabled : (settings.topBarGothCornersEnabled !== undefined ? settings.topBarGothCornersEnabled : false)
|
||||
dankBarPosition = settings.dankBarPosition !== undefined ? settings.dankBarPosition : (settings.dankBarAtBottom !== undefined ? (settings.dankBarAtBottom ? SettingsData.Position.Bottom : SettingsData.Position.Top) : (settings.topBarAtBottom !== undefined ? (settings.topBarAtBottom ? SettingsData.Position.Bottom : SettingsData.Position.Top) : SettingsData.Position.Top))
|
||||
lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true
|
||||
hideBrightnessSlider = settings.hideBrightnessSlider !== undefined ? settings.hideBrightnessSlider : false
|
||||
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch"
|
||||
surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s"
|
||||
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
|
||||
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||
applyStoredTheme()
|
||||
detectAvailableIconThemes()
|
||||
detectQtTools()
|
||||
@@ -276,15 +370,20 @@ Singleton {
|
||||
}
|
||||
} catch (e) {
|
||||
applyStoredTheme()
|
||||
} finally {
|
||||
_loading = false
|
||||
}
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
if (_loading)
|
||||
return
|
||||
settingsFile.setText(JSON.stringify({
|
||||
"currentThemeName": currentThemeName,
|
||||
"customThemeFile": customThemeFile,
|
||||
"topBarTransparency": topBarTransparency,
|
||||
"topBarWidgetTransparency": topBarWidgetTransparency,
|
||||
"matugenScheme": matugenScheme,
|
||||
"dankBarTransparency": dankBarTransparency,
|
||||
"dankBarWidgetTransparency": dankBarWidgetTransparency,
|
||||
"popupTransparency": popupTransparency,
|
||||
"dockTransparency": dockTransparency,
|
||||
"use24HourClock": use24HourClock,
|
||||
@@ -314,12 +413,14 @@ Singleton {
|
||||
"controlCenterShowNetworkIcon": controlCenterShowNetworkIcon,
|
||||
"controlCenterShowBluetoothIcon": controlCenterShowBluetoothIcon,
|
||||
"controlCenterShowAudioIcon": controlCenterShowAudioIcon,
|
||||
"controlCenterWidgets": controlCenterWidgets,
|
||||
"showWorkspaceIndex": showWorkspaceIndex,
|
||||
"showWorkspacePadding": showWorkspacePadding,
|
||||
"showWorkspaceApps": showWorkspaceApps,
|
||||
"maxWorkspaceIcons": maxWorkspaceIcons,
|
||||
"workspacesPerMonitor": workspacesPerMonitor,
|
||||
"workspaceNameIcons": workspaceNameIcons,
|
||||
"waveProgressEnabled": waveProgressEnabled,
|
||||
"clockCompactMode": clockCompactMode,
|
||||
"focusedWindowCompactMode": focusedWindowCompactMode,
|
||||
"runningAppsCompactMode": runningAppsCompactMode,
|
||||
@@ -327,9 +428,9 @@ Singleton {
|
||||
"clockDateFormat": clockDateFormat,
|
||||
"lockDateFormat": lockDateFormat,
|
||||
"mediaSize": mediaSize,
|
||||
"topBarLeftWidgets": topBarLeftWidgets,
|
||||
"topBarCenterWidgets": topBarCenterWidgets,
|
||||
"topBarRightWidgets": topBarRightWidgets,
|
||||
"dankBarLeftWidgets": dankBarLeftWidgets,
|
||||
"dankBarCenterWidgets": dankBarCenterWidgets,
|
||||
"dankBarRightWidgets": dankBarRightWidgets,
|
||||
"appLauncherViewMode": appLauncherViewMode,
|
||||
"spotlightModalViewMode": spotlightModalViewMode,
|
||||
"networkPreference": networkPreference,
|
||||
@@ -338,31 +439,47 @@ Singleton {
|
||||
"osLogoColorOverride": osLogoColorOverride,
|
||||
"osLogoBrightness": osLogoBrightness,
|
||||
"osLogoContrast": osLogoContrast,
|
||||
"wallpaperDynamicTheming": wallpaperDynamicTheming,
|
||||
"fontFamily": fontFamily,
|
||||
"monoFontFamily": monoFontFamily,
|
||||
"fontWeight": fontWeight,
|
||||
"fontScale": fontScale,
|
||||
"notepadUseMonospace": notepadUseMonospace,
|
||||
"notepadFontFamily": notepadFontFamily,
|
||||
"notepadFontSize": notepadFontSize,
|
||||
"notepadShowLineNumbers": notepadShowLineNumbers,
|
||||
"notepadTransparencyOverride": notepadTransparencyOverride,
|
||||
"notepadLastCustomTransparency": notepadLastCustomTransparency,
|
||||
"gtkThemingEnabled": gtkThemingEnabled,
|
||||
"qtThemingEnabled": qtThemingEnabled,
|
||||
"showDock": showDock,
|
||||
"dockAutoHide": dockAutoHide,
|
||||
"dockGroupByApp": dockGroupByApp,
|
||||
"dockOpenOnOverview": dockOpenOnOverview,
|
||||
"dockPosition": dockPosition,
|
||||
"dockSpacing": dockSpacing,
|
||||
"dockBottomGap": dockBottomGap,
|
||||
"cornerRadius": cornerRadius,
|
||||
"notificationOverlayEnabled": notificationOverlayEnabled,
|
||||
"topBarAutoHide": topBarAutoHide,
|
||||
"topBarOpenOnOverview": topBarOpenOnOverview,
|
||||
"topBarVisible": topBarVisible,
|
||||
"topBarSpacing": topBarSpacing,
|
||||
"topBarBottomGap": topBarBottomGap,
|
||||
"topBarInnerPadding": topBarInnerPadding,
|
||||
"topBarSquareCorners": topBarSquareCorners,
|
||||
"topBarNoBackground": topBarNoBackground,
|
||||
"dankBarAutoHide": dankBarAutoHide,
|
||||
"dankBarOpenOnOverview": dankBarOpenOnOverview,
|
||||
"dankBarVisible": dankBarVisible,
|
||||
"dankBarSpacing": dankBarSpacing,
|
||||
"dankBarBottomGap": dankBarBottomGap,
|
||||
"dankBarInnerPadding": dankBarInnerPadding,
|
||||
"dankBarSquareCorners": dankBarSquareCorners,
|
||||
"dankBarNoBackground": dankBarNoBackground,
|
||||
"dankBarGothCornersEnabled": dankBarGothCornersEnabled,
|
||||
"dankBarPosition": dankBarPosition,
|
||||
"lockScreenShowPowerActions": lockScreenShowPowerActions,
|
||||
"hideBrightnessSlider": hideBrightnessSlider,
|
||||
"widgetBackgroundColor": widgetBackgroundColor,
|
||||
"surfaceBase": surfaceBase,
|
||||
"notificationTimeoutLow": notificationTimeoutLow,
|
||||
"notificationTimeoutNormal": notificationTimeoutNormal,
|
||||
"notificationTimeoutCritical": notificationTimeoutCritical,
|
||||
"screenPreferences": screenPreferences
|
||||
"notificationPopupPosition": notificationPopupPosition,
|
||||
"screenPreferences": screenPreferences,
|
||||
"animationSpeed": animationSpeed
|
||||
}, null, 2))
|
||||
}
|
||||
|
||||
@@ -391,6 +508,11 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setWaveProgressEnabled(enabled) {
|
||||
waveProgressEnabled = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setWorkspaceNameIcon(workspaceName, iconData) {
|
||||
var iconMap = JSON.parse(JSON.stringify(workspaceNameIcons))
|
||||
iconMap[workspaceName] = iconData
|
||||
@@ -473,11 +595,11 @@ Singleton {
|
||||
|
||||
function applyStoredTheme() {
|
||||
if (typeof Theme !== "undefined")
|
||||
Theme.switchTheme(currentThemeName, false)
|
||||
Theme.switchTheme(currentThemeName, false, false)
|
||||
else
|
||||
Qt.callLater(() => {
|
||||
if (typeof Theme !== "undefined")
|
||||
Theme.switchTheme(currentThemeName, false)
|
||||
Theme.switchTheme(currentThemeName, false, false)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -491,13 +613,26 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarTransparency(transparency) {
|
||||
topBarTransparency = transparency
|
||||
function setMatugenScheme(scheme) {
|
||||
var normalized = scheme || "scheme-tonal-spot"
|
||||
if (matugenScheme === normalized)
|
||||
return
|
||||
|
||||
matugenScheme = normalized
|
||||
saveSettings()
|
||||
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
|
||||
function setDankBarTransparency(transparency) {
|
||||
dankBarTransparency = transparency
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarWidgetTransparency(transparency) {
|
||||
topBarWidgetTransparency = transparency
|
||||
function setDankBarWidgetTransparency(transparency) {
|
||||
dankBarWidgetTransparency = transparency
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
@@ -627,26 +762,30 @@ Singleton {
|
||||
controlCenterShowAudioIcon = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarWidgetOrder(order) {
|
||||
topBarWidgetOrder = order
|
||||
function setControlCenterWidgets(widgets) {
|
||||
controlCenterWidgets = widgets
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarLeftWidgets(order) {
|
||||
topBarLeftWidgets = order
|
||||
function setDankBarWidgetOrder(order) {
|
||||
dankBarWidgetOrder = order
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDankBarLeftWidgets(order) {
|
||||
dankBarLeftWidgets = order
|
||||
updateListModel(leftWidgetsModel, order)
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarCenterWidgets(order) {
|
||||
topBarCenterWidgets = order
|
||||
function setDankBarCenterWidgets(order) {
|
||||
dankBarCenterWidgets = order
|
||||
updateListModel(centerWidgetsModel, order)
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarRightWidgets(order) {
|
||||
topBarRightWidgets = order
|
||||
function setDankBarRightWidgets(order) {
|
||||
dankBarRightWidgets = order
|
||||
updateListModel(rightWidgetsModel, order)
|
||||
saveSettings()
|
||||
}
|
||||
@@ -659,6 +798,7 @@ Singleton {
|
||||
var size = typeof order[i] === "string" ? undefined : order[i].size
|
||||
var selectedGpuIndex = typeof order[i] === "string" ? undefined : order[i].selectedGpuIndex
|
||||
var pciId = typeof order[i] === "string" ? undefined : order[i].pciId
|
||||
var mountPath = typeof order[i] === "string" ? undefined : order[i].mountPath
|
||||
var item = {
|
||||
"widgetId": widgetId,
|
||||
"enabled": enabled
|
||||
@@ -669,6 +809,8 @@ Singleton {
|
||||
item.selectedGpuIndex = selectedGpuIndex
|
||||
if (pciId !== undefined)
|
||||
item.pciId = pciId
|
||||
if (mountPath !== undefined)
|
||||
item.mountPath = mountPath
|
||||
|
||||
listModel.append(item)
|
||||
}
|
||||
@@ -676,13 +818,13 @@ Singleton {
|
||||
widgetDataChanged()
|
||||
}
|
||||
|
||||
function resetTopBarWidgetsToDefault() {
|
||||
function resetDankBarWidgetsToDefault() {
|
||||
var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"]
|
||||
var defaultCenter = ["music", "clock", "weather"]
|
||||
var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"]
|
||||
topBarLeftWidgets = defaultLeft
|
||||
topBarCenterWidgets = defaultCenter
|
||||
topBarRightWidgets = defaultRight
|
||||
dankBarLeftWidgets = defaultLeft
|
||||
dankBarCenterWidgets = defaultCenter
|
||||
dankBarRightWidgets = defaultRight
|
||||
updateListModel(leftWidgetsModel, defaultLeft)
|
||||
updateListModel(centerWidgetsModel, defaultCenter)
|
||||
updateListModel(rightWidgetsModel, defaultRight)
|
||||
@@ -773,7 +915,7 @@ Singleton {
|
||||
|
||||
function updateQtIconTheme(themeName) {
|
||||
var qtThemeName = (themeName === "System Default") ? "" : themeName
|
||||
var home = _shq(root._homeUrl.replace("file://", ""))
|
||||
var home = _shq(Paths.strip(root._homeUrl))
|
||||
if (!qtThemeName) {
|
||||
// When "System Default" is selected, don't modify the config files at all
|
||||
// This preserves the user's existing qt6ct configuration
|
||||
@@ -811,11 +953,6 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setWallpaperDynamicTheming(enabled) {
|
||||
wallpaperDynamicTheming = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setFontFamily(family) {
|
||||
fontFamily = family
|
||||
saveSettings()
|
||||
@@ -854,6 +991,14 @@ Singleton {
|
||||
|
||||
function setShowDock(enabled) {
|
||||
showDock = enabled
|
||||
if (enabled && dankBarPosition === SettingsData.Position.Top) {
|
||||
setDockPosition(SettingsData.Position.Bottom)
|
||||
return
|
||||
}
|
||||
if (enabled && dankBarPosition === SettingsData.Position.Top) {
|
||||
setDockPosition(SettingsData.Position.Bottom)
|
||||
return
|
||||
}
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
@@ -862,6 +1007,16 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDockGroupByApp(enabled) {
|
||||
dockGroupByApp = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setdockOpenOnOverview(enabled) {
|
||||
dockOpenOnOverview = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setCornerRadius(radius) {
|
||||
cornerRadius = radius
|
||||
saveSettings()
|
||||
@@ -872,23 +1027,23 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarAutoHide(enabled) {
|
||||
topBarAutoHide = enabled
|
||||
function setDankBarAutoHide(enabled) {
|
||||
dankBarAutoHide = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarOpenOnOverview(enabled) {
|
||||
topBarOpenOnOverview = enabled
|
||||
function setDankBarOpenOnOverview(enabled) {
|
||||
dankBarOpenOnOverview = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarVisible(visible) {
|
||||
topBarVisible = visible
|
||||
function setDankBarVisible(visible) {
|
||||
dankBarVisible = visible
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function toggleTopBarVisible() {
|
||||
topBarVisible = !topBarVisible
|
||||
function toggleDankBarVisible() {
|
||||
dankBarVisible = !dankBarVisible
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
@@ -907,31 +1062,148 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarSpacing(spacing) {
|
||||
topBarSpacing = spacing
|
||||
function setNotificationPopupPosition(position) {
|
||||
notificationPopupPosition = position
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarBottomGap(gap) {
|
||||
topBarBottomGap = gap
|
||||
function sendTestNotifications() {
|
||||
sendTestNotification(0)
|
||||
testNotifTimer1.start()
|
||||
testNotifTimer2.start()
|
||||
}
|
||||
|
||||
function sendTestNotification(index) {
|
||||
const notifications = [
|
||||
["Notification Position Test", "DMS test notification 1 of 3 ~ Hi there!", "dialog-information"],
|
||||
["Second Test", "DMS Notification 2 of 3 ~ Check it out!", "emblem-default"],
|
||||
["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "emblem-favorite"]
|
||||
]
|
||||
|
||||
if (index < 0 || index >= notifications.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const notif = notifications[index]
|
||||
testNotificationProcess.command = ["notify-send", "-h", "int:transient:1", "-a", "DMS", "-i", notif[2], notif[0], notif[1]]
|
||||
testNotificationProcess.running = true
|
||||
}
|
||||
|
||||
property Process testNotificationProcess
|
||||
|
||||
testNotificationProcess: Process {
|
||||
command: []
|
||||
running: false
|
||||
}
|
||||
|
||||
property Timer testNotifTimer1
|
||||
|
||||
testNotifTimer1: Timer {
|
||||
interval: 400
|
||||
repeat: false
|
||||
onTriggered: sendTestNotification(1)
|
||||
}
|
||||
|
||||
property Timer testNotifTimer2
|
||||
|
||||
testNotifTimer2: Timer {
|
||||
interval: 800
|
||||
repeat: false
|
||||
onTriggered: sendTestNotification(2)
|
||||
}
|
||||
|
||||
function setDankBarSpacing(spacing) {
|
||||
dankBarSpacing = spacing
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarInnerPadding(padding) {
|
||||
topBarInnerPadding = padding
|
||||
function setDankBarBottomGap(gap) {
|
||||
dankBarBottomGap = gap
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarSquareCorners(enabled) {
|
||||
topBarSquareCorners = enabled
|
||||
function setDankBarInnerPadding(padding) {
|
||||
dankBarInnerPadding = padding
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setTopBarNoBackground(enabled) {
|
||||
topBarNoBackground = enabled
|
||||
function setDankBarSquareCorners(enabled) {
|
||||
dankBarSquareCorners = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDankBarNoBackground(enabled) {
|
||||
dankBarNoBackground = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDankBarGothCornersEnabled(enabled) {
|
||||
dankBarGothCornersEnabled = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDankBarPosition(position) {
|
||||
dankBarPosition = position
|
||||
if (position === SettingsData.Position.Bottom && showDock) {
|
||||
setDockPosition(SettingsData.Position.Top)
|
||||
return
|
||||
}
|
||||
if (position === SettingsData.Position.Top && showDock) {
|
||||
setDockPosition(SettingsData.Position.Bottom)
|
||||
return
|
||||
}
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setDockPosition(position) {
|
||||
dockPosition = position
|
||||
if (position === SettingsData.Position.Bottom && dankBarPosition === SettingsData.Position.Bottom && showDock) {
|
||||
setDankBarPosition(SettingsData.Position.Top)
|
||||
}
|
||||
if (position === SettingsData.Position.Top && dankBarPosition === SettingsData.Position.Top && showDock) {
|
||||
setDankBarPosition(SettingsData.Position.Bottom)
|
||||
}
|
||||
saveSettings()
|
||||
Qt.callLater(() => forceDockLayoutRefresh())
|
||||
}
|
||||
function setDockSpacing(spacing) {
|
||||
dockSpacing = spacing
|
||||
saveSettings()
|
||||
}
|
||||
function setDockBottomGap(gap) {
|
||||
dockBottomGap = gap
|
||||
saveSettings()
|
||||
}
|
||||
function setDockOpenOnOverview(enabled) {
|
||||
dockOpenOnOverview = enabled
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function getPopupYPosition(barHeight) {
|
||||
const gothOffset = dankBarGothCornersEnabled ? Theme.cornerRadius : 0
|
||||
return barHeight + dankBarSpacing + dankBarBottomGap - gothOffset + Theme.popupDistance
|
||||
}
|
||||
|
||||
function getPopupTriggerPosition(globalPos, screen, barThickness, widgetWidth) {
|
||||
const screenX = screen ? screen.x : 0
|
||||
const screenY = screen ? screen.y : 0
|
||||
const relativeX = globalPos.x - screenX
|
||||
const relativeY = globalPos.y - screenY
|
||||
|
||||
if (dankBarPosition === SettingsData.Position.Left || dankBarPosition === SettingsData.Position.Right) {
|
||||
return {
|
||||
x: relativeY,
|
||||
y: barThickness + dankBarSpacing + Theme.popupDistance,
|
||||
width: widgetWidth
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: relativeX,
|
||||
y: barThickness + dankBarSpacing + dankBarBottomGap + Theme.popupDistance,
|
||||
width: widgetWidth
|
||||
}
|
||||
}
|
||||
|
||||
function setLockScreenShowPowerActions(enabled) {
|
||||
lockScreenShowPowerActions = enabled
|
||||
saveSettings()
|
||||
@@ -942,6 +1214,19 @@ Singleton {
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setWidgetBackgroundColor(color) {
|
||||
widgetBackgroundColor = color
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function setSurfaceBase(base) {
|
||||
surfaceBase = base
|
||||
saveSettings()
|
||||
if (typeof Theme !== "undefined") {
|
||||
Theme.generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
}
|
||||
|
||||
function setScreenPreferences(prefs) {
|
||||
screenPreferences = prefs
|
||||
saveSettings()
|
||||
@@ -955,6 +1240,11 @@ Singleton {
|
||||
return Quickshell.screens.filter(screen => prefs.includes(screen.name))
|
||||
}
|
||||
|
||||
function setAnimationSpeed(speed) {
|
||||
animationSpeed = speed
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
function _shq(s) {
|
||||
return "'" + String(s).replace(/'/g, "'\\''") + "'"
|
||||
}
|
||||
@@ -1006,6 +1296,7 @@ Singleton {
|
||||
path: StandardPaths.writableLocation(StandardPaths.ConfigLocation) + "/DankMaterialShell/settings.json"
|
||||
blockLoading: true
|
||||
blockWrites: true
|
||||
atomicWrites: true
|
||||
watchChanges: true
|
||||
onLoaded: {
|
||||
parseSettings(settingsFile.text())
|
||||
@@ -1100,22 +1391,22 @@ Singleton {
|
||||
|
||||
IpcHandler {
|
||||
function reveal(): string {
|
||||
root.setTopBarVisible(true)
|
||||
root.setDankBarVisible(true)
|
||||
return "BAR_SHOW_SUCCESS"
|
||||
}
|
||||
|
||||
function hide(): string {
|
||||
root.setTopBarVisible(false)
|
||||
root.setDankBarVisible(false)
|
||||
return "BAR_HIDE_SUCCESS"
|
||||
}
|
||||
|
||||
function toggle(): string {
|
||||
root.toggleTopBarVisible()
|
||||
return topBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS"
|
||||
root.toggleDankBarVisible()
|
||||
return root.dankBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS"
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
return topBarVisible ? "visible" : "hidden"
|
||||
return root.dankBarVisible ? "visible" : "hidden"
|
||||
}
|
||||
|
||||
target: "bar"
|
||||
|
||||
@@ -1,41 +1,136 @@
|
||||
// Stock theme definitions for DankMaterialShell
|
||||
// Separated from Theme.qml to keep that file clean
|
||||
|
||||
const CatppuccinMocha = {
|
||||
surface: "#313244",
|
||||
surfaceText: "#cdd6f4",
|
||||
surfaceVariant: "#313244",
|
||||
surfaceVariantText: "#a6adc8",
|
||||
background: "#1e1e2e",
|
||||
backgroundText: "#cdd6f4",
|
||||
outline: "#6c7086",
|
||||
surfaceContainer: "#45475a",
|
||||
surfaceContainerHigh: "#585b70",
|
||||
surfaceContainerHighest: "#6c7086"
|
||||
}
|
||||
|
||||
const CatppuccinLatte = {
|
||||
surface: "#e6e9ef",
|
||||
surfaceText: "#4c4f69",
|
||||
surfaceVariant: "#e6e9ef",
|
||||
surfaceVariantText: "#6c6f85",
|
||||
background: "#eff1f5",
|
||||
backgroundText: "#4c4f69",
|
||||
outline: "#9ca0b0",
|
||||
surfaceContainer: "#dce0e8",
|
||||
surfaceContainerHigh: "#ccd0da",
|
||||
surfaceContainerHighest: "#bcc0cc"
|
||||
}
|
||||
|
||||
const CatppuccinVariants = {
|
||||
"cat-rosewater": {
|
||||
name: "Rosewater",
|
||||
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" },
|
||||
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" }
|
||||
},
|
||||
"cat-flamingo": {
|
||||
name: "Flamingo",
|
||||
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" },
|
||||
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" }
|
||||
},
|
||||
"cat-pink": {
|
||||
name: "Pink",
|
||||
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" },
|
||||
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" }
|
||||
},
|
||||
"cat-mauve": {
|
||||
name: "Mauve",
|
||||
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" },
|
||||
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" }
|
||||
},
|
||||
"cat-red": {
|
||||
name: "Red",
|
||||
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" },
|
||||
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" }
|
||||
},
|
||||
"cat-maroon": {
|
||||
name: "Maroon",
|
||||
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" },
|
||||
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" }
|
||||
},
|
||||
"cat-peach": {
|
||||
name: "Peach",
|
||||
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" },
|
||||
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" }
|
||||
},
|
||||
"cat-yellow": {
|
||||
name: "Yellow",
|
||||
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" },
|
||||
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" }
|
||||
},
|
||||
"cat-green": {
|
||||
name: "Green",
|
||||
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" },
|
||||
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" }
|
||||
},
|
||||
"cat-teal": {
|
||||
name: "Teal",
|
||||
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" },
|
||||
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" }
|
||||
},
|
||||
"cat-sky": {
|
||||
name: "Sky",
|
||||
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" },
|
||||
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" }
|
||||
},
|
||||
"cat-sapphire": {
|
||||
name: "Sapphire",
|
||||
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" },
|
||||
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" }
|
||||
},
|
||||
"cat-blue": {
|
||||
name: "Blue",
|
||||
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" },
|
||||
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" }
|
||||
},
|
||||
"cat-lavender": {
|
||||
name: "Lavender",
|
||||
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" },
|
||||
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" }
|
||||
}
|
||||
}
|
||||
|
||||
function getCatppuccinTheme(variant, isLight = false) {
|
||||
const variantData = CatppuccinVariants[variant]
|
||||
if (!variantData) return null
|
||||
|
||||
const baseColors = isLight ? CatppuccinLatte : CatppuccinMocha
|
||||
const accentColors = isLight ? variantData.light : variantData.dark
|
||||
|
||||
return Object.assign({
|
||||
name: `${variantData.name}${isLight ? ' Light' : ''}`
|
||||
}, baseColors, accentColors)
|
||||
}
|
||||
|
||||
const StockThemes = {
|
||||
DARK: {
|
||||
blue: {
|
||||
name: "Blue",
|
||||
primary: "#42a5f5",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#1976d2",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#0d47a1",
|
||||
secondary: "#8ab4f8",
|
||||
surface: "#1a1c1e",
|
||||
surfaceText: "#e3e8ef",
|
||||
surfaceVariant: "#44464f",
|
||||
surfaceVariantText: "#c4c7c5",
|
||||
surface: "#101418",
|
||||
surfaceText: "#e0e2e8",
|
||||
surfaceVariant: "#42474e",
|
||||
surfaceVariantText: "#c2c7cf",
|
||||
surfaceTint: "#8ab4f8",
|
||||
background: "#1a1c1e",
|
||||
backgroundText: "#e3e8ef",
|
||||
outline: "#8e918f",
|
||||
surfaceContainer: "#1e2023",
|
||||
surfaceContainerHigh: "#292b2f"
|
||||
},
|
||||
deepBlue: {
|
||||
name: "Deep Blue",
|
||||
primary: "#0061a4",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#004881",
|
||||
secondary: "#42a5f5",
|
||||
surface: "#1a1c1e",
|
||||
surfaceText: "#e3e8ef",
|
||||
surfaceVariant: "#44464f",
|
||||
surfaceVariantText: "#c4c7c5",
|
||||
surfaceTint: "#8ab4f8",
|
||||
background: "#1a1c1e",
|
||||
backgroundText: "#e3e8ef",
|
||||
outline: "#8e918f",
|
||||
surfaceContainer: "#1e2023",
|
||||
surfaceContainerHigh: "#292b2f"
|
||||
background: "#101418",
|
||||
backgroundText: "#e0e2e8",
|
||||
outline: "#8c9199",
|
||||
surfaceContainer: "#1d2024",
|
||||
surfaceContainerHigh: "#272a2f",
|
||||
surfaceContainerHighest: "#32353a"
|
||||
},
|
||||
purple: {
|
||||
name: "Purple",
|
||||
@@ -43,135 +138,165 @@ const StockThemes = {
|
||||
primaryText: "#381E72",
|
||||
primaryContainer: "#4F378B",
|
||||
secondary: "#CCC2DC",
|
||||
surface: "#10121E",
|
||||
surfaceText: "#E6E0E9",
|
||||
surfaceVariant: "#49454F",
|
||||
surfaceVariantText: "#CAC4D0",
|
||||
surface: "#141218",
|
||||
surfaceText: "#e6e0e9",
|
||||
surfaceVariant: "#49454e",
|
||||
surfaceVariantText: "#cac4cf",
|
||||
surfaceTint: "#D0BCFF",
|
||||
background: "#10121E",
|
||||
backgroundText: "#E6E0E9",
|
||||
outline: "#938F99",
|
||||
surfaceContainer: "#1D1B20",
|
||||
surfaceContainerHigh: "#2B2930"
|
||||
background: "#141218",
|
||||
backgroundText: "#e6e0e9",
|
||||
outline: "#948f99",
|
||||
surfaceContainer: "#211f24",
|
||||
surfaceContainerHigh: "#2b292f",
|
||||
surfaceContainerHighest: "#36343a"
|
||||
},
|
||||
green: {
|
||||
name: "Green",
|
||||
primary: "#4caf50",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#388e3c",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#1b5e20",
|
||||
secondary: "#81c995",
|
||||
surface: "#0f1411",
|
||||
surfaceText: "#e1f5e3",
|
||||
surfaceVariant: "#404943",
|
||||
surfaceVariantText: "#c1cbc4",
|
||||
surface: "#10140f",
|
||||
surfaceText: "#e0e4db",
|
||||
surfaceVariant: "#424940",
|
||||
surfaceVariantText: "#c2c9bd",
|
||||
surfaceTint: "#81c995",
|
||||
background: "#0f1411",
|
||||
backgroundText: "#e1f5e3",
|
||||
outline: "#8b938c",
|
||||
surfaceContainer: "#1a1f1b",
|
||||
surfaceContainerHigh: "#252a26"
|
||||
background: "#10140f",
|
||||
backgroundText: "#e0e4db",
|
||||
outline: "#8c9388",
|
||||
surfaceContainer: "#1d211b",
|
||||
surfaceContainerHigh: "#272b25",
|
||||
surfaceContainerHighest: "#323630"
|
||||
},
|
||||
orange: {
|
||||
name: "Orange",
|
||||
primary: "#ff6d00",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e65100",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#3e2723",
|
||||
secondary: "#ffb74d",
|
||||
surface: "#1c1410",
|
||||
surfaceText: "#f5f1ea",
|
||||
surfaceVariant: "#4a453a",
|
||||
surfaceVariantText: "#cbc5b8",
|
||||
surface: "#1a120e",
|
||||
surfaceText: "#f0dfd8",
|
||||
surfaceVariant: "#52443d",
|
||||
surfaceVariantText: "#d7c2b9",
|
||||
surfaceTint: "#ffb74d",
|
||||
background: "#1c1410",
|
||||
backgroundText: "#f5f1ea",
|
||||
outline: "#958f84",
|
||||
surfaceContainer: "#211e17",
|
||||
surfaceContainerHigh: "#2c291f"
|
||||
background: "#1a120e",
|
||||
backgroundText: "#f0dfd8",
|
||||
outline: "#a08d85",
|
||||
surfaceContainer: "#271e1a",
|
||||
surfaceContainerHigh: "#322824",
|
||||
surfaceContainerHighest: "#3d332e"
|
||||
},
|
||||
red: {
|
||||
name: "Red",
|
||||
primary: "#f44336",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#d32f2f",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e0e",
|
||||
secondary: "#f28b82",
|
||||
surface: "#1c1011",
|
||||
surfaceText: "#f5e8ea",
|
||||
surfaceVariant: "#4a3f41",
|
||||
surfaceVariantText: "#cbc2c4",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2be",
|
||||
surfaceTint: "#f28b82",
|
||||
background: "#1c1011",
|
||||
backgroundText: "#f5e8ea",
|
||||
outline: "#958b8d",
|
||||
surfaceContainer: "#211b1c",
|
||||
surfaceContainerHigh: "#2c2426"
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c89",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231"
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan",
|
||||
primary: "#00bcd4",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#0097a7",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#004d5c",
|
||||
secondary: "#4dd0e1",
|
||||
surface: "#0f1617",
|
||||
surfaceText: "#e8f4f5",
|
||||
surfaceVariant: "#3f474a",
|
||||
surfaceVariantText: "#c2c9cb",
|
||||
surface: "#0e1416",
|
||||
surfaceText: "#dee3e5",
|
||||
surfaceVariant: "#3f484a",
|
||||
surfaceVariantText: "#bfc8ca",
|
||||
surfaceTint: "#4dd0e1",
|
||||
background: "#0f1617",
|
||||
backgroundText: "#e8f4f5",
|
||||
outline: "#8c9194",
|
||||
surfaceContainer: "#1a1f20",
|
||||
surfaceContainerHigh: "#252b2c"
|
||||
background: "#0e1416",
|
||||
backgroundText: "#dee3e5",
|
||||
outline: "#899295",
|
||||
surfaceContainer: "#1b2122",
|
||||
surfaceContainerHigh: "#252b2c",
|
||||
surfaceContainerHighest: "#303637"
|
||||
},
|
||||
pink: {
|
||||
name: "Pink",
|
||||
primary: "#e91e63",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#c2185b",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#4a0e2f",
|
||||
secondary: "#f8bbd9",
|
||||
surface: "#1a1014",
|
||||
surfaceText: "#f3e8ee",
|
||||
surfaceVariant: "#483f45",
|
||||
surfaceVariantText: "#c9c2c7",
|
||||
surface: "#191112",
|
||||
surfaceText: "#f0dee0",
|
||||
surfaceVariant: "#524345",
|
||||
surfaceVariantText: "#d6c2c3",
|
||||
surfaceTint: "#f8bbd9",
|
||||
background: "#1a1014",
|
||||
backgroundText: "#f3e8ee",
|
||||
outline: "#938a90",
|
||||
surfaceContainer: "#1f1b1e",
|
||||
surfaceContainerHigh: "#2a2428"
|
||||
background: "#191112",
|
||||
backgroundText: "#f0dee0",
|
||||
outline: "#9f8c8e",
|
||||
surfaceContainer: "#261d1e",
|
||||
surfaceContainerHigh: "#312829",
|
||||
surfaceContainerHighest: "#3c3233"
|
||||
},
|
||||
amber: {
|
||||
name: "Amber",
|
||||
primary: "#ffc107",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#ff8f00",
|
||||
primaryContainer: "#4a3c00",
|
||||
secondary: "#ffd54f",
|
||||
surface: "#1a1710",
|
||||
surfaceText: "#f3f0e8",
|
||||
surfaceVariant: "#49453a",
|
||||
surfaceVariantText: "#cac5b8",
|
||||
surface: "#17130b",
|
||||
surfaceText: "#ebe1d4",
|
||||
surfaceVariant: "#4d4639",
|
||||
surfaceVariantText: "#d0c5b4",
|
||||
surfaceTint: "#ffd54f",
|
||||
background: "#1a1710",
|
||||
backgroundText: "#f3f0e8",
|
||||
outline: "#949084",
|
||||
surfaceContainer: "#1f1e17",
|
||||
surfaceContainerHigh: "#2a281f"
|
||||
background: "#17130b",
|
||||
backgroundText: "#ebe1d4",
|
||||
outline: "#998f80",
|
||||
surfaceContainer: "#231f17",
|
||||
surfaceContainerHigh: "#2e2921",
|
||||
surfaceContainerHighest: "#39342b"
|
||||
},
|
||||
coral: {
|
||||
name: "Coral",
|
||||
primary: "#ffb4ab",
|
||||
primaryText: "#5f1412",
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#8c1d18",
|
||||
secondary: "#f9dedc",
|
||||
surface: "#1a1110",
|
||||
surfaceText: "#f1e8e7",
|
||||
surfaceVariant: "#4a4142",
|
||||
surfaceVariantText: "#cdc2c1",
|
||||
surfaceText: "#f1dedc",
|
||||
surfaceVariant: "#534341",
|
||||
surfaceVariantText: "#d8c2bf",
|
||||
surfaceTint: "#ffb4ab",
|
||||
background: "#1a1110",
|
||||
backgroundText: "#f1e8e7",
|
||||
outline: "#968b8a",
|
||||
surfaceContainer: "#201a19",
|
||||
surfaceContainerHigh: "#2b2221"
|
||||
backgroundText: "#f1dedc",
|
||||
outline: "#a08c8a",
|
||||
surfaceContainer: "#271d1c",
|
||||
surfaceContainerHigh: "#322826",
|
||||
surfaceContainerHighest: "#3d3231"
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome",
|
||||
primary: "#ffffff",
|
||||
primaryText: "#2b303c",
|
||||
primaryContainer: "#424753",
|
||||
secondary: "#c4c6d0",
|
||||
surface: "#2a2a2a",
|
||||
surfaceText: "#e4e2e3",
|
||||
surfaceVariant: "#474648",
|
||||
surfaceVariantText: "#c8c6c7",
|
||||
surfaceTint: "#c2c6d6",
|
||||
background: "#131315",
|
||||
backgroundText: "#e4e2e3",
|
||||
outline: "#929092",
|
||||
surfaceContainer: "#353535",
|
||||
surfaceContainerHigh: "#424242",
|
||||
surfaceContainerHighest: "#505050",
|
||||
error: "#ffb4ab",
|
||||
warning: "#3f4759",
|
||||
info: "#595e6c",
|
||||
matugen_type: "scheme-monochrome"
|
||||
}
|
||||
},
|
||||
LIGHT: {
|
||||
@@ -181,33 +306,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e3f2fd",
|
||||
secondary: "#42a5f5",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#f7f9ff",
|
||||
surfaceText: "#181c20",
|
||||
surfaceVariant: "#dee3eb",
|
||||
surfaceVariantText: "#42474e",
|
||||
surfaceTint: "#1976d2",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
},
|
||||
deepBlue: {
|
||||
name: "Deep Blue Light",
|
||||
primary: "#0061a4",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#cfe5ff",
|
||||
secondary: "#1976d2",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surfaceTint: "#0061a4",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#f7f9ff",
|
||||
backgroundText: "#181c20",
|
||||
outline: "#72777f",
|
||||
surfaceContainer: "#eceef4",
|
||||
surfaceContainerHigh: "#e6e8ee",
|
||||
surfaceContainerHighest: "#e0e2e8"
|
||||
},
|
||||
purple: {
|
||||
name: "Purple Light",
|
||||
@@ -215,16 +324,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#EADDFF",
|
||||
secondary: "#625B71",
|
||||
surface: "#FFFBFE",
|
||||
surfaceText: "#1C1B1F",
|
||||
surfaceVariant: "#E7E0EC",
|
||||
surfaceVariantText: "#49454F",
|
||||
surface: "#fef7ff",
|
||||
surfaceText: "#1d1b20",
|
||||
surfaceVariant: "#e7e0eb",
|
||||
surfaceVariantText: "#49454e",
|
||||
surfaceTint: "#6750A4",
|
||||
background: "#FFFBFE",
|
||||
backgroundText: "#1C1B1F",
|
||||
outline: "#79747E",
|
||||
surfaceContainer: "#F3EDF7",
|
||||
surfaceContainerHigh: "#ECE6F0"
|
||||
background: "#fef7ff",
|
||||
backgroundText: "#1d1b20",
|
||||
outline: "#7a757f",
|
||||
surfaceContainer: "#f2ecf4",
|
||||
surfaceContainerHigh: "#ece6ee",
|
||||
surfaceContainerHighest: "#e6e0e9"
|
||||
},
|
||||
green: {
|
||||
name: "Green Light",
|
||||
@@ -232,16 +342,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e8f5e8",
|
||||
secondary: "#4caf50",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#f7fbf1",
|
||||
surfaceText: "#191d17",
|
||||
surfaceVariant: "#dee5d8",
|
||||
surfaceVariantText: "#424940",
|
||||
surfaceTint: "#2e7d32",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#f7fbf1",
|
||||
backgroundText: "#191d17",
|
||||
outline: "#72796f",
|
||||
surfaceContainer: "#ecefe6",
|
||||
surfaceContainerHigh: "#e6e9e0",
|
||||
surfaceContainerHighest: "#e0e4db"
|
||||
},
|
||||
orange: {
|
||||
name: "Orange Light",
|
||||
@@ -249,16 +360,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffecb3",
|
||||
secondary: "#ff9800",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#fff8f6",
|
||||
surfaceText: "#221a16",
|
||||
surfaceVariant: "#f4ded5",
|
||||
surfaceVariantText: "#52443d",
|
||||
surfaceTint: "#e65100",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#fff8f6",
|
||||
backgroundText: "#221a16",
|
||||
outline: "#85736c",
|
||||
surfaceContainer: "#fceae3",
|
||||
surfaceContainerHigh: "#f6e5de",
|
||||
surfaceContainerHighest: "#f0dfd8"
|
||||
},
|
||||
red: {
|
||||
name: "Red Light",
|
||||
@@ -266,16 +378,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffebee",
|
||||
secondary: "#f44336",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#d32f2f",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857370",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f7e4e1",
|
||||
surfaceContainerHighest: "#f1dedc"
|
||||
},
|
||||
cyan: {
|
||||
name: "Cyan Light",
|
||||
@@ -283,16 +396,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#e0f2f1",
|
||||
secondary: "#00bcd4",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#f5fafc",
|
||||
surfaceText: "#171d1e",
|
||||
surfaceVariant: "#dbe4e6",
|
||||
surfaceVariantText: "#3f484a",
|
||||
surfaceTint: "#0097a7",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#f5fafc",
|
||||
backgroundText: "#171d1e",
|
||||
outline: "#6f797b",
|
||||
surfaceContainer: "#e9eff0",
|
||||
surfaceContainerHigh: "#e3e9eb",
|
||||
surfaceContainerHighest: "#dee3e5"
|
||||
},
|
||||
pink: {
|
||||
name: "Pink Light",
|
||||
@@ -300,16 +414,17 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#fce4ec",
|
||||
secondary: "#e91e63",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#22191a",
|
||||
surfaceVariant: "#f3dddf",
|
||||
surfaceVariantText: "#524345",
|
||||
surfaceTint: "#c2185b",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#22191a",
|
||||
outline: "#847375",
|
||||
surfaceContainer: "#fbeaeb",
|
||||
surfaceContainerHigh: "#f5e4e5",
|
||||
surfaceContainerHighest: "#f0dee0"
|
||||
},
|
||||
amber: {
|
||||
name: "Amber Light",
|
||||
@@ -317,16 +432,17 @@ const StockThemes = {
|
||||
primaryText: "#000000",
|
||||
primaryContainer: "#fff8e1",
|
||||
secondary: "#ffc107",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#fff8f2",
|
||||
surfaceText: "#1f1b13",
|
||||
surfaceVariant: "#ede1cf",
|
||||
surfaceVariantText: "#4d4639",
|
||||
surfaceTint: "#ff8f00",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#fff8f2",
|
||||
backgroundText: "#1f1b13",
|
||||
outline: "#7f7667",
|
||||
surfaceContainer: "#f6ecdf",
|
||||
surfaceContainerHigh: "#f1e7d9",
|
||||
surfaceContainerHighest: "#ebe1d4"
|
||||
},
|
||||
coral: {
|
||||
name: "Coral Light",
|
||||
@@ -334,23 +450,56 @@ const StockThemes = {
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#ffdad6",
|
||||
secondary: "#ff5449",
|
||||
surface: "#fefefe",
|
||||
surfaceText: "#1a1c1e",
|
||||
surfaceVariant: "#e7e0ec",
|
||||
surfaceVariantText: "#49454f",
|
||||
surface: "#fff8f7",
|
||||
surfaceText: "#231918",
|
||||
surfaceVariant: "#f5ddda",
|
||||
surfaceVariantText: "#534341",
|
||||
surfaceTint: "#8c1d18",
|
||||
background: "#fefefe",
|
||||
backgroundText: "#1a1c1e",
|
||||
outline: "#79747e",
|
||||
surfaceContainer: "#f3f3f3",
|
||||
surfaceContainerHigh: "#ececec"
|
||||
background: "#fff8f7",
|
||||
backgroundText: "#231918",
|
||||
outline: "#857371",
|
||||
surfaceContainer: "#fceae7",
|
||||
surfaceContainerHigh: "#f6e4e2",
|
||||
surfaceContainerHighest: "#f1dedc"
|
||||
},
|
||||
monochrome: {
|
||||
name: "Monochrome Light",
|
||||
primary: "#2b303c",
|
||||
primaryText: "#ffffff",
|
||||
primaryContainer: "#d6d7dc",
|
||||
secondary: "#4a4d56",
|
||||
surface: "#f5f5f6",
|
||||
surfaceText: "#2a2a2a",
|
||||
surfaceVariant: "#e0e0e2",
|
||||
surfaceVariantText: "#424242",
|
||||
surfaceTint: "#5a5f6e",
|
||||
background: "#ffffff",
|
||||
backgroundText: "#1a1a1a",
|
||||
outline: "#757577",
|
||||
surfaceContainer: "#e8e8ea",
|
||||
surfaceContainerHigh: "#dcdcde",
|
||||
surfaceContainerHighest: "#d0d0d2",
|
||||
error: "#ba1a1a",
|
||||
warning: "#f9e79f",
|
||||
info: "#5d6475",
|
||||
matugen_type: "scheme-monochrome"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ThemeCategories = {
|
||||
GENERIC: {
|
||||
name: "Generic",
|
||||
variants: ["blue", "purple", "green", "orange", "red", "cyan", "pink", "amber", "coral", "monochrome"]
|
||||
},
|
||||
CATPPUCCIN: {
|
||||
name: "Catppuccin",
|
||||
variants: Object.keys(CatppuccinVariants)
|
||||
}
|
||||
}
|
||||
|
||||
const ThemeNames = {
|
||||
BLUE: "blue",
|
||||
DEEP_BLUE: "deepBlue",
|
||||
PURPLE: "purple",
|
||||
GREEN: "green",
|
||||
ORANGE: "orange",
|
||||
@@ -359,6 +508,7 @@ const ThemeNames = {
|
||||
PINK: "pink",
|
||||
AMBER: "amber",
|
||||
CORAL: "coral",
|
||||
MONOCHROME: "monochrome",
|
||||
DYNAMIC: "dynamic"
|
||||
}
|
||||
|
||||
@@ -366,15 +516,30 @@ function isStockTheme(themeName) {
|
||||
return Object.keys(StockThemes.DARK).includes(themeName)
|
||||
}
|
||||
|
||||
function isCatppuccinVariant(themeName) {
|
||||
return Object.keys(CatppuccinVariants).includes(themeName)
|
||||
}
|
||||
|
||||
function getAvailableThemes(isLight = false) {
|
||||
return isLight ? StockThemes.LIGHT : StockThemes.DARK
|
||||
}
|
||||
|
||||
function getThemeByName(themeName, isLight = false) {
|
||||
if (isCatppuccinVariant(themeName)) {
|
||||
return getCatppuccinTheme(themeName, isLight)
|
||||
}
|
||||
const themes = getAvailableThemes(isLight)
|
||||
return themes[themeName] || themes.blue
|
||||
}
|
||||
|
||||
function getAllThemeNames() {
|
||||
return Object.keys(StockThemes.DARK)
|
||||
}
|
||||
|
||||
function getCatppuccinVariantNames() {
|
||||
return Object.keys(CatppuccinVariants)
|
||||
}
|
||||
|
||||
function getThemeCategories() {
|
||||
return ThemeCategories
|
||||
}
|
||||
559
Common/Theme.qml
559
Common/Theme.qml
@@ -7,29 +7,57 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "StockThemes.js" as StockThemes
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
|
||||
|
||||
// ! TODO - Synchronize with niri/hyprland gaps?
|
||||
readonly property real popupDistance: 2
|
||||
|
||||
property string currentTheme: "blue"
|
||||
property bool isLightMode: false
|
||||
property string currentThemeCategory: "generic"
|
||||
property bool isLightMode: typeof SessionData !== "undefined" ? SessionData.isLightMode : false
|
||||
|
||||
readonly property string dynamic: "dynamic"
|
||||
readonly property string custom : "custom"
|
||||
|
||||
readonly property string homeDir: {
|
||||
const url = StandardPaths.writableLocation(StandardPaths.HomeLocation).toString()
|
||||
return url.startsWith("file://") ? url.substring(7) : url
|
||||
}
|
||||
readonly property string configDir: {
|
||||
const url = StandardPaths.writableLocation(StandardPaths.ConfigLocation).toString()
|
||||
return url.startsWith("file://") ? url.substring(7) : url
|
||||
}
|
||||
readonly property string shellDir: Qt.resolvedUrl(".").toString().replace("file://", "").replace("/Common/", "")
|
||||
readonly property string homeDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.HomeLocation))
|
||||
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
|
||||
readonly property string shellDir: Paths.strip(Qt.resolvedUrl(".").toString()).replace("/Common/", "")
|
||||
readonly property string wallpaperPath: {
|
||||
if (typeof SessionData === "undefined") return ""
|
||||
|
||||
if (SessionData.perMonitorWallpaper) {
|
||||
// Use first monitor's wallpaper for dynamic theming
|
||||
var screens = Quickshell.screens
|
||||
if (screens.length > 0) {
|
||||
var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name)
|
||||
var wallpaperPath = firstMonitorWallpaper || SessionData.wallpaperPath
|
||||
|
||||
if (wallpaperPath && wallpaperPath.startsWith("we:")) {
|
||||
return stateDir + "/we_screenshots/" + wallpaperPath.substring(3) + ".jpg"
|
||||
}
|
||||
|
||||
return wallpaperPath
|
||||
}
|
||||
}
|
||||
|
||||
var wallpaperPath = SessionData.wallpaperPath
|
||||
var screens = Quickshell.screens
|
||||
if (screens.length > 0 && wallpaperPath && wallpaperPath.startsWith("we:")) {
|
||||
return stateDir + "/we_screenshots/" + wallpaperPath.substring(3) + ".jpg"
|
||||
}
|
||||
|
||||
return wallpaperPath
|
||||
}
|
||||
readonly property string rawWallpaperPath: {
|
||||
if (typeof SessionData === "undefined") return ""
|
||||
|
||||
if (SessionData.perMonitorWallpaper) {
|
||||
// Use first monitor's wallpaper for dynamic theming
|
||||
var screens = Quickshell.screens
|
||||
@@ -38,7 +66,7 @@ Singleton {
|
||||
return firstMonitorWallpaper || SessionData.wallpaperPath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return SessionData.wallpaperPath
|
||||
}
|
||||
|
||||
@@ -47,24 +75,21 @@ Singleton {
|
||||
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
|
||||
property var workerRunning: false
|
||||
property var matugenColors: ({})
|
||||
property bool extractionRequested: false
|
||||
property int colorUpdateTrigger: 0
|
||||
property var customThemeData: null
|
||||
|
||||
readonly property string stateDir: {
|
||||
const cacheHome = StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()
|
||||
const path = cacheHome.startsWith("file://") ? cacheHome.substring(7) : cacheHome
|
||||
return path + "/dankshell"
|
||||
}
|
||||
readonly property string stateDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()) + "/dankshell"
|
||||
|
||||
Component.onCompleted: {
|
||||
Quickshell.execDetached(["mkdir", "-p", stateDir])
|
||||
matugenCheck.running = true
|
||||
if (typeof SessionData !== "undefined")
|
||||
if (typeof SessionData !== "undefined") {
|
||||
SessionData.isLightModeChanged.connect(root.onLightModeChanged)
|
||||
|
||||
isLightMode = SessionData.isLightMode
|
||||
}
|
||||
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
|
||||
switchTheme(SettingsData.currentThemeName, false)
|
||||
switchTheme(SettingsData.currentThemeName, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +124,7 @@ Singleton {
|
||||
"outline": getMatugenColor("outline", "#8e918f"),
|
||||
"surfaceContainer": getMatugenColor("surface_container", "#1e2023"),
|
||||
"surfaceContainerHigh": getMatugenColor("surface_container_high", "#292b2f"),
|
||||
"surfaceContainerHighest": getMatugenColor("surface_container_highest", "#343740"),
|
||||
"error": "#F2B8B5",
|
||||
"warning": "#FF9800",
|
||||
"info": "#2196F3",
|
||||
@@ -109,11 +135,36 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
readonly property var availableMatugenSchemes: [
|
||||
({ "value": "scheme-tonal-spot", "label": "Tonal Spot", "description": "Balanced palette with focused accents (default)." }),
|
||||
({ "value": "scheme-content", "label": "Content", "description": "Derives colors that closely match the underlying image." }),
|
||||
({ "value": "scheme-expressive", "label": "Expressive", "description": "Vibrant palette with playful saturation." }),
|
||||
({ "value": "scheme-fidelity", "label": "Fidelity", "description": "High-fidelity palette that preserves source hues." }),
|
||||
({ "value": "scheme-fruit-salad", "label": "Fruit Salad", "description": "Colorful mix of bright contrasting accents." }),
|
||||
({ "value": "scheme-monochrome", "label": "Monochrome", "description": "Minimal palette built around a single hue." }),
|
||||
({ "value": "scheme-neutral", "label": "Neutral", "description": "Muted palette with subdued, calming tones." }),
|
||||
({ "value": "scheme-rainbow", "label": "Rainbow", "description": "Diverse palette spanning the full spectrum." })
|
||||
]
|
||||
|
||||
function getMatugenScheme(value) {
|
||||
const schemes = availableMatugenSchemes
|
||||
for (let i = 0; i < schemes.length; i++) {
|
||||
if (schemes[i].value === value)
|
||||
return schemes[i]
|
||||
}
|
||||
return schemes[0]
|
||||
}
|
||||
|
||||
property color primary: currentThemeData.primary
|
||||
property color primaryText: currentThemeData.primaryText
|
||||
property color primaryContainer: currentThemeData.primaryContainer
|
||||
property color secondary: currentThemeData.secondary
|
||||
property color surface: currentThemeData.surface
|
||||
property color surface: {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||
return currentThemeData.background
|
||||
}
|
||||
return currentThemeData.surface
|
||||
}
|
||||
property color surfaceText: currentThemeData.surfaceText
|
||||
property color surfaceVariant: currentThemeData.surfaceVariant
|
||||
property color surfaceVariantText: currentThemeData.surfaceVariantText
|
||||
@@ -121,8 +172,32 @@ Singleton {
|
||||
property color background: currentThemeData.background
|
||||
property color backgroundText: currentThemeData.backgroundText
|
||||
property color outline: currentThemeData.outline
|
||||
property color surfaceContainer: currentThemeData.surfaceContainer
|
||||
property color surfaceContainerHigh: currentThemeData.surfaceContainerHigh
|
||||
property color outlineVariant: currentThemeData.outlineVariant || Qt.rgba(outline.r, outline.g, outline.b, 0.6)
|
||||
property color surfaceContainer: {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||
return currentThemeData.surface
|
||||
}
|
||||
return currentThemeData.surfaceContainer
|
||||
}
|
||||
property color surfaceContainerHigh: {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||
return currentThemeData.surfaceContainer
|
||||
}
|
||||
return currentThemeData.surfaceContainerHigh
|
||||
}
|
||||
property color surfaceContainerHighest: {
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.surfaceBase === "s") {
|
||||
return currentThemeData.surfaceContainerHigh
|
||||
}
|
||||
return currentThemeData.surfaceContainerHighest
|
||||
}
|
||||
|
||||
property color onSurface: surfaceText
|
||||
property color onSurfaceVariant: surfaceVariantText
|
||||
property color onPrimary: primaryText
|
||||
property color onSurface_12: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.12)
|
||||
property color onSurface_38: Qt.rgba(onSurface.r, onSurface.g, onSurface.b, 0.38)
|
||||
property color onSurfaceVariant_30: Qt.rgba(onSurfaceVariant.r, onSurfaceVariant.g, onSurfaceVariant.b, 0.30)
|
||||
|
||||
property color error: currentThemeData.error || "#F2B8B5"
|
||||
property color warning: currentThemeData.warning || "#FF9800"
|
||||
@@ -155,15 +230,27 @@ Singleton {
|
||||
property color outlineStrong: Qt.rgba(outline.r, outline.g, outline.b, 0.12)
|
||||
|
||||
property color errorHover: Qt.rgba(error.r, error.g, error.b, 0.12)
|
||||
property color errorPressed: Qt.rgba(error.r, error.g, error.b, 0.16)
|
||||
|
||||
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
|
||||
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
|
||||
|
||||
property int shorterDuration: 100
|
||||
property int shortDuration: 150
|
||||
property int mediumDuration: 300
|
||||
property int longDuration: 500
|
||||
property int extraLongDuration: 1000
|
||||
readonly property var animationDurations: [
|
||||
{ shorter: 0, short: 0, medium: 0, long: 0, extraLong: 0 },
|
||||
{ shorter: 50, short: 75, medium: 150, long: 250, extraLong: 500 },
|
||||
{ shorter: 100, short: 150, medium: 300, long: 500, extraLong: 1000 },
|
||||
{ shorter: 150, short: 225, medium: 450, long: 750, extraLong: 1500 },
|
||||
{ shorter: 200, short: 300, medium: 600, long: 1000, extraLong: 2000 }
|
||||
]
|
||||
|
||||
readonly property int currentAnimationSpeed: typeof SettingsData !== "undefined" ? SettingsData.animationSpeed : SettingsData.AnimationSpeed.Short
|
||||
readonly property var currentDurations: animationDurations[currentAnimationSpeed] || animationDurations[SettingsData.AnimationSpeed.Short]
|
||||
|
||||
property int shorterDuration: currentDurations.shorter
|
||||
property int shortDuration: currentDurations.short
|
||||
property int mediumDuration: currentDurations.medium
|
||||
property int longDuration: currentDurations.long
|
||||
property int extraLongDuration: currentDurations.extraLong
|
||||
property int standardEasing: Easing.OutCubic
|
||||
property int emphasizedEasing: Easing.OutQuart
|
||||
|
||||
@@ -183,20 +270,38 @@ Singleton {
|
||||
property real iconSizeLarge: 32
|
||||
|
||||
property real panelTransparency: 0.85
|
||||
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.topBarWidgetTransparency !== undefined ? SettingsData.topBarWidgetTransparency : 0.85
|
||||
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 0.92
|
||||
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.dankBarWidgetTransparency !== undefined ? SettingsData.dankBarWidgetTransparency : 1.0
|
||||
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0
|
||||
|
||||
function screenTransition() {
|
||||
CompositorService.isNiri && NiriService.doScreenTransition()
|
||||
}
|
||||
|
||||
function switchTheme(themeName, savePrefs = true, enableTransition = true) {
|
||||
if (enableTransition) {
|
||||
screenTransition()
|
||||
themeTransitionTimer.themeName = themeName
|
||||
themeTransitionTimer.savePrefs = savePrefs
|
||||
themeTransitionTimer.restart()
|
||||
return
|
||||
}
|
||||
|
||||
function switchTheme(themeName, savePrefs = true) {
|
||||
if (themeName === dynamic) {
|
||||
currentTheme = dynamic
|
||||
extractColors()
|
||||
} else if (themeName === "custom") {
|
||||
currentTheme = "custom"
|
||||
currentThemeCategory = dynamic
|
||||
} else if (themeName === custom) {
|
||||
currentTheme = custom
|
||||
currentThemeCategory = custom
|
||||
if (typeof SettingsData !== "undefined" && SettingsData.customThemeFile) {
|
||||
loadCustomThemeFromFile(SettingsData.customThemeFile)
|
||||
}
|
||||
} else {
|
||||
currentTheme = themeName
|
||||
if (StockThemes.isCatppuccinVariant(themeName)) {
|
||||
currentThemeCategory = "catppuccin"
|
||||
} else {
|
||||
currentThemeCategory = "generic"
|
||||
}
|
||||
}
|
||||
if (savePrefs && typeof SettingsData !== "undefined")
|
||||
SettingsData.setTheme(currentTheme)
|
||||
@@ -204,7 +309,15 @@ Singleton {
|
||||
generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
|
||||
function setLightMode(light, savePrefs = true) {
|
||||
function setLightMode(light, savePrefs = true, enableTransition = false) {
|
||||
if (enableTransition) {
|
||||
screenTransition()
|
||||
lightModeTransitionTimer.lightMode = light
|
||||
lightModeTransitionTimer.savePrefs = savePrefs
|
||||
lightModeTransitionTimer.restart()
|
||||
return
|
||||
}
|
||||
|
||||
isLightMode = light
|
||||
if (savePrefs && typeof SessionData !== "undefined")
|
||||
SessionData.setLightMode(isLightMode)
|
||||
@@ -213,14 +326,11 @@ Singleton {
|
||||
}
|
||||
|
||||
function toggleLightMode(savePrefs = true) {
|
||||
setLightMode(!isLightMode, savePrefs)
|
||||
setLightMode(!isLightMode, savePrefs, true)
|
||||
}
|
||||
|
||||
function forceGenerateSystemThemes() {
|
||||
if (!matugenAvailable) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.showWarning("matugen not available - cannot generate system themes")
|
||||
}
|
||||
return
|
||||
}
|
||||
generateSystemThemesFromCurrentTheme()
|
||||
@@ -242,6 +352,33 @@ Singleton {
|
||||
return StockThemes.getThemeByName(themeName, isLightMode)
|
||||
}
|
||||
|
||||
function switchThemeCategory(category, defaultTheme) {
|
||||
screenTransition()
|
||||
themeCategoryTransitionTimer.category = category
|
||||
themeCategoryTransitionTimer.defaultTheme = defaultTheme
|
||||
themeCategoryTransitionTimer.restart()
|
||||
}
|
||||
|
||||
function getCatppuccinColor(variantName) {
|
||||
const catColors = {
|
||||
"cat-rosewater": "#f5e0dc", "cat-flamingo": "#f2cdcd", "cat-pink": "#f5c2e7", "cat-mauve": "#cba6f7",
|
||||
"cat-red": "#f38ba8", "cat-maroon": "#eba0ac", "cat-peach": "#fab387", "cat-yellow": "#f9e2af",
|
||||
"cat-green": "#a6e3a1", "cat-teal": "#94e2d5", "cat-sky": "#89dceb", "cat-sapphire": "#74c7ec",
|
||||
"cat-blue": "#89b4fa", "cat-lavender": "#b4befe"
|
||||
}
|
||||
return catColors[variantName] || "#cba6f7"
|
||||
}
|
||||
|
||||
function getCatppuccinVariantName(variantName) {
|
||||
const catNames = {
|
||||
"cat-rosewater": "Rosewater", "cat-flamingo": "Flamingo", "cat-pink": "Pink", "cat-mauve": "Mauve",
|
||||
"cat-red": "Red", "cat-maroon": "Maroon", "cat-peach": "Peach", "cat-yellow": "Yellow",
|
||||
"cat-green": "Green", "cat-teal": "Teal", "cat-sky": "Sky", "cat-sapphire": "Sapphire",
|
||||
"cat-blue": "Blue", "cat-lavender": "Lavender"
|
||||
}
|
||||
return catNames[variantName] || "Unknown"
|
||||
}
|
||||
|
||||
function loadCustomTheme(themeData) {
|
||||
if (themeData.dark || themeData.light) {
|
||||
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
|
||||
@@ -274,8 +411,42 @@ Singleton {
|
||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, panelTransparency)
|
||||
}
|
||||
|
||||
function widgetBackground() {
|
||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
||||
property real notepadTransparency: SettingsData.notepadTransparencyOverride >= 0 ? SettingsData.notepadTransparencyOverride : popupTransparency
|
||||
|
||||
property var widgetBaseBackgroundColor: {
|
||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
||||
switch (colorMode) {
|
||||
case "s":
|
||||
return surface
|
||||
case "sc":
|
||||
return surfaceContainer
|
||||
case "sch":
|
||||
return surfaceContainerHigh
|
||||
case "sth":
|
||||
default:
|
||||
return surfaceTextHover
|
||||
}
|
||||
}
|
||||
|
||||
property var widgetBaseHoverColor: {
|
||||
const baseColor = widgetBaseBackgroundColor
|
||||
const factor = 1.2
|
||||
return isLightMode ? Qt.darker(baseColor, factor) : Qt.lighter(baseColor, factor)
|
||||
}
|
||||
|
||||
property var widgetBackground: {
|
||||
const colorMode = typeof SettingsData !== "undefined" ? SettingsData.widgetBackgroundColor : "sch"
|
||||
switch (colorMode) {
|
||||
case "s":
|
||||
return Qt.rgba(surface.r, surface.g, surface.b, widgetTransparency)
|
||||
case "sc":
|
||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
||||
case "sch":
|
||||
return Qt.rgba(surfaceContainerHigh.r, surfaceContainerHigh.g, surfaceContainerHigh.b, widgetTransparency)
|
||||
case "sth":
|
||||
default:
|
||||
return Qt.rgba(surfaceContainer.r, surfaceContainer.g, surfaceContainer.b, widgetTransparency)
|
||||
}
|
||||
}
|
||||
|
||||
function getPopupBackgroundAlpha() {
|
||||
@@ -380,13 +551,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function extractColors() {
|
||||
extractionRequested = true
|
||||
if (matugenAvailable)
|
||||
fileChecker.running = true
|
||||
else
|
||||
matugenCheck.running = true
|
||||
}
|
||||
|
||||
function onLightModeChanged() {
|
||||
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
||||
@@ -396,21 +560,25 @@ Singleton {
|
||||
if (currentTheme === "custom" && customThemeFileView.path) {
|
||||
customThemeFileView.reload()
|
||||
}
|
||||
|
||||
generateSystemThemesFromCurrentTheme()
|
||||
}
|
||||
|
||||
function setDesiredTheme(kind, value, isLight, iconTheme) {
|
||||
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType) {
|
||||
if (!matugenAvailable) {
|
||||
console.warn("matugen not available - cannot set system theme")
|
||||
console.warn("matugen not available or disabled - cannot set system theme")
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||
NiriService.suppressNextToast()
|
||||
}
|
||||
|
||||
const desired = {
|
||||
"kind": kind,
|
||||
"value": value,
|
||||
"mode": isLight ? "light" : "dark",
|
||||
"iconTheme": iconTheme || "System Default"
|
||||
"iconTheme": iconTheme || "System Default",
|
||||
"matugenType": matugenType || "scheme-tonal-spot",
|
||||
"surfaceBase": (typeof SettingsData !== "undefined" && SettingsData.surfaceBase) ? SettingsData.surfaceBase : "sc"
|
||||
}
|
||||
|
||||
const json = JSON.stringify(desired)
|
||||
@@ -418,7 +586,15 @@ Singleton {
|
||||
|
||||
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
|
||||
workerRunning = true
|
||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"]
|
||||
if (rawWallpaperPath.startsWith("we:")) {
|
||||
console.log("calling matugen worker")
|
||||
systemThemeGenerator.command = [
|
||||
"sh", "-c",
|
||||
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' --run`
|
||||
]
|
||||
} else {
|
||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"]
|
||||
}
|
||||
systemThemeGenerator.running = true
|
||||
}
|
||||
|
||||
@@ -433,35 +609,39 @@ Singleton {
|
||||
if (!wallpaperPath) {
|
||||
return
|
||||
}
|
||||
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||
if (wallpaperPath.startsWith("#")) {
|
||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme)
|
||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||
} else {
|
||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme)
|
||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||
}
|
||||
} else {
|
||||
let primaryColor
|
||||
let matugenType
|
||||
if (currentTheme === "custom") {
|
||||
if (!customThemeData || !customThemeData.primary) {
|
||||
console.warn("Custom theme data not available for system theme generation")
|
||||
return
|
||||
}
|
||||
primaryColor = customThemeData.primary
|
||||
matugenType = customThemeData.matugen_type
|
||||
} else {
|
||||
primaryColor = currentThemeData.primary
|
||||
matugenType = currentThemeData.matugen_type
|
||||
}
|
||||
|
||||
if (!primaryColor) {
|
||||
console.warn("No primary color available for theme:", currentTheme)
|
||||
return
|
||||
}
|
||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme)
|
||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
|
||||
}
|
||||
}
|
||||
|
||||
function applyGtkColors() {
|
||||
if (!matugenAvailable) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.showError("matugen not available - cannot apply GTK colors")
|
||||
ToastService.showError("matugen not available or disabled - cannot apply GTK colors")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -474,7 +654,7 @@ Singleton {
|
||||
function applyQtColors() {
|
||||
if (!matugenAvailable) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.showError("matugen not available - cannot apply Qt colors")
|
||||
ToastService.showError("matugen not available or disabled - cannot apply Qt colors")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -483,77 +663,17 @@ Singleton {
|
||||
qtApplier.running = true
|
||||
}
|
||||
|
||||
function extractJsonFromText(text) {
|
||||
if (!text)
|
||||
return null
|
||||
|
||||
const start = text.search(/[{\[]/)
|
||||
if (start === -1)
|
||||
return null
|
||||
|
||||
const open = text[start]
|
||||
const pairs = {
|
||||
"{": '}',
|
||||
"[": ']'
|
||||
}
|
||||
const close = pairs[open]
|
||||
if (!close)
|
||||
return null
|
||||
|
||||
let inString = false
|
||||
let escape = false
|
||||
const stack = [open]
|
||||
|
||||
for (var i = start + 1; i < text.length; i++) {
|
||||
const ch = text[i]
|
||||
|
||||
if (inString) {
|
||||
if (escape) {
|
||||
escape = false
|
||||
} else if (ch === '\\') {
|
||||
escape = true
|
||||
} else if (ch === '"') {
|
||||
inString = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
inString = true
|
||||
continue
|
||||
}
|
||||
if (ch === '{' || ch === '[') {
|
||||
stack.push(ch)
|
||||
continue
|
||||
}
|
||||
if (ch === '}' || ch === ']') {
|
||||
const last = stack.pop()
|
||||
if (!last || pairs[last] !== ch) {
|
||||
return null
|
||||
}
|
||||
if (stack.length === 0) {
|
||||
return text.slice(start, i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); }
|
||||
|
||||
Process {
|
||||
id: matugenCheck
|
||||
command: ["which", "matugen"]
|
||||
onExited: code => {
|
||||
matugenAvailable = (code === 0)
|
||||
matugenAvailable = (code === 0) && !envDisableMatugen
|
||||
if (!matugenAvailable) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "matugen_missing"
|
||||
ToastService.showWarning("matugen not found - dynamic theming disabled")
|
||||
}
|
||||
console.log("matugen not not available in path or disabled via DMS_DISABLE_MATUGEN")
|
||||
return
|
||||
}
|
||||
if (extractionRequested) {
|
||||
fileChecker.running = true
|
||||
}
|
||||
|
||||
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
|
||||
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default"
|
||||
@@ -561,137 +681,35 @@ Singleton {
|
||||
if (currentTheme === dynamic) {
|
||||
if (wallpaperPath) {
|
||||
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot"
|
||||
if (wallpaperPath.startsWith("#")) {
|
||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme)
|
||||
setDesiredTheme("hex", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||
} else {
|
||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme)
|
||||
setDesiredTheme("image", wallpaperPath, isLight, iconTheme, selectedMatugenType)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let primaryColor
|
||||
let matugenType
|
||||
if (currentTheme === "custom") {
|
||||
if (customThemeData && customThemeData.primary) {
|
||||
primaryColor = customThemeData.primary
|
||||
matugenType = customThemeData.matugen_type
|
||||
}
|
||||
} else {
|
||||
primaryColor = currentThemeData.primary
|
||||
matugenType = currentThemeData.matugen_type
|
||||
}
|
||||
|
||||
if (primaryColor) {
|
||||
Quickshell.execDetached(["rm", "-f", stateDir + "/matugen.key"])
|
||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme)
|
||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fileChecker
|
||||
command: ["test", "-r", wallpaperPath]
|
||||
onExited: code => {
|
||||
if (code === 0) {
|
||||
matugenProcess.running = true
|
||||
} else if (wallpaperPath.startsWith("#")) {
|
||||
colorMatugenProcess.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: matugenProcess
|
||||
command: ["matugen", "image", wallpaperPath, "--json", "hex"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: matugenCollector
|
||||
onStreamFinished: {
|
||||
if (!matugenCollector.text) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Wallpaper Processing Failed: Empty JSON extracted from matugen output.")
|
||||
}
|
||||
return
|
||||
}
|
||||
const extractedJson = extractJsonFromText(matugenCollector.text)
|
||||
if (!extractedJson) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Wallpaper Processing Failed: Invalid JSON extracted from matugen output.")
|
||||
}
|
||||
console.log("Raw matugen output:", matugenCollector.text)
|
||||
return
|
||||
}
|
||||
try {
|
||||
root.matugenColors = JSON.parse(extractedJson)
|
||||
root.colorUpdateTrigger++
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.clearWallpaperError()
|
||||
}
|
||||
} catch (e) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Wallpaper processing failed (JSON parse error after extraction)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: code => {
|
||||
if (code !== 0) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Matugen command failed with exit code " + code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: colorMatugenProcess
|
||||
command: ["matugen", "color", "hex", wallpaperPath, "--json", "hex"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: colorMatugenCollector
|
||||
onStreamFinished: {
|
||||
if (!colorMatugenCollector.text) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Color Processing Failed: Empty JSON extracted from matugen output.")
|
||||
}
|
||||
return
|
||||
}
|
||||
const extractedJson = extractJsonFromText(colorMatugenCollector.text)
|
||||
if (!extractedJson) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Color Processing Failed: Invalid JSON extracted from matugen output.")
|
||||
}
|
||||
console.log("Raw matugen output:", colorMatugenCollector.text)
|
||||
return
|
||||
}
|
||||
try {
|
||||
root.matugenColors = JSON.parse(extractedJson)
|
||||
root.colorUpdateTrigger++
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.clearWallpaperError()
|
||||
}
|
||||
} catch (e) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Color processing failed (JSON parse error after extraction)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onExited: code => {
|
||||
if (code !== 0) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Matugen color command failed with exit code " + code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: ensureStateDir
|
||||
@@ -730,7 +748,7 @@ Singleton {
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
if (typeof ToastService !== "undefined" && typeof NiriService !== "undefined" && !NiriService.matugenSuppression) {
|
||||
ToastService.showInfo("GTK colors applied successfully")
|
||||
}
|
||||
} else {
|
||||
@@ -794,6 +812,48 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: dynamicColorsFileView
|
||||
path: stateDir + "/dms-colors.json"
|
||||
watchChanges: currentTheme === dynamic
|
||||
|
||||
function parseAndLoadColors() {
|
||||
try {
|
||||
const colorsText = dynamicColorsFileView.text()
|
||||
if (colorsText) {
|
||||
root.matugenColors = JSON.parse(colorsText)
|
||||
root.colorUpdateTrigger++
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.clearWallpaperError()
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (typeof ToastService !== "undefined") {
|
||||
ToastService.wallpaperErrorStatus = "error"
|
||||
ToastService.showError("Dynamic colors parse error: " + e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
if (currentTheme === dynamic) {
|
||||
parseAndLoadColors()
|
||||
}
|
||||
}
|
||||
|
||||
onFileChanged: {
|
||||
if (currentTheme === dynamic) {
|
||||
dynamicColorsFileView.reload()
|
||||
}
|
||||
}
|
||||
|
||||
onLoadFailed: function (error) {
|
||||
if (currentTheme === dynamic && typeof ToastService !== "undefined") {
|
||||
ToastService.showError("Failed to read dynamic colors: " + error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "theme"
|
||||
|
||||
@@ -803,12 +863,12 @@ Singleton {
|
||||
}
|
||||
|
||||
function light(): string {
|
||||
root.setLightMode(true)
|
||||
root.setLightMode(true, true, true)
|
||||
return "light"
|
||||
}
|
||||
|
||||
function dark(): string {
|
||||
root.setLightMode(false)
|
||||
root.setLightMode(false, true, true)
|
||||
return "dark"
|
||||
}
|
||||
|
||||
@@ -816,4 +876,35 @@ Singleton {
|
||||
return root.isLightMode ? "light" : "dark"
|
||||
}
|
||||
}
|
||||
|
||||
// These timers are for screen transitions, since sometimes QML still beats the niri call
|
||||
Timer {
|
||||
id: themeTransitionTimer
|
||||
interval: 50
|
||||
repeat: false
|
||||
property string themeName: ""
|
||||
property bool savePrefs: true
|
||||
onTriggered: root.switchTheme(themeName, savePrefs, false)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: lightModeTransitionTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
property bool lightMode: false
|
||||
property bool savePrefs: true
|
||||
onTriggered: root.setLightMode(lightMode, savePrefs, false)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: themeCategoryTransitionTimer
|
||||
interval: 50
|
||||
repeat: false
|
||||
property string category: ""
|
||||
property string defaultTheme: ""
|
||||
onTriggered: {
|
||||
root.currentThemeCategory = category
|
||||
root.switchTheme(defaultTheme, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,15 +77,12 @@ Item {
|
||||
width: parent.width
|
||||
height: parent.height - ClipboardConstants.headerHeight - 70
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceLight
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
clip: true
|
||||
|
||||
DankListView {
|
||||
id: clipboardListView
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
model: filteredModel
|
||||
|
||||
currentIndex: clipboardContent.modal ? clipboardContent.modal.selectedIndex : 0
|
||||
|
||||
@@ -24,17 +24,10 @@ Rectangle {
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (isSelected) {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2)
|
||||
return Theme.primaryPressed
|
||||
}
|
||||
return mouseArea.containsMouse ? Theme.primaryHover : Theme.primaryBackground
|
||||
return mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||
}
|
||||
border.color: {
|
||||
if (isSelected) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5)
|
||||
}
|
||||
return Theme.outlineStrong
|
||||
}
|
||||
border.width: isSelected ? 1.5 : 1
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -53,7 +53,7 @@ QtObject {
|
||||
modal.hide()
|
||||
event.accepted = true
|
||||
}
|
||||
} else if (event.key === Qt.Key_Down) {
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Tab) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
@@ -62,7 +62,7 @@ QtObject {
|
||||
selectNext()
|
||||
event.accepted = true
|
||||
}
|
||||
} else if (event.key === Qt.Key_Up) {
|
||||
} else if (event.key === Qt.Key_Up || event.key === Qt.Key_Backtab) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
@@ -74,6 +74,42 @@ QtObject {
|
||||
selectPrevious()
|
||||
event.accepted = true
|
||||
}
|
||||
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
} else {
|
||||
selectNext()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
} else if (modal.selectedIndex === 0) {
|
||||
modal.keyboardNavigationActive = false
|
||||
} else {
|
||||
selectPrevious()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
} else {
|
||||
selectNext()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||
if (!modal.keyboardNavigationActive) {
|
||||
modal.keyboardNavigationActive = true
|
||||
modal.selectedIndex = 0
|
||||
} else if (modal.selectedIndex === 0) {
|
||||
modal.keyboardNavigationActive = false
|
||||
} else {
|
||||
selectPrevious()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) {
|
||||
modal.clearAll()
|
||||
modal.hide()
|
||||
|
||||
37
Modals/ColorPickerModal.qml
Normal file
37
Modals/ColorPickerModal.qml
Normal file
@@ -0,0 +1,37 @@
|
||||
import QtQuick
|
||||
import Qt.labs.platform
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: colorPickerModal
|
||||
|
||||
signal colorSelected(color selectedColor)
|
||||
|
||||
function show() {
|
||||
colorDialog.open()
|
||||
}
|
||||
|
||||
function hide() {
|
||||
colorDialog.close()
|
||||
}
|
||||
|
||||
function copyColorToClipboard(colorValue) {
|
||||
Quickshell.execDetached(["sh", "-c", `echo "${colorValue}" | wl-copy`])
|
||||
ToastService.showInfo(`Color ${colorValue} copied to clipboard`)
|
||||
console.log("Copied color to clipboard:", colorValue)
|
||||
}
|
||||
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
title: "Color Picker - Select and copy color"
|
||||
color: Theme.primary
|
||||
|
||||
onAccepted: {
|
||||
const colorString = color.toString()
|
||||
copyColorToClipboard(colorString)
|
||||
colorSelected(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,48 @@ DankModal {
|
||||
selectedButton = 1
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_N:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = (selectedButton + 1) % 2
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_P:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = selectedButton === -1 ? 1 : (selectedButton - 1 + 2) % 2
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_J:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = 1
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_K:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = 0
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_H:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = 0
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_L:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
keyboardNavigation = true
|
||||
selectedButton = 1
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_Tab:
|
||||
keyboardNavigation = true
|
||||
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2
|
||||
|
||||
@@ -22,7 +22,7 @@ PanelWindow {
|
||||
property bool closeOnEscapeKey: true
|
||||
property bool closeOnBackgroundClick: true
|
||||
property string animationType: "scale"
|
||||
property int animationDuration: Theme.shorterDuration
|
||||
property int animationDuration: Theme.shortDuration
|
||||
property var animationEasing: Theme.emphasizedEasing
|
||||
property color backgroundColor: Theme.surfaceContainer
|
||||
property color borderColor: Theme.outlineMedium
|
||||
@@ -34,6 +34,7 @@ PanelWindow {
|
||||
property bool shouldHaveFocus: shouldBeVisible
|
||||
property bool allowFocusOverride: false
|
||||
property bool allowStacking: false
|
||||
property bool keepContentLoaded: false
|
||||
|
||||
signal opened
|
||||
signal dialogClosed
|
||||
@@ -90,7 +91,7 @@ PanelWindow {
|
||||
Timer {
|
||||
id: closeTimer
|
||||
|
||||
interval: animationDuration + 50
|
||||
interval: animationDuration + 100
|
||||
onTriggered: {
|
||||
visible = false
|
||||
}
|
||||
@@ -158,7 +159,6 @@ PanelWindow {
|
||||
border.width: root.borderWidth
|
||||
layer.enabled: root.enableShadow
|
||||
opacity: root.shouldBeVisible ? 1 : 0
|
||||
scale: root.animationType === "scale" ? (root.shouldBeVisible ? 1 : 0.9) : 1
|
||||
transform: root.animationType === "slide" ? slideTransform : null
|
||||
|
||||
Translate {
|
||||
@@ -172,7 +172,7 @@ PanelWindow {
|
||||
id: contentLoader
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.visible
|
||||
active: root.keepContentLoaded || root.shouldBeVisible || root.visible
|
||||
asynchronous: false
|
||||
}
|
||||
|
||||
@@ -183,15 +183,6 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
enabled: root.animationType === "scale"
|
||||
|
||||
NumberAnimation {
|
||||
duration: root.animationDuration
|
||||
easing.type: root.animationEasing
|
||||
}
|
||||
}
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
@@ -207,8 +198,8 @@ PanelWindow {
|
||||
|
||||
objectName: "modalFocusScope"
|
||||
anchors.fill: parent
|
||||
visible: root.visible // Only active when the modal is visible
|
||||
focus: root.visible
|
||||
visible: root.shouldBeVisible || root.visible
|
||||
focus: root.shouldBeVisible
|
||||
Keys.onEscapePressed: event => {
|
||||
if (root.closeOnEscapeKey && shouldHaveFocus) {
|
||||
root.close()
|
||||
@@ -223,7 +214,7 @@ PanelWindow {
|
||||
|
||||
Connections {
|
||||
function onShouldHaveFocusChanged() {
|
||||
if (shouldHaveFocus && visible) {
|
||||
if (shouldHaveFocus && shouldBeVisible) {
|
||||
Qt.callLater(() => focusScope.forceActiveFocus())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ DankModal {
|
||||
property bool selectedFileIsDir: false
|
||||
property bool showOverwriteConfirmation: false
|
||||
property string pendingFilePath: ""
|
||||
property bool weAvailable: false
|
||||
property string wePath: ""
|
||||
property bool weMode: false
|
||||
|
||||
signal fileSelected(string path)
|
||||
|
||||
@@ -131,6 +134,31 @@ DankModal {
|
||||
Component.onCompleted: {
|
||||
currentPath = getLastPath()
|
||||
}
|
||||
|
||||
property var steamPaths: [
|
||||
StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/.steam/steam/steamapps/workshop/content/431960",
|
||||
StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/.local/share/Steam/steamapps/workshop/content/431960",
|
||||
StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/workshop/content/431960",
|
||||
StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/snap/steam/common/.local/share/Steam/steamapps/workshop/content/431960"
|
||||
]
|
||||
property int currentPathIndex: 0
|
||||
|
||||
function discoverWallpaperEngine() {
|
||||
currentPathIndex = 0
|
||||
checkNextPath()
|
||||
}
|
||||
|
||||
function checkNextPath() {
|
||||
if (currentPathIndex >= steamPaths.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const wePath = steamPaths[currentPathIndex]
|
||||
const cleanPath = wePath.replace(/^file:\/\//, '')
|
||||
weDiscoveryProcess.command = ["test", "-d", cleanPath]
|
||||
weDiscoveryProcess.wePath = wePath
|
||||
weDiscoveryProcess.running = true
|
||||
}
|
||||
width: 800
|
||||
height: 600
|
||||
enableShadow: true
|
||||
@@ -148,6 +176,9 @@ DankModal {
|
||||
selectedIndex = -1
|
||||
keyboardNavigationActive = false
|
||||
backButtonFocused = false
|
||||
if (browserType === "wallpaper" && !weAvailable) {
|
||||
discoverWallpaperEngine()
|
||||
}
|
||||
}
|
||||
}
|
||||
onCurrentPathChanged: {
|
||||
@@ -208,7 +239,12 @@ DankModal {
|
||||
return
|
||||
}
|
||||
if (!keyboardNavigationActive) {
|
||||
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right) {
|
||||
const isInitKey = event.key === Qt.Key_Tab || event.key === Qt.Key_Down || event.key === Qt.Key_Right ||
|
||||
(event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) ||
|
||||
(event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) ||
|
||||
(event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier)
|
||||
|
||||
if (isInitKey) {
|
||||
keyboardNavigationActive = true
|
||||
if (currentPath !== homeDir) {
|
||||
backButtonFocused = true
|
||||
@@ -250,6 +286,69 @@ DankModal {
|
||||
}
|
||||
event.accepted = true
|
||||
break
|
||||
case Qt.Key_N:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (backButtonFocused) {
|
||||
backButtonFocused = false
|
||||
selectedIndex = 0
|
||||
} else if (selectedIndex < totalItems - 1) {
|
||||
selectedIndex++
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_P:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (selectedIndex > 0) {
|
||||
selectedIndex--
|
||||
} else if (currentPath !== homeDir) {
|
||||
backButtonFocused = true
|
||||
selectedIndex = -1
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_J:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (selectedIndex < totalItems - 1) {
|
||||
selectedIndex++
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_K:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (selectedIndex > 0) {
|
||||
selectedIndex--
|
||||
} else if (currentPath !== homeDir) {
|
||||
backButtonFocused = true
|
||||
selectedIndex = -1
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_H:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (!backButtonFocused && selectedIndex > 0) {
|
||||
selectedIndex--
|
||||
} else if (currentPath !== homeDir) {
|
||||
backButtonFocused = true
|
||||
selectedIndex = -1
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_L:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
if (backButtonFocused) {
|
||||
backButtonFocused = false
|
||||
selectedIndex = 0
|
||||
} else if (selectedIndex < totalItems - 1) {
|
||||
selectedIndex++
|
||||
}
|
||||
event.accepted = true
|
||||
}
|
||||
break
|
||||
case Qt.Key_Left:
|
||||
if (backButtonFocused)
|
||||
return
|
||||
@@ -334,6 +433,23 @@ DankModal {
|
||||
executeKeyboardSelection(targetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: weDiscoveryProcess
|
||||
|
||||
property string wePath: ""
|
||||
running: false
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
fileBrowserModal.weAvailable = true
|
||||
fileBrowserModal.wePath = wePath
|
||||
} else {
|
||||
currentPathIndex++
|
||||
checkNextPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Item {
|
||||
@@ -373,6 +489,22 @@ DankModal {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
circular: false
|
||||
iconName: "movie"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: weMode ? Theme.primary : Theme.surfaceText
|
||||
visible: weAvailable && browserType === "wallpaper"
|
||||
onClicked: {
|
||||
weMode = !weMode
|
||||
if (weMode) {
|
||||
navigateTo(wePath)
|
||||
} else {
|
||||
navigateTo(getLastPath())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
circular: false
|
||||
iconName: "info"
|
||||
@@ -439,8 +571,8 @@ DankModal {
|
||||
width: parent.width
|
||||
height: parent.height - 80
|
||||
clip: true
|
||||
cellWidth: 150
|
||||
cellHeight: 130
|
||||
cellWidth: weMode ? 255 : 150
|
||||
cellHeight: weMode ? 215 : 130
|
||||
cacheBuffer: 260
|
||||
model: folderModel
|
||||
currentIndex: selectedIndex
|
||||
@@ -466,8 +598,8 @@ DankModal {
|
||||
required property url fileURL
|
||||
required property int index
|
||||
|
||||
width: 140
|
||||
height: 120
|
||||
width: weMode ? 245 : 140
|
||||
height: weMode ? 205 : 120
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
|
||||
@@ -498,16 +630,33 @@ DankModal {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Item {
|
||||
width: 80
|
||||
height: 60
|
||||
width: weMode ? 225 : 80
|
||||
height: weMode ? 165 : 60
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
CachingImage {
|
||||
anchors.fill: parent
|
||||
source: (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : ""
|
||||
property var weExtensions: [".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tga"]
|
||||
property int weExtIndex: 0
|
||||
source: {
|
||||
if (weMode && delegateRoot.fileIsDir) {
|
||||
return "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
|
||||
}
|
||||
return (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : ""
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (weMode && delegateRoot.fileIsDir && status === Image.Error) {
|
||||
if (weExtIndex < weExtensions.length - 1) {
|
||||
weExtIndex++
|
||||
source = "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
|
||||
} else {
|
||||
source = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
visible: !delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)
|
||||
maxCacheSize: 80
|
||||
visible: (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) || (weMode && delegateRoot.fileIsDir)
|
||||
maxCacheSize: weMode ? 225 : 80
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
@@ -523,7 +672,7 @@ DankModal {
|
||||
name: "folder"
|
||||
size: Theme.iconSizeLarge
|
||||
color: Theme.primary
|
||||
visible: delegateRoot.fileIsDir
|
||||
visible: delegateRoot.fileIsDir && !weMode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,11 +699,15 @@ DankModal {
|
||||
// Update selected file info and index first
|
||||
selectedIndex = delegateRoot.index
|
||||
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
|
||||
if (delegateRoot.fileIsDir) {
|
||||
if (weMode && delegateRoot.fileIsDir) {
|
||||
var sceneId = delegateRoot.filePath.split("/").pop()
|
||||
fileSelected("we:" + sceneId)
|
||||
fileBrowserModal.close()
|
||||
} else if (delegateRoot.fileIsDir) {
|
||||
navigateTo(delegateRoot.filePath)
|
||||
} else {
|
||||
fileSelected(delegateRoot.filePath)
|
||||
fileBrowserModal.close() // Close modal after file selection
|
||||
fileBrowserModal.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,7 +719,11 @@ DankModal {
|
||||
fileBrowserModal.keyboardSelectionRequested = false
|
||||
selectedIndex = delegateRoot.index
|
||||
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
|
||||
if (delegateRoot.fileIsDir) {
|
||||
if (weMode && delegateRoot.fileIsDir) {
|
||||
var sceneId = delegateRoot.filePath.split("/").pop()
|
||||
fileSelected("we:" + sceneId)
|
||||
fileBrowserModal.close()
|
||||
} else if (delegateRoot.fileIsDir) {
|
||||
navigateTo(delegateRoot.filePath)
|
||||
} else {
|
||||
fileSelected(delegateRoot.filePath)
|
||||
|
||||
@@ -27,6 +27,7 @@ DankModal {
|
||||
modalKeyboardController.updateSelectedIdFromIndex()
|
||||
if (notificationListRef) {
|
||||
notificationListRef.keyboardActive = true
|
||||
notificationListRef.currentIndex = 0
|
||||
}
|
||||
modalKeyboardController.selectionVersion++
|
||||
modalKeyboardController.ensureVisible()
|
||||
|
||||
@@ -1,45 +1,51 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modals.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankModal {
|
||||
id: root
|
||||
|
||||
property int selectedIndex: 0
|
||||
property int optionCount: 4
|
||||
property int optionCount: SessionService.hibernateSupported ? 5 : 4
|
||||
|
||||
signal powerActionRequested(string action, string title, string message)
|
||||
|
||||
function selectOption() {
|
||||
function selectOption(action) {
|
||||
close();
|
||||
const actions = [{
|
||||
"action": "logout",
|
||||
"title": "Log Out",
|
||||
"message": "Are you sure you want to log out?"
|
||||
}, {
|
||||
"action": "suspend",
|
||||
"title": "Suspend",
|
||||
"message": "Are you sure you want to suspend the system?"
|
||||
}, {
|
||||
"action": "reboot",
|
||||
"title": "Reboot",
|
||||
"message": "Are you sure you want to reboot the system?"
|
||||
}, {
|
||||
"action": "poweroff",
|
||||
"title": "Power Off",
|
||||
"message": "Are you sure you want to power off the system?"
|
||||
}];
|
||||
const selected = actions[selectedIndex];
|
||||
const actions = {
|
||||
"logout": {
|
||||
"title": "Log Out",
|
||||
"message": "Are you sure you want to log out?"
|
||||
},
|
||||
"suspend": {
|
||||
"title": "Suspend",
|
||||
"message": "Are you sure you want to suspend the system?"
|
||||
},
|
||||
"hibernate": {
|
||||
"title": "Hibernate",
|
||||
"message": "Are you sure you want to hibernate the system?"
|
||||
},
|
||||
"reboot": {
|
||||
"title": "Reboot",
|
||||
"message": "Are you sure you want to reboot the system?"
|
||||
},
|
||||
"poweroff": {
|
||||
"title": "Power Off",
|
||||
"message": "Are you sure you want to power off the system?"
|
||||
}
|
||||
}
|
||||
const selected = actions[action]
|
||||
if (selected) {
|
||||
root.powerActionRequested(selected.action, selected.title, selected.message);
|
||||
root.powerActionRequested(action, selected.title, selected.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shouldBeVisible: false
|
||||
width: 320
|
||||
height: 300
|
||||
height: contentLoader.item ? contentLoader.item.implicitHeight : 300
|
||||
enableShadow: true
|
||||
onBackgroundClicked: () => {
|
||||
return close();
|
||||
@@ -51,30 +57,59 @@ DankModal {
|
||||
modalFocusScope.Keys.onPressed: (event) => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Backtab:
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Tab:
|
||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
selectOption();
|
||||
const actions = ["logout", "suspend"];
|
||||
if (SessionService.hibernateSupported) actions.push("hibernate");
|
||||
actions.push("reboot", "poweroff");
|
||||
if (selectedIndex < actions.length) {
|
||||
selectOption(actions[selectedIndex]);
|
||||
}
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_N:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_P:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_J:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex + 1) % optionCount;
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
case Qt.Key_K:
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
|
||||
event.accepted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: mainColumn.implicitHeight + Theme.spacingL * 2
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
@@ -157,7 +192,7 @@ DankModal {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
selectedIndex = 0;
|
||||
selectOption();
|
||||
selectOption("logout");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +245,7 @@ DankModal {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
selectedIndex = 1;
|
||||
selectOption();
|
||||
selectOption("suspend");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,15 +257,70 @@ DankModal {
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (selectedIndex === 2) {
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||
} else if (rebootArea.containsMouse) {
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.08);
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||
} else if (hibernateArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === 2 ? Theme.warning : "transparent"
|
||||
border.color: selectedIndex === 2 ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === 2 ? 1 : 0
|
||||
visible: SessionService.hibernateSupported
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "ac_unit"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Hibernate"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hibernateArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
selectedIndex = 2;
|
||||
selectOption("hibernate");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
const rebootIndex = SessionService.hibernateSupported ? 3 : 2;
|
||||
if (selectedIndex === rebootIndex) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||
} else if (rebootArea.containsMouse) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === (SessionService.hibernateSupported ? 3 : 2) ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === (SessionService.hibernateSupported ? 3 : 2) ? 1 : 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -262,8 +352,8 @@ DankModal {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
selectedIndex = 2;
|
||||
selectOption();
|
||||
selectedIndex = SessionService.hibernateSupported ? 3 : 2;
|
||||
selectOption("reboot");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,16 +364,17 @@ DankModal {
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (selectedIndex === 3) {
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
||||
const powerOffIndex = SessionService.hibernateSupported ? 4 : 3;
|
||||
if (selectedIndex === powerOffIndex) {
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
||||
} else if (powerOffArea.containsMouse) {
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08);
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
||||
} else {
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||
}
|
||||
}
|
||||
border.color: selectedIndex === 3 ? Theme.error : "transparent"
|
||||
border.width: selectedIndex === 3 ? 1 : 0
|
||||
border.color: selectedIndex === (SessionService.hibernateSupported ? 4 : 3) ? Theme.primary : "transparent"
|
||||
border.width: selectedIndex === (SessionService.hibernateSupported ? 4 : 3) ? 1 : 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -315,8 +406,8 @@ DankModal {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: () => {
|
||||
selectedIndex = 3;
|
||||
selectOption();
|
||||
selectedIndex = SessionService.hibernateSupported ? 4 : 3;
|
||||
selectOption("poweroff");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,14 +419,6 @@ DankModal {
|
||||
height: Theme.spacingS
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "↑↓ Navigate • Tab Cycle • Enter Select • Esc Close"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ DankModal {
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 52
|
||||
color: Theme.surfaceSelected
|
||||
color: Theme.surfaceContainerHigh
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
@@ -281,7 +281,7 @@ DankModal {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceLight
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
|
||||
|
||||
246
Modals/Settings/PowerSettings.qml
Normal file
246
Modals/Settings/PowerSettings.qml
Normal file
@@ -0,0 +1,246 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: powerTab
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Theme.spacingL
|
||||
clip: true
|
||||
contentHeight: mainColumn.height
|
||||
contentWidth: width
|
||||
|
||||
Column {
|
||||
id: mainColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXL
|
||||
|
||||
StyledText {
|
||||
text: "Battery not detected - only AC power settings available"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
visible: !BatteryService.batteryAvailable
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: timeoutSection.implicitHeight + Theme.spacingL * 2
|
||||
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.2)
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
id: timeoutSection
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "schedule"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Idle Settings"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: Math.max(0, parent.width - parent.children[0].width - parent.children[1].width - powerCategory.width - Theme.spacingM * 3)
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: powerCategory
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
model: ["AC Power", "Battery"]
|
||||
currentIndex: 0
|
||||
selectionMode: "single"
|
||||
checkEnabled: false
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: lockDropdown
|
||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||
|
||||
width: parent.width
|
||||
text: "Automatically lock after"
|
||||
options: timeoutOptions
|
||||
|
||||
Connections {
|
||||
target: powerCategory
|
||||
function onCurrentIndexChanged() {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acLockTimeout : SessionData.batteryLockTimeout
|
||||
const index = lockDropdown.timeoutValues.indexOf(currentTimeout)
|
||||
lockDropdown.currentValue = index >= 0 ? lockDropdown.timeoutOptions[index] : "Never"
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acLockTimeout : SessionData.batteryLockTimeout
|
||||
const index = timeoutValues.indexOf(currentTimeout)
|
||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||
}
|
||||
|
||||
onValueChanged: value => {
|
||||
const index = timeoutOptions.indexOf(value)
|
||||
if (index >= 0) {
|
||||
const timeout = timeoutValues[index]
|
||||
if (powerCategory.currentIndex === 0) {
|
||||
SessionData.setAcLockTimeout(timeout)
|
||||
} else {
|
||||
SessionData.setBatteryLockTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: monitorDropdown
|
||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||
|
||||
width: parent.width
|
||||
text: "Turn off monitors after"
|
||||
options: timeoutOptions
|
||||
|
||||
Connections {
|
||||
target: powerCategory
|
||||
function onCurrentIndexChanged() {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acMonitorTimeout : SessionData.batteryMonitorTimeout
|
||||
const index = monitorDropdown.timeoutValues.indexOf(currentTimeout)
|
||||
monitorDropdown.currentValue = index >= 0 ? monitorDropdown.timeoutOptions[index] : "Never"
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acMonitorTimeout : SessionData.batteryMonitorTimeout
|
||||
const index = timeoutValues.indexOf(currentTimeout)
|
||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||
}
|
||||
|
||||
onValueChanged: value => {
|
||||
const index = timeoutOptions.indexOf(value)
|
||||
if (index >= 0) {
|
||||
const timeout = timeoutValues[index]
|
||||
if (powerCategory.currentIndex === 0) {
|
||||
SessionData.setAcMonitorTimeout(timeout)
|
||||
} else {
|
||||
SessionData.setBatteryMonitorTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: suspendDropdown
|
||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||
|
||||
width: parent.width
|
||||
text: "Suspend system after"
|
||||
options: timeoutOptions
|
||||
|
||||
Connections {
|
||||
target: powerCategory
|
||||
function onCurrentIndexChanged() {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acSuspendTimeout : SessionData.batterySuspendTimeout
|
||||
const index = suspendDropdown.timeoutValues.indexOf(currentTimeout)
|
||||
suspendDropdown.currentValue = index >= 0 ? suspendDropdown.timeoutOptions[index] : "Never"
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acSuspendTimeout : SessionData.batterySuspendTimeout
|
||||
const index = timeoutValues.indexOf(currentTimeout)
|
||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||
}
|
||||
|
||||
onValueChanged: value => {
|
||||
const index = timeoutOptions.indexOf(value)
|
||||
if (index >= 0) {
|
||||
const timeout = timeoutValues[index]
|
||||
if (powerCategory.currentIndex === 0) {
|
||||
SessionData.setAcSuspendTimeout(timeout)
|
||||
} else {
|
||||
SessionData.setBatterySuspendTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
id: hibernateDropdown
|
||||
property var timeoutOptions: ["Never", "1 minute", "2 minutes", "3 minutes", "5 minutes", "10 minutes", "15 minutes", "20 minutes", "30 minutes", "1 hour", "1 hour 30 minutes", "2 hours", "3 hours"]
|
||||
property var timeoutValues: [0, 60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 5400, 7200, 10800]
|
||||
|
||||
width: parent.width
|
||||
text: "Hibernate system after"
|
||||
options: timeoutOptions
|
||||
visible: SessionService.hibernateSupported
|
||||
|
||||
Connections {
|
||||
target: powerCategory
|
||||
function onCurrentIndexChanged() {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acHibernateTimeout : SessionData.batteryHibernateTimeout
|
||||
const index = hibernateDropdown.timeoutValues.indexOf(currentTimeout)
|
||||
hibernateDropdown.currentValue = index >= 0 ? hibernateDropdown.timeoutOptions[index] : "Never"
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const currentTimeout = powerCategory.currentIndex === 0 ? SessionData.acHibernateTimeout : SessionData.batteryHibernateTimeout
|
||||
const index = timeoutValues.indexOf(currentTimeout)
|
||||
currentValue = index >= 0 ? timeoutOptions[index] : "Never"
|
||||
}
|
||||
|
||||
onValueChanged: value => {
|
||||
const index = timeoutOptions.indexOf(value)
|
||||
if (index >= 0) {
|
||||
const timeout = timeoutValues[index]
|
||||
if (powerCategory.currentIndex === 0) {
|
||||
SessionData.setAcHibernateTimeout(timeout)
|
||||
} else {
|
||||
SessionData.setBatteryHibernateTimeout(timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: "Lock before suspend"
|
||||
description: "Automatically lock the screen when the system prepares to suspend"
|
||||
checked: SessionData.lockBeforeSuspend
|
||||
onToggled: checked => SessionData.setLockBeforeSuspend(checked)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Idle monitoring not supported - requires newer Quickshell version"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: !IdleService.idleMonitorAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,25 +25,15 @@ Rectangle {
|
||||
Item {
|
||||
id: profileImageContainer
|
||||
|
||||
property bool hasImage: profileImageSource.status === Image.Ready
|
||||
|
||||
width: 80
|
||||
height: 80
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle {
|
||||
DankCircularImage {
|
||||
id: profileImage
|
||||
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: Theme.primary
|
||||
border.width: 1
|
||||
visible: parent.hasImage
|
||||
}
|
||||
|
||||
Image {
|
||||
id: profileImageSource
|
||||
|
||||
source: {
|
||||
imageSource: {
|
||||
if (PortalService.profileImage === "") {
|
||||
return "";
|
||||
}
|
||||
@@ -52,55 +42,7 @@ Rectangle {
|
||||
}
|
||||
return PortalService.profileImage;
|
||||
}
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
mipmap: true
|
||||
cache: true
|
||||
visible: false
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
source: profileImageSource
|
||||
maskEnabled: true
|
||||
maskSource: profileCircularMask
|
||||
visible: profileImageContainer.hasImage
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
|
||||
Item {
|
||||
id: profileCircularMask
|
||||
|
||||
width: 70
|
||||
height: 70
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "black"
|
||||
antialiasing: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: Theme.primary
|
||||
visible: !parent.hasImage
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "person"
|
||||
size: Theme.iconSizeLarge
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
fallbackIcon: "person"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -147,7 +89,7 @@ Rectangle {
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Qt.rgba(255, 255, 255, 0.9)
|
||||
visible: profileImageContainer.hasImage
|
||||
visible: profileImage.hasImage
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -67,7 +67,7 @@ Item {
|
||||
visible: active
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: TopBarTab {
|
||||
sourceComponent: DankBarTab {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -115,14 +115,14 @@ Item {
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: recentAppsLoader
|
||||
id: launcherLoader
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.currentIndex === 7
|
||||
visible: active
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: RecentAppsTab {
|
||||
sourceComponent: LauncherTab {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -141,13 +141,26 @@ Item {
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: aboutLoader
|
||||
id: powerLoader
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.currentIndex === 9
|
||||
visible: active
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: PowerSettings {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: aboutLoader
|
||||
|
||||
anchors.fill: parent
|
||||
active: root.currentIndex === 10
|
||||
visible: active
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: AboutTab {
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ DankModal {
|
||||
id: settingsModal
|
||||
|
||||
property Component settingsContent
|
||||
property alias profileBrowser: profileBrowser
|
||||
|
||||
signal closingModal()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Rectangle {
|
||||
"text": "Weather",
|
||||
"icon": "cloud"
|
||||
}, {
|
||||
"text": "Top Bar",
|
||||
"text": "Dank Bar",
|
||||
"icon": "toolbar"
|
||||
}, {
|
||||
"text": "Widgets",
|
||||
@@ -30,11 +30,14 @@ Rectangle {
|
||||
"text": "Displays",
|
||||
"icon": "monitor"
|
||||
}, {
|
||||
"text": "Recent Apps",
|
||||
"icon": "history"
|
||||
"text": "Launcher",
|
||||
"icon": "apps"
|
||||
}, {
|
||||
"text": "Theme & Colors",
|
||||
"icon": "palette"
|
||||
}, {
|
||||
"text": "Power",
|
||||
"icon": "power_settings_new"
|
||||
}, {
|
||||
"text": "About",
|
||||
"icon": "info"
|
||||
@@ -80,7 +83,7 @@ Rectangle {
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: isActive ? Theme.surfaceContainerHigh : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
color: isActive ? Theme.primaryContainer : tabMouseArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.primaryContainer, 0)
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -91,14 +94,14 @@ Rectangle {
|
||||
DankIcon {
|
||||
name: modelData.icon || ""
|
||||
size: Theme.iconSize - 2
|
||||
color: parent.parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.text || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: parent.parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
color: parent.parent.isActive ? Theme.surfaceText : Theme.surfaceText
|
||||
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
@@ -33,6 +33,46 @@ Item {
|
||||
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectPreviousInRow()
|
||||
event.accepted = true
|
||||
} else if (event.key == Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectNext()
|
||||
event.accepted = true
|
||||
} else if (event.key == Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectPrevious()
|
||||
event.accepted = true
|
||||
} else if (event.key == Qt.Key_L && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectNextInRow()
|
||||
event.accepted = true
|
||||
} else if (event.key == Qt.Key_H && event.modifiers & Qt.ControlModifier && appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectPreviousInRow()
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Tab) {
|
||||
if (appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectNextInRow()
|
||||
} else {
|
||||
appLauncher.selectNext()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Backtab) {
|
||||
if (appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectPreviousInRow()
|
||||
} else {
|
||||
appLauncher.selectPrevious()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||
if (appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectNextInRow()
|
||||
} else {
|
||||
appLauncher.selectNext()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||
if (appLauncher.viewMode === "grid") {
|
||||
appLauncher.selectPreviousInRow()
|
||||
} else {
|
||||
appLauncher.selectPrevious()
|
||||
}
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
appLauncher.launchSelected()
|
||||
event.accepted = true
|
||||
@@ -59,23 +99,21 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: categorySelector.height + Theme.spacingM * 2
|
||||
height: categorySelector.height + Theme.spacingS * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceVariantAlpha
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
visible: appLauncher.categories.length > 1 || appLauncher.model.count > 0
|
||||
|
||||
CategorySelector {
|
||||
id: categorySelector
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
categories: appLauncher.categories
|
||||
selectedCategory: appLauncher.selectedCategory
|
||||
compact: false
|
||||
@@ -88,14 +126,15 @@ Item {
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
leftPadding: Theme.spacingS
|
||||
|
||||
DankTextField {
|
||||
id: searchField
|
||||
|
||||
width: parent.width - 80 - Theme.spacingM
|
||||
width: parent.width - 80 - Theme.spacingL
|
||||
height: 56
|
||||
cornerRadius: Theme.cornerRadius
|
||||
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
|
||||
backgroundColor: Theme.surfaceContainerHigh
|
||||
normalBorderColor: Theme.outlineMedium
|
||||
focusedBorderColor: Theme.primary
|
||||
leftIconName: "search"
|
||||
@@ -125,7 +164,7 @@ Item {
|
||||
else if (appLauncher.model.count > 0)
|
||||
appLauncher.launchApp(appLauncher.model.get(0))
|
||||
event.accepted = true
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||
event.accepted = false
|
||||
}
|
||||
}
|
||||
@@ -141,8 +180,6 @@ Item {
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
border.color: appLauncher.viewMode === "list" ? Theme.primarySelected : "transparent"
|
||||
border.width: 1
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
@@ -168,8 +205,6 @@ Item {
|
||||
height: 36
|
||||
radius: Theme.cornerRadius
|
||||
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
border.color: appLauncher.viewMode === "grid" ? Theme.primarySelected : "transparent"
|
||||
border.width: 1
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -32,11 +32,7 @@ DankModal {
|
||||
function hide() {
|
||||
spotlightOpen = false
|
||||
close()
|
||||
if (contentLoader.item && contentLoader.item.appLauncher) {
|
||||
contentLoader.item.appLauncher.searchQuery = ""
|
||||
contentLoader.item.appLauncher.selectedIndex = 0
|
||||
contentLoader.item.appLauncher.setCategory("All")
|
||||
}
|
||||
cleanupTimer.restart()
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
@@ -55,6 +51,7 @@ DankModal {
|
||||
borderColor: Theme.outlineMedium
|
||||
borderWidth: 1
|
||||
enableShadow: true
|
||||
keepContentLoaded: true
|
||||
onVisibleChanged: () => {
|
||||
if (visible && !spotlightOpen) {
|
||||
show()
|
||||
@@ -72,6 +69,19 @@ DankModal {
|
||||
}
|
||||
content: spotlightContent
|
||||
|
||||
Timer {
|
||||
id: cleanupTimer
|
||||
|
||||
interval: animationDuration + 50
|
||||
onTriggered: {
|
||||
if (contentLoader.item && contentLoader.item.appLauncher) {
|
||||
contentLoader.item.appLauncher.searchQuery = ""
|
||||
contentLoader.item.appLauncher.selectedIndex = 0
|
||||
contentLoader.item.appLauncher.setCategory("All")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onCloseAllModalsExcept(excludedModal) {
|
||||
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {
|
||||
|
||||
@@ -13,9 +13,7 @@ Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height - y
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceLight
|
||||
border.color: Theme.outlineLight
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
|
||||
DankListView {
|
||||
id: resultsList
|
||||
@@ -75,9 +73,7 @@ Rectangle {
|
||||
width: ListView.view.width
|
||||
height: resultsList.itemHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
|
||||
border.width: ListView.isCurrentItem ? 2 : 1
|
||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
@@ -136,6 +132,7 @@ Rectangle {
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
visible: resultsList.showDescription && model.comment && model.comment.length > 0
|
||||
}
|
||||
}
|
||||
@@ -237,9 +234,7 @@ Rectangle {
|
||||
width: resultsGrid.cellWidth - resultsGrid.cellPadding
|
||||
height: resultsGrid.cellHeight - resultsGrid.cellPadding
|
||||
radius: Theme.cornerRadius
|
||||
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: resultsGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
|
||||
border.width: resultsGrid.currentIndex === index ? 2 : 1
|
||||
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -12,7 +12,6 @@ import qs.Widgets
|
||||
DankPopout {
|
||||
id: appDrawerPopout
|
||||
|
||||
property string triggerSection: "left"
|
||||
property var triggerScreen: null
|
||||
|
||||
// Setting to Exclusive, so virtual keyboards can send input to app drawer
|
||||
@@ -33,9 +32,9 @@ DankPopout {
|
||||
popupWidth: 520
|
||||
popupHeight: 600
|
||||
triggerX: Theme.spacingL
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
|
||||
triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
|
||||
triggerWidth: 40
|
||||
positioning: "center"
|
||||
positioning: ""
|
||||
screen: triggerScreen
|
||||
|
||||
onShouldBeVisibleChanged: {
|
||||
@@ -95,7 +94,7 @@ DankPopout {
|
||||
color: "transparent"
|
||||
radius: parent.radius + Math.abs(modelData.margin)
|
||||
border.color: modelData.color
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
z: modelData.z
|
||||
}
|
||||
}
|
||||
@@ -128,6 +127,44 @@ DankPopout {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectNext()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectPrevious()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectNext()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectPrevious()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
if (appLauncher.viewMode === "grid") {
|
||||
if (event.key === Qt.Key_L && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectNextInRow()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_H && event.modifiers & Qt.ControlModifier) {
|
||||
appLauncher.selectPreviousInRow()
|
||||
event.accepted = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!searchField.activeFocus && event.text && /[a-zA-Z0-9\s]/.test(event.text)) {
|
||||
searchField.forceActiveFocus()
|
||||
searchField.insertText(event.text)
|
||||
@@ -136,15 +173,16 @@ DankPopout {
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
height: parent.height - Theme.spacingL * 2
|
||||
x: Theme.spacingL
|
||||
y: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: parent.height - Theme.spacingS * 2
|
||||
x: Theme.spacingS
|
||||
y: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: 40
|
||||
leftPadding: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -170,7 +208,8 @@ DankPopout {
|
||||
DankTextField {
|
||||
id: searchField
|
||||
|
||||
width: parent.width
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 52
|
||||
cornerRadius: Theme.cornerRadius
|
||||
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.7)
|
||||
@@ -231,6 +270,8 @@ DankPopout {
|
||||
height: 40
|
||||
spacing: Theme.spacingM
|
||||
visible: searchField.text.length === 0
|
||||
leftPadding: Theme.spacingS
|
||||
topPadding: Theme.spacingXS
|
||||
|
||||
Item {
|
||||
width: 200
|
||||
@@ -238,6 +279,9 @@ DankPopout {
|
||||
|
||||
DankDropdown {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
text: ""
|
||||
currentValue: appLauncher.selectedCategory
|
||||
options: appLauncher.categories
|
||||
@@ -249,7 +293,7 @@ DankPopout {
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - 300
|
||||
width: parent.width - 310
|
||||
height: 1
|
||||
}
|
||||
|
||||
@@ -286,15 +330,13 @@ DankPopout {
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: {
|
||||
let usedHeight = 40 + Theme.spacingL
|
||||
usedHeight += 52 + Theme.spacingL
|
||||
usedHeight += (searchField.text.length === 0 ? 40 + Theme.spacingL : 0)
|
||||
let usedHeight = 40 + Theme.spacingS
|
||||
usedHeight += 52 + Theme.spacingS
|
||||
usedHeight += (searchField.text.length === 0 ? 40 : 0)
|
||||
return parent.height - usedHeight
|
||||
}
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
|
||||
DankListView {
|
||||
id: appList
|
||||
@@ -323,7 +365,9 @@ DankPopout {
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
visible: appLauncher.viewMode === "list"
|
||||
model: appLauncher.model
|
||||
currentIndex: appLauncher.selectedIndex
|
||||
@@ -353,9 +397,7 @@ DankPopout {
|
||||
width: ListView.view.width
|
||||
height: appList.itemHeight
|
||||
radius: Theme.cornerRadius
|
||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: ListView.isCurrentItem ? Theme.primarySelected : Theme.outlineMedium
|
||||
border.width: ListView.isCurrentItem ? 2 : 1
|
||||
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
@@ -371,6 +413,7 @@ DankPopout {
|
||||
id: listIconImg
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingXS
|
||||
source: Quickshell.iconPath(model.icon, true)
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
@@ -379,10 +422,13 @@ DankPopout {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingM
|
||||
visible: !listIconImg.visible
|
||||
color: Theme.surfaceLight
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Theme.primarySelected
|
||||
|
||||
StyledText {
|
||||
@@ -415,6 +461,7 @@ DankPopout {
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
visible: appList.showDescription && model.comment && model.comment.length > 0
|
||||
}
|
||||
}
|
||||
@@ -424,6 +471,9 @@ DankPopout {
|
||||
id: listMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingM
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
@@ -483,7 +533,9 @@ DankPopout {
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
visible: appLauncher.viewMode === "grid"
|
||||
model: appLauncher.model
|
||||
clip: true
|
||||
@@ -515,9 +567,7 @@ DankPopout {
|
||||
width: appGrid.cellWidth - appGrid.cellPadding
|
||||
height: appGrid.cellHeight - appGrid.cellPadding
|
||||
radius: Theme.cornerRadius
|
||||
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: appGrid.currentIndex === index ? Theme.primarySelected : Theme.outlineMedium
|
||||
border.width: appGrid.currentIndex === index ? 2 : 1
|
||||
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
@@ -534,6 +584,9 @@ DankPopout {
|
||||
id: gridIconImg
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
source: Quickshell.iconPath(model.icon, true)
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
@@ -542,10 +595,13 @@ DankPopout {
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
visible: !gridIconImg.visible
|
||||
color: Theme.surfaceLight
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Theme.primarySelected
|
||||
|
||||
StyledText {
|
||||
@@ -576,6 +632,9 @@ DankPopout {
|
||||
id: gridMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
@@ -652,7 +711,7 @@ DankPopout {
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.popupBackground()
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
z: 1000
|
||||
opacity: menuVisible ? 1 : 0
|
||||
scale: menuVisible ? 1 : 0.85
|
||||
|
||||
@@ -17,6 +17,7 @@ Item {
|
||||
property bool debounceSearch: true
|
||||
property int debounceInterval: 50
|
||||
property bool keyboardNavigationActive: false
|
||||
property bool suppressUpdatesWhileLaunching: false
|
||||
readonly property var categories: {
|
||||
const allCategories = AppSearchService.getAllCategories().filter(cat => cat !== "Education" && cat !== "Science")
|
||||
const result = ["All"]
|
||||
@@ -32,6 +33,10 @@ Item {
|
||||
signal viewModeSelected(string mode)
|
||||
|
||||
function updateFilteredModel() {
|
||||
if (suppressUpdatesWhileLaunching) {
|
||||
suppressUpdatesWhileLaunching = false
|
||||
return
|
||||
}
|
||||
filteredModel.clear()
|
||||
selectedIndex = 0
|
||||
keyboardNavigationActive = false
|
||||
@@ -125,7 +130,8 @@ Item {
|
||||
if (!appData) {
|
||||
return
|
||||
}
|
||||
appData.desktopEntry.execute()
|
||||
suppressUpdatesWhileLaunching = true
|
||||
SessionService.launchDesktopEntry(appData.desktopEntry)
|
||||
appLaunched(appData)
|
||||
AppUsageHistoryData.addAppUsage(appData.desktopEntry)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ Item {
|
||||
readonly property int maxCompactItems: 8
|
||||
readonly property int itemHeight: 36
|
||||
readonly property color selectedBorderColor: "transparent"
|
||||
readonly property color unselectedBorderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||
readonly property color unselectedBorderColor: "transparent"
|
||||
|
||||
function handleCategoryClick(category) {
|
||||
selectedCategory = category
|
||||
@@ -42,8 +42,7 @@ Item {
|
||||
height: root.itemHeight
|
||||
width: root.getButtonWidth(itemCount, parent.width)
|
||||
radius: Theme.cornerRadius
|
||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
||||
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
@@ -82,7 +81,7 @@ Item {
|
||||
height: root.itemHeight
|
||||
width: root.getButtonWidth(itemCount, parent.width)
|
||||
radius: Theme.cornerRadius
|
||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
||||
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
||||
|
||||
StyledText {
|
||||
@@ -118,7 +117,7 @@ Item {
|
||||
height: root.itemHeight
|
||||
width: root.getButtonWidth(itemCount, parent.width)
|
||||
radius: Theme.cornerRadius
|
||||
color: selectedCategory === modelData ? Theme.primary : "transparent"
|
||||
color: selectedCategory === modelData ? Theme.primary : Theme.surfaceContainerHigh
|
||||
border.color: selectedCategory === modelData ? selectedBorderColor : unselectedBorderColor
|
||||
|
||||
StyledText {
|
||||
|
||||
120
Modules/ControlCenter/Components/ActionTile.qml
Normal file
120
Modules/ControlCenter/Components/ActionTile.qml
Normal file
@@ -0,0 +1,120 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property string text: ""
|
||||
property string secondaryText: ""
|
||||
property bool isActive: false
|
||||
property bool enabled: true
|
||||
property int widgetIndex: 0
|
||||
property var widgetData: null
|
||||
property bool editMode: false
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: parent ? parent.width : 200
|
||||
height: 60
|
||||
radius: {
|
||||
if (Theme.cornerRadius === 0) return 0
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive:
|
||||
Theme.surfaceContainerHigh
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
|
||||
color: isActive ? _tileBgActive : _tileBgInactive
|
||||
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: isActive ? 1 : 1
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: mouseArea.containsMouse ? hoverTint(root.color) : "transparent"
|
||||
opacity: mouseArea.containsMouse ? 0.08 : 0.0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingL + 2
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? Theme.primaryContainer : Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - Theme.iconSize - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
Typography {
|
||||
width: parent.width
|
||||
text: root.text
|
||||
style: Typography.Style.Body
|
||||
color: isActive ? Theme.primaryContainer : Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
Typography {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
style: Typography.Style.Caption
|
||||
color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: root.enabled
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
97
Modules/ControlCenter/Components/DetailHost.qml
Normal file
97
Modules/ControlCenter/Components/DetailHost.qml
Normal file
@@ -0,0 +1,97 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.ControlCenter.Details
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string expandedSection: ""
|
||||
property var expandedWidgetData: null
|
||||
property var bluetoothCodecSelector: null
|
||||
|
||||
Loader {
|
||||
width: parent.width
|
||||
height: 250
|
||||
y: Theme.spacingS
|
||||
active: parent.height > 0
|
||||
property string sectionKey: root.expandedSection
|
||||
sourceComponent: {
|
||||
switch (root.expandedSection) {
|
||||
case "network":
|
||||
case "wifi": return networkDetailComponent
|
||||
case "bluetooth": return bluetoothDetailComponent
|
||||
case "audioOutput": return audioOutputDetailComponent
|
||||
case "audioInput": return audioInputDetailComponent
|
||||
case "battery": return batteryDetailComponent
|
||||
default:
|
||||
if (root.expandedSection.startsWith("diskUsage_")) {
|
||||
return diskUsageDetailComponent
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
onSectionKeyChanged: {
|
||||
active = false
|
||||
active = true
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: networkDetailComponent
|
||||
NetworkDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: bluetoothDetailComponent
|
||||
BluetoothDetail {
|
||||
id: bluetoothDetail
|
||||
onShowCodecSelector: function(device) {
|
||||
if (root.bluetoothCodecSelector) {
|
||||
root.bluetoothCodecSelector.show(device)
|
||||
root.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) {
|
||||
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioOutputDetailComponent
|
||||
AudioOutputDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioInputDetailComponent
|
||||
AudioInputDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryDetailComponent
|
||||
BatteryDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: diskUsageDetailComponent
|
||||
DiskUsageDetail {
|
||||
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
||||
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||
|
||||
|
||||
onMountPathChanged: (newMountPath) => {
|
||||
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
const newWidgets = widgets.map(w => {
|
||||
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||
const updatedWidget = Object.assign({}, w)
|
||||
updatedWidget.mountPath = newMountPath
|
||||
return updatedWidget
|
||||
}
|
||||
return w
|
||||
})
|
||||
SettingsData.setControlCenterWidgets(newWidgets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Modules/ControlCenter/Components/DragDropDetailHost.qml
Normal file
91
Modules/ControlCenter/Components/DragDropDetailHost.qml
Normal file
@@ -0,0 +1,91 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Modules.ControlCenter.Details
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string expandedSection: ""
|
||||
property var expandedWidgetData: null
|
||||
|
||||
height: active ? 250 : 0
|
||||
visible: active
|
||||
|
||||
readonly property bool active: expandedSection !== ""
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Theme.spacingS
|
||||
sourceComponent: {
|
||||
if (!root.active) return null
|
||||
|
||||
if (expandedSection.startsWith("diskUsage_")) {
|
||||
return diskUsageDetailComponent
|
||||
}
|
||||
|
||||
switch (expandedSection) {
|
||||
case "wifi": return networkDetailComponent
|
||||
case "bluetooth": return bluetoothDetailComponent
|
||||
case "audioOutput": return audioOutputDetailComponent
|
||||
case "audioInput": return audioInputDetailComponent
|
||||
case "battery": return batteryDetailComponent
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: networkDetailComponent
|
||||
NetworkDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: bluetoothDetailComponent
|
||||
BluetoothDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioOutputDetailComponent
|
||||
AudioOutputDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioInputDetailComponent
|
||||
AudioInputDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryDetailComponent
|
||||
BatteryDetail {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: diskUsageDetailComponent
|
||||
DiskUsageDetail {
|
||||
currentMountPath: root.expandedWidgetData?.mountPath || "/"
|
||||
instanceId: root.expandedWidgetData?.instanceId || ""
|
||||
|
||||
onMountPathChanged: (newMountPath) => {
|
||||
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
const newWidgets = widgets.map(w => {
|
||||
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
|
||||
const updatedWidget = Object.assign({}, w)
|
||||
updatedWidget.mountPath = newMountPath
|
||||
return updatedWidget
|
||||
}
|
||||
return w
|
||||
})
|
||||
SettingsData.setControlCenterWidgets(newWidgets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
718
Modules/ControlCenter/Components/DragDropGrid.qml
Normal file
718
Modules/ControlCenter/Components/DragDropGrid.qml
Normal file
@@ -0,0 +1,718 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
import qs.Modules.ControlCenter.Components
|
||||
import "../utils/layout.js" as LayoutUtils
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property bool editMode: false
|
||||
property string expandedSection: ""
|
||||
property int expandedWidgetIndex: -1
|
||||
property var model: null
|
||||
property var expandedWidgetData: null
|
||||
property var bluetoothCodecSelector: null
|
||||
property bool darkModeTransitionPending: false
|
||||
|
||||
signal expandClicked(var widgetData, int globalIndex)
|
||||
signal removeWidget(int index)
|
||||
signal moveWidget(int fromIndex, int toIndex)
|
||||
signal toggleWidgetSize(int index)
|
||||
|
||||
spacing: editMode ? Theme.spacingL : Theme.spacingS
|
||||
|
||||
property var currentRowWidgets: []
|
||||
property real currentRowWidth: 0
|
||||
property int expandedRowIndex: -1
|
||||
property var colorPickerModal: null
|
||||
|
||||
function calculateRowsAndWidgets() {
|
||||
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
|
||||
}
|
||||
|
||||
property var layoutResult: {
|
||||
const dummy = [expandedSection, expandedWidgetIndex, model?.controlCenterWidgets]
|
||||
return calculateRowsAndWidgets()
|
||||
}
|
||||
|
||||
onLayoutResultChanged: {
|
||||
expandedRowIndex = layoutResult.expandedRowIndex
|
||||
}
|
||||
|
||||
function moveToTop(item) {
|
||||
const children = root.children
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (children[i] === item)
|
||||
continue
|
||||
if (children[i].z)
|
||||
children[i].z = Math.min(children[i].z, 999)
|
||||
}
|
||||
item.z = 1000
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.layoutResult.rows
|
||||
|
||||
Column {
|
||||
width: root.width
|
||||
spacing: 0
|
||||
property int rowIndex: index
|
||||
property var rowWidgets: modelData
|
||||
property bool isSliderOnlyRow: {
|
||||
const widgets = rowWidgets || []
|
||||
if (widgets.length === 0) return false
|
||||
return widgets.every(w => w.id === "volumeSlider" || w.id === "brightnessSlider" || w.id === "inputVolumeSlider")
|
||||
}
|
||||
topPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0
|
||||
bottomPadding: isSliderOnlyRow ? (root.editMode ? 4 : -6) : 0
|
||||
|
||||
Flow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: rowWidgets || []
|
||||
|
||||
DragDropWidgetWrapper {
|
||||
widgetData: modelData
|
||||
property int globalWidgetIndex: {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
if (widgets[i].id === modelData.id) {
|
||||
if (modelData.id === "diskUsage") {
|
||||
if (widgets[i].instanceId === modelData.instanceId) {
|
||||
return i
|
||||
}
|
||||
} else {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
property int widgetWidth: modelData.width || 50
|
||||
width: {
|
||||
const baseWidth = root.width
|
||||
const spacing = Theme.spacingS
|
||||
if (widgetWidth <= 25) {
|
||||
return (baseWidth - spacing * 3) / 4
|
||||
} else if (widgetWidth <= 50) {
|
||||
return (baseWidth - spacing) / 2
|
||||
} else if (widgetWidth <= 75) {
|
||||
return (baseWidth - spacing * 2) * 0.75
|
||||
} else {
|
||||
return baseWidth
|
||||
}
|
||||
}
|
||||
height: isSliderOnlyRow ? 48 : 60
|
||||
|
||||
editMode: root.editMode
|
||||
widgetIndex: globalWidgetIndex
|
||||
gridCellWidth: width
|
||||
gridCellHeight: height
|
||||
gridColumns: 4
|
||||
gridLayout: root
|
||||
isSlider: {
|
||||
const id = modelData.id || ""
|
||||
return id === "volumeSlider" || id === "brightnessSlider" || id === "inputVolumeSlider"
|
||||
}
|
||||
|
||||
widgetComponent: {
|
||||
const id = modelData.id || ""
|
||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
||||
return compoundPillComponent
|
||||
} else if (id === "volumeSlider") {
|
||||
return audioSliderComponent
|
||||
} else if (id === "brightnessSlider") {
|
||||
return brightnessSliderComponent
|
||||
} else if (id === "inputVolumeSlider") {
|
||||
return inputAudioSliderComponent
|
||||
} else if (id === "battery") {
|
||||
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
|
||||
} else if (id === "diskUsage") {
|
||||
return diskUsagePillComponent
|
||||
} else if (id === "colorPicker") {
|
||||
return colorPickerPillComponent
|
||||
} else {
|
||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
|
||||
}
|
||||
}
|
||||
|
||||
onWidgetMoved: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
|
||||
onRemoveWidget: index => root.removeWidget(index)
|
||||
onToggleWidgetSize: index => root.toggleWidgetSize(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DetailHost {
|
||||
width: parent.width
|
||||
height: active ? (250 + Theme.spacingS) : 0
|
||||
property bool active: {
|
||||
if (root.expandedSection === "") return false
|
||||
|
||||
if (root.expandedSection.startsWith("diskUsage_") && root.expandedWidgetData) {
|
||||
const expandedInstanceId = root.expandedWidgetData.instanceId
|
||||
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
|
||||
}
|
||||
|
||||
return rowIndex === root.expandedRowIndex
|
||||
}
|
||||
visible: active
|
||||
expandedSection: root.expandedSection
|
||||
expandedWidgetData: root.expandedWidgetData
|
||||
bluetoothCodecSelector: root.bluetoothCodecSelector
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: compoundPillComponent
|
||||
CompoundPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 60
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return "sync"
|
||||
if (NetworkService.networkStatus === "ethernet")
|
||||
return "settings_ethernet"
|
||||
if (NetworkService.networkStatus === "wifi")
|
||||
return NetworkService.wifiSignalIcon
|
||||
if (NetworkService.wifiEnabled)
|
||||
return "wifi_off"
|
||||
return "wifi_off"
|
||||
}
|
||||
case "bluetooth":
|
||||
{
|
||||
if (!BluetoothService.available)
|
||||
return "bluetooth_disabled"
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||
return "bluetooth_disabled"
|
||||
const primaryDevice = (() => {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return null
|
||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
||||
for (let device of devices) {
|
||||
if (device && device.connected)
|
||||
return device
|
||||
}
|
||||
return null
|
||||
})()
|
||||
if (primaryDevice)
|
||||
return BluetoothService.getDeviceIcon(primaryDevice)
|
||||
return "bluetooth"
|
||||
}
|
||||
case "audioOutput":
|
||||
{
|
||||
if (!AudioService.sink)
|
||||
return "volume_off"
|
||||
let volume = AudioService.sink.audio.volume
|
||||
let muted = AudioService.sink.audio.muted
|
||||
if (muted || volume === 0.0)
|
||||
return "volume_off"
|
||||
if (volume <= 0.33)
|
||||
return "volume_down"
|
||||
if (volume <= 0.66)
|
||||
return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
case "audioInput":
|
||||
{
|
||||
if (!AudioService.source)
|
||||
return "mic_off"
|
||||
let muted = AudioService.source.audio.muted
|
||||
return muted ? "mic_off" : "mic"
|
||||
}
|
||||
default:
|
||||
return widgetDef?.icon || "help"
|
||||
}
|
||||
}
|
||||
primaryText: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return NetworkService.wifiEnabled ? "Disabling WiFi..." : "Enabling WiFi..."
|
||||
if (NetworkService.networkStatus === "ethernet")
|
||||
return "Ethernet"
|
||||
if (NetworkService.networkStatus === "wifi" && NetworkService.currentWifiSSID)
|
||||
return NetworkService.currentWifiSSID
|
||||
if (NetworkService.wifiEnabled)
|
||||
return "Not connected"
|
||||
return "WiFi off"
|
||||
}
|
||||
case "bluetooth":
|
||||
{
|
||||
if (!BluetoothService.available)
|
||||
return "Bluetooth"
|
||||
if (!BluetoothService.adapter)
|
||||
return "No adapter"
|
||||
if (!BluetoothService.adapter.enabled)
|
||||
return "Disabled"
|
||||
return "Enabled"
|
||||
}
|
||||
case "audioOutput":
|
||||
return AudioService.sink?.description || "No output device"
|
||||
case "audioInput":
|
||||
return AudioService.source?.description || "No input device"
|
||||
default:
|
||||
return widgetDef?.text || "Unknown"
|
||||
}
|
||||
}
|
||||
secondaryText: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return "Please wait..."
|
||||
if (NetworkService.networkStatus === "ethernet")
|
||||
return "Connected"
|
||||
if (NetworkService.networkStatus === "wifi")
|
||||
return NetworkService.wifiSignalStrength > 0 ? NetworkService.wifiSignalStrength + "%" : "Connected"
|
||||
if (NetworkService.wifiEnabled)
|
||||
return "Select network"
|
||||
return ""
|
||||
}
|
||||
case "bluetooth":
|
||||
{
|
||||
if (!BluetoothService.available)
|
||||
return "No adapters"
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||
return "Off"
|
||||
const primaryDevice = (() => {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return null
|
||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
||||
for (let device of devices) {
|
||||
if (device && device.connected)
|
||||
return device
|
||||
}
|
||||
return null
|
||||
})()
|
||||
if (primaryDevice)
|
||||
return primaryDevice.name || primaryDevice.alias || primaryDevice.deviceName || "Connected Device"
|
||||
return "No devices"
|
||||
}
|
||||
case "audioOutput":
|
||||
{
|
||||
if (!AudioService.sink)
|
||||
return "Select device"
|
||||
if (AudioService.sink.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.sink.audio.volume * 100) + "%"
|
||||
}
|
||||
case "audioInput":
|
||||
{
|
||||
if (!AudioService.source)
|
||||
return "Select device"
|
||||
if (AudioService.source.audio.muted)
|
||||
return "Muted"
|
||||
return Math.round(AudioService.source.audio.volume * 100) + "%"
|
||||
}
|
||||
default:
|
||||
return widgetDef?.description || ""
|
||||
}
|
||||
}
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return false
|
||||
if (NetworkService.networkStatus === "ethernet")
|
||||
return true
|
||||
if (NetworkService.networkStatus === "wifi")
|
||||
return true
|
||||
return NetworkService.wifiEnabled
|
||||
}
|
||||
case "bluetooth":
|
||||
return !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
|
||||
case "audioOutput":
|
||||
return !!(AudioService.sink && !AudioService.sink.audio.muted)
|
||||
case "audioInput":
|
||||
return !!(AudioService.source && !AudioService.source.audio.muted)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
enabled: widgetDef?.enabled ?? true
|
||||
onToggled: {
|
||||
if (root.editMode) return
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
}
|
||||
break
|
||||
}
|
||||
case "bluetooth":
|
||||
{
|
||||
if (BluetoothService.available && BluetoothService.adapter) {
|
||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
||||
}
|
||||
break
|
||||
}
|
||||
case "audioOutput":
|
||||
{
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
|
||||
}
|
||||
break
|
||||
}
|
||||
case "audioInput":
|
||||
{
|
||||
if (AudioService.source && AudioService.source.audio) {
|
||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
onExpandClicked: {
|
||||
if (root.editMode) return
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (root.editMode) return
|
||||
const id = widgetData.id || ""
|
||||
if (id === "audioOutput") {
|
||||
if (!AudioService.sink || !AudioService.sink.audio)
|
||||
return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = AudioService.sink.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
AudioService.sink.audio.muted = false
|
||||
AudioService.sink.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
} else if (id === "audioInput") {
|
||||
if (!AudioService.source || !AudioService.source.audio)
|
||||
return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = AudioService.source.audio.volume * 100
|
||||
let newVolume
|
||||
if (delta > 0)
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
else
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
AudioService.source.audio.muted = false
|
||||
AudioService.source.audio.volume = newVolume / 100
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
AudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: brightnessSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
BrightnessSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: inputAudioSliderComponent
|
||||
Item {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 16
|
||||
|
||||
InputAudioSliderRow {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 14
|
||||
property color sliderTrackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryPillComponent
|
||||
BatteryPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: smallBatteryComponent
|
||||
SmallBatteryButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
onClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: toggleButtonComponent
|
||||
ToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
case "darkMode":
|
||||
return "contrast"
|
||||
case "doNotDisturb":
|
||||
return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default:
|
||||
return "help"
|
||||
}
|
||||
}
|
||||
|
||||
text: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
return "Night Mode"
|
||||
case "darkMode":
|
||||
return SessionData.isLightMode ? "Light Mode" : "Dark Mode"
|
||||
case "doNotDisturb":
|
||||
return "Do Not Disturb"
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "Keeping Awake" : "Keep Awake"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
iconRotation: {
|
||||
if (widgetData.id !== "darkMode") return 0
|
||||
if (darkModeTransitionPending) {
|
||||
return SessionData.isLightMode ? 0 : 180
|
||||
}
|
||||
return SessionData.isLightMode ? 180 : 0
|
||||
}
|
||||
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
return DisplayService.nightModeEnabled || false
|
||||
case "darkMode":
|
||||
return !SessionData.isLightMode
|
||||
case "doNotDisturb":
|
||||
return SessionData.doNotDisturb || false
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited || false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enabled: !root.editMode
|
||||
|
||||
onIconRotationCompleted: {
|
||||
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
|
||||
root.darkModeTransitionPending = false
|
||||
Theme.screenTransition()
|
||||
Theme.toggleLightMode()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.editMode)
|
||||
return
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
{
|
||||
if (DisplayService.automationAvailable)
|
||||
DisplayService.toggleNightMode()
|
||||
break
|
||||
}
|
||||
case "darkMode":
|
||||
{
|
||||
root.darkModeTransitionPending = true
|
||||
break
|
||||
}
|
||||
case "doNotDisturb":
|
||||
{
|
||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
||||
break
|
||||
}
|
||||
case "idleInhibitor":
|
||||
{
|
||||
SessionService.toggleIdleInhibit()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: smallToggleComponent
|
||||
SmallToggleButton {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
case "darkMode":
|
||||
return "contrast"
|
||||
case "doNotDisturb":
|
||||
return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
|
||||
default:
|
||||
return "help"
|
||||
}
|
||||
}
|
||||
|
||||
iconRotation: {
|
||||
if (widgetData.id !== "darkMode") return 0
|
||||
if (darkModeTransitionPending) {
|
||||
return SessionData.isLightMode ? 0 : 180
|
||||
}
|
||||
return SessionData.isLightMode ? 180 : 0
|
||||
}
|
||||
|
||||
isActive: {
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
return DisplayService.nightModeEnabled || false
|
||||
case "darkMode":
|
||||
return !SessionData.isLightMode
|
||||
case "doNotDisturb":
|
||||
return SessionData.doNotDisturb || false
|
||||
case "idleInhibitor":
|
||||
return SessionService.idleInhibited || false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enabled: !root.editMode
|
||||
|
||||
onIconRotationCompleted: {
|
||||
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
|
||||
root.darkModeTransitionPending = false
|
||||
Theme.screenTransition()
|
||||
Theme.toggleLightMode()
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.editMode)
|
||||
return
|
||||
switch (widgetData.id || "") {
|
||||
case "nightMode":
|
||||
{
|
||||
if (DisplayService.automationAvailable)
|
||||
DisplayService.toggleNightMode()
|
||||
break
|
||||
}
|
||||
case "darkMode":
|
||||
{
|
||||
root.darkModeTransitionPending = true
|
||||
break
|
||||
}
|
||||
case "doNotDisturb":
|
||||
{
|
||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
||||
break
|
||||
}
|
||||
case "idleInhibitor":
|
||||
{
|
||||
SessionService.toggleIdleInhibit()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: diskUsagePillComponent
|
||||
DiskUsagePill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
mountPath: widgetData.mountPath || "/"
|
||||
instanceId: widgetData.instanceId || ""
|
||||
|
||||
onExpandClicked: {
|
||||
if (!root.editMode) {
|
||||
root.expandClicked(widgetData, widgetIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: colorPickerPillComponent
|
||||
ColorPickerPill {
|
||||
property var widgetData: parent.widgetData || {}
|
||||
property int widgetIndex: parent.widgetIndex || 0
|
||||
width: parent.width
|
||||
height: 60
|
||||
|
||||
colorPickerModal: root.colorPickerModal
|
||||
}
|
||||
}
|
||||
}
|
||||
289
Modules/ControlCenter/Components/DragDropWidgetWrapper.qml
Normal file
289
Modules/ControlCenter/Components/DragDropWidgetWrapper.qml
Normal file
@@ -0,0 +1,289 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool editMode: false
|
||||
property var widgetData: null
|
||||
property int widgetIndex: -1
|
||||
property bool isSlider: false
|
||||
property Component widgetComponent: null
|
||||
property real gridCellWidth: 100
|
||||
property real gridCellHeight: 60
|
||||
property int gridColumns: 4
|
||||
property var gridLayout: null
|
||||
|
||||
z: dragArea.drag.active ? 10000 : 1
|
||||
|
||||
signal widgetMoved(int fromIndex, int toIndex)
|
||||
signal removeWidget(int index)
|
||||
signal toggleWidgetSize(int index)
|
||||
|
||||
width: {
|
||||
const widgetWidth = widgetData?.width || 50
|
||||
if (widgetWidth <= 25) return gridCellWidth
|
||||
else if (widgetWidth <= 50) return gridCellWidth * 2
|
||||
else if (widgetWidth <= 75) return gridCellWidth * 3
|
||||
else return gridCellWidth * 4
|
||||
}
|
||||
height: isSlider ? 16 : gridCellHeight
|
||||
|
||||
Rectangle {
|
||||
id: dragIndicator
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: Theme.primary
|
||||
border.width: dragArea.drag.active ? 2 : 0
|
||||
radius: Theme.cornerRadius
|
||||
opacity: dragArea.drag.active ? 0.8 : 1.0
|
||||
z: dragArea.drag.active ? 10000 : 1
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation { duration: 150 }
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 150 }
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: widgetLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: widgetComponent
|
||||
property var widgetData: root.widgetData
|
||||
property int widgetIndex: root.widgetIndex
|
||||
property int globalWidgetIndex: root.widgetIndex
|
||||
property int widgetWidth: root.widgetData?.width || 50
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: editModeBlocker
|
||||
anchors.fill: parent
|
||||
enabled: root.editMode
|
||||
acceptedButtons: Qt.AllButtons
|
||||
onPressed: function(mouse) { mouse.accepted = true }
|
||||
onWheel: function(wheel) { wheel.accepted = true }
|
||||
z: 100
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
enabled: editMode
|
||||
cursorShape: editMode ? Qt.OpenHandCursor : Qt.PointingHandCursor
|
||||
drag.target: editMode ? root : null
|
||||
drag.axis: Drag.XAndYAxis
|
||||
drag.smoothed: true
|
||||
|
||||
onPressed: function(mouse) {
|
||||
if (editMode) {
|
||||
cursorShape = Qt.ClosedHandCursor
|
||||
if (root.gridLayout && root.gridLayout.moveToTop) {
|
||||
root.gridLayout.moveToTop(root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: function(mouse) {
|
||||
if (editMode) {
|
||||
cursorShape = Qt.OpenHandCursor
|
||||
root.snapToGrid()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Drag.active: dragArea.drag.active
|
||||
Drag.hotSpot.x: width / 2
|
||||
Drag.hotSpot.y: height / 2
|
||||
|
||||
function swapIndices(i, j) {
|
||||
if (i === j) return;
|
||||
const arr = SettingsData.controlCenterWidgets;
|
||||
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
|
||||
|
||||
const copy = arr.slice();
|
||||
const tmp = copy[i];
|
||||
copy[i] = copy[j];
|
||||
copy[j] = tmp;
|
||||
|
||||
SettingsData.setControlCenterWidgets(copy);
|
||||
}
|
||||
|
||||
function snapToGrid() {
|
||||
if (!editMode || !gridLayout) return
|
||||
|
||||
const globalPos = root.mapToItem(gridLayout, 0, 0)
|
||||
const cellWidth = gridLayout.width / gridColumns
|
||||
const cellHeight = gridCellHeight + Theme.spacingS
|
||||
|
||||
const centerX = globalPos.x + (root.width / 2)
|
||||
const centerY = globalPos.y + (root.height / 2)
|
||||
|
||||
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
|
||||
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
|
||||
|
||||
targetCol = Math.min(targetCol, gridColumns - 1)
|
||||
|
||||
const newIndex = findBestInsertionIndex(targetRow, targetCol)
|
||||
|
||||
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
|
||||
swapIndices(widgetIndex, newIndex)
|
||||
}
|
||||
}
|
||||
|
||||
function findBestInsertionIndex(targetRow, targetCol) {
|
||||
const widgets = SettingsData.controlCenterWidgets || [];
|
||||
const n = widgets.length;
|
||||
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
|
||||
|
||||
function spanFor(width) {
|
||||
const w = width ?? 50;
|
||||
if (w <= 25) return 1;
|
||||
if (w <= 50) return 2;
|
||||
if (w <= 75) return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
const cols = gridColumns || 4;
|
||||
|
||||
let row = 0, col = 0;
|
||||
let draggedOrigKey = null;
|
||||
|
||||
const pos = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const span = Math.min(spanFor(widgets[i].width), cols);
|
||||
|
||||
if (col + span > cols) {
|
||||
row++;
|
||||
col = 0;
|
||||
}
|
||||
|
||||
const startCol = col;
|
||||
const centerKey = row * cols + (startCol + (span - 1) / 2);
|
||||
|
||||
if (i === widgetIndex) {
|
||||
draggedOrigKey = centerKey;
|
||||
} else {
|
||||
pos.push({ index: i, row, startCol, span, centerKey });
|
||||
}
|
||||
|
||||
col += span;
|
||||
if (col >= cols) {
|
||||
row++;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.length === 0) return -1;
|
||||
|
||||
const centerColCoord = targetCol + 0.5;
|
||||
const targetKey = targetRow * cols + centerColCoord;
|
||||
|
||||
for (let k = 0; k < pos.length; k++) {
|
||||
const p = pos[k];
|
||||
if (p.row === targetRow && centerColCoord >= p.startCol && centerColCoord < (p.startCol + p.span)) {
|
||||
return p.index;
|
||||
}
|
||||
}
|
||||
|
||||
let lo = 0, hi = pos.length - 1;
|
||||
if (targetKey <= pos[0].centerKey) return pos[0].index;
|
||||
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
|
||||
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
const mk = pos[mid].centerKey;
|
||||
if (targetKey < mk) hi = mid - 1;
|
||||
else if (targetKey > mk) lo = mid + 1;
|
||||
else return pos[mid].index;
|
||||
}
|
||||
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
|
||||
return (movingUp ? pos[lo].index : pos[hi].index);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 16
|
||||
height: 16
|
||||
radius: 8
|
||||
color: Theme.error
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: -4
|
||||
visible: editMode
|
||||
z: 10
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "close"
|
||||
size: 12
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: removeWidget(widgetIndex)
|
||||
}
|
||||
}
|
||||
|
||||
SizeControls {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.margins: -6
|
||||
visible: editMode
|
||||
z: 10
|
||||
currentSize: root.widgetData?.width || 50
|
||||
isSlider: root.isSlider
|
||||
widgetIndex: root.widgetIndex
|
||||
onSizeChanged: (newSize) => {
|
||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
||||
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
|
||||
widgets[widgetIndex].width = newSize
|
||||
SettingsData.setControlCenterWidgets(widgets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dragHandle
|
||||
width: 16
|
||||
height: 12
|
||||
radius: 2
|
||||
color: Theme.primary
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: 4
|
||||
visible: editMode
|
||||
z: 15
|
||||
opacity: dragArea.drag.active ? 1.0 : 0.7
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "drag_indicator"
|
||||
size: 10
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 150 }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: editMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||
radius: Theme.cornerRadius
|
||||
border.color: "transparent"
|
||||
border.width: 0
|
||||
z: -1
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Modules/ControlCenter/Components/EditControls.qml
Normal file
236
Modules/ControlCenter/Components/EditControls.qml
Normal file
@@ -0,0 +1,236 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property var availableWidgets: []
|
||||
|
||||
signal addWidget(string widgetId)
|
||||
signal resetToDefault()
|
||||
signal clearAll()
|
||||
|
||||
height: 48
|
||||
spacing: Theme.spacingS
|
||||
|
||||
onAddWidget: addWidgetPopup.close()
|
||||
|
||||
Popup {
|
||||
id: addWidgetPopup
|
||||
anchors.centerIn: parent
|
||||
width: 400
|
||||
height: 300
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.primarySelected
|
||||
border.width: 0
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "add_circle"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: "Add Widget"
|
||||
style: Typography.Style.Subtitle
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
DankListView {
|
||||
anchors.top: headerRow.bottom
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
spacing: Theme.spacingS
|
||||
model: root.availableWidgets
|
||||
|
||||
delegate: Rectangle {
|
||||
width: 400 - Theme.spacingL * 2
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: widgetMouseArea.containsMouse ? Theme.primaryHover : Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: modelData.icon
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
width: 400 - Theme.spacingL * 2 - Theme.iconSize - Theme.spacingM * 3 - Theme.iconSize
|
||||
|
||||
Typography {
|
||||
text: modelData.text
|
||||
style: Typography.Style.Body
|
||||
color: Theme.surfaceText
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: modelData.description
|
||||
style: Typography.Style.Caption
|
||||
color: Theme.outline
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "add"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: widgetMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.addWidget(modelData.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingS * 2) / 3
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
border.color: Theme.primary
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "add"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: "Add Widget"
|
||||
style: Typography.Style.Button
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: addWidgetPopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingS * 2) / 3
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
||||
border.color: Theme.warning
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "settings_backup_restore"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.warning
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: "Defaults"
|
||||
style: Typography.Style.Button
|
||||
color: Theme.warning
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.resetToDefault()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingS * 2) / 3
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
border.color: Theme.error
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "clear_all"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: "Reset"
|
||||
style: Typography.Style.Button
|
||||
color: Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.clearAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Modules/ControlCenter/Components/HeaderPane.qml
Normal file
115
Modules/ControlCenter/Components/HeaderPane.qml
Normal file
@@ -0,0 +1,115 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool powerOptionsExpanded: false
|
||||
property bool editMode: false
|
||||
|
||||
signal powerActionRequested(string action, string title, string message)
|
||||
signal lockRequested()
|
||||
signal editModeToggled()
|
||||
|
||||
implicitHeight: 70
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankCircularImage {
|
||||
id: avatarContainer
|
||||
|
||||
width: 60
|
||||
height: 60
|
||||
imageSource: {
|
||||
if (PortalService.profileImage === "")
|
||||
return ""
|
||||
|
||||
if (PortalService.profileImage.startsWith("/"))
|
||||
return "file://" + PortalService.profileImage
|
||||
|
||||
return PortalService.profileImage
|
||||
}
|
||||
fallbackIcon: "person"
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
Typography {
|
||||
text: UserInfoService.fullName
|
||||
|| UserInfoService.username || "User"
|
||||
style: Typography.Style.Subtitle
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: (UserInfoService.uptime || "Unknown")
|
||||
style: Typography.Style.Caption
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: actionButtonsRow
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: Theme.spacingXS
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 36
|
||||
iconName: "lock"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: {
|
||||
root.lockRequested()
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 36
|
||||
iconName: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: root.powerOptionsExpanded ? Theme.primary : Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: {
|
||||
root.powerOptionsExpanded = !root.powerOptionsExpanded
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 36
|
||||
iconName: "settings"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: {
|
||||
settingsModal.show()
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 36
|
||||
iconName: editMode ? "done" : "edit"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: editMode ? Theme.primary : Theme.surfaceText
|
||||
backgroundColor: "transparent"
|
||||
onClicked: root.editModeToggled()
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Modules/ControlCenter/Components/PowerButton.qml
Normal file
52
Modules/ControlCenter/Components/PowerButton.qml
Normal file
@@ -0,0 +1,52 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property string text: ""
|
||||
|
||||
signal pressed()
|
||||
|
||||
height: 34
|
||||
radius: Theme.cornerRadius
|
||||
color: mouseArea.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)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.fontSizeSmall
|
||||
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Typography {
|
||||
text: root.text
|
||||
style: Typography.Style.Button
|
||||
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: root.pressed()
|
||||
}
|
||||
}
|
||||
70
Modules/ControlCenter/Components/PowerOptionsPane.qml
Normal file
70
Modules/ControlCenter/Components/PowerOptionsPane.qml
Normal file
@@ -0,0 +1,70 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool expanded: false
|
||||
|
||||
signal powerActionRequested(string action, string title, string message)
|
||||
|
||||
implicitHeight: expanded ? 60 : 0
|
||||
height: implicitHeight
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: root.expanded ? 1 : 0
|
||||
opacity: root.expanded ? 1 : 0
|
||||
clip: true
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: SessionService.hibernateSupported ? Theme.spacingS : Theme.spacingL
|
||||
visible: root.expanded
|
||||
|
||||
PowerButton {
|
||||
width: SessionService.hibernateSupported ? 85 : 100
|
||||
iconName: "logout"
|
||||
text: "Logout"
|
||||
onPressed: root.powerActionRequested("logout", "Logout", "Are you sure you want to logout?")
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
width: SessionService.hibernateSupported ? 85 : 100
|
||||
iconName: "restart_alt"
|
||||
text: "Restart"
|
||||
onPressed: root.powerActionRequested("reboot", "Restart", "Are you sure you want to restart?")
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
width: SessionService.hibernateSupported ? 85 : 100
|
||||
iconName: "bedtime"
|
||||
text: "Suspend"
|
||||
onPressed: root.powerActionRequested("suspend", "Suspend", "Are you sure you want to suspend?")
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
width: SessionService.hibernateSupported ? 85 : 100
|
||||
iconName: "ac_unit"
|
||||
text: "Hibernate"
|
||||
visible: SessionService.hibernateSupported
|
||||
onPressed: root.powerActionRequested("hibernate", "Hibernate", "Are you sure you want to hibernate?")
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
width: SessionService.hibernateSupported ? 85 : 100
|
||||
iconName: "power_settings_new"
|
||||
text: "Shutdown"
|
||||
onPressed: root.powerActionRequested("poweroff", "Shutdown", "Are you sure you want to shutdown?")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Modules/ControlCenter/Components/SizeControls.qml
Normal file
52
Modules/ControlCenter/Components/SizeControls.qml
Normal file
@@ -0,0 +1,52 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property int currentSize: 50
|
||||
property bool isSlider: false
|
||||
property int widgetIndex: -1
|
||||
|
||||
signal sizeChanged(int newSize)
|
||||
|
||||
readonly property var availableSizes: isSlider ? [50, 100] : [25, 50, 75, 100]
|
||||
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
model: root.availableSizes
|
||||
|
||||
Rectangle {
|
||||
width: 16
|
||||
height: 16
|
||||
radius: 3
|
||||
color: modelData === root.currentSize ? Theme.primary : Theme.surfaceContainer
|
||||
border.color: modelData === root.currentSize ? Theme.primary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: modelData.toString()
|
||||
font.pixelSize: 8
|
||||
font.weight: Font.Medium
|
||||
color: modelData === root.currentSize ? Theme.primaryContainer : Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.currentSize = modelData
|
||||
root.sizeChanged(modelData)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Modules/ControlCenter/Components/Typography.qml
Normal file
46
Modules/ControlCenter/Components/Typography.qml
Normal file
@@ -0,0 +1,46 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
StyledText {
|
||||
id: root
|
||||
|
||||
enum Style {
|
||||
Title,
|
||||
Subtitle,
|
||||
Body,
|
||||
Caption,
|
||||
Button
|
||||
}
|
||||
|
||||
property int style: Typography.Style.Body
|
||||
|
||||
font.pixelSize: {
|
||||
switch (style) {
|
||||
case Typography.Style.Title: return Theme.fontSizeXLarge
|
||||
case Typography.Style.Subtitle: return Theme.fontSizeLarge
|
||||
case Typography.Style.Body: return Theme.fontSizeMedium
|
||||
case Typography.Style.Caption: return Theme.fontSizeSmall
|
||||
case Typography.Style.Button: return Theme.fontSizeSmall
|
||||
default: return Theme.fontSizeMedium
|
||||
}
|
||||
}
|
||||
|
||||
font.weight: {
|
||||
switch (style) {
|
||||
case Typography.Style.Title: return Font.Bold
|
||||
case Typography.Style.Subtitle: return Font.Medium
|
||||
case Typography.Style.Body: return Font.Normal
|
||||
case Typography.Style.Caption: return Font.Normal
|
||||
case Typography.Style.Button: return Font.Medium
|
||||
default: return Font.Normal
|
||||
}
|
||||
}
|
||||
|
||||
color: {
|
||||
switch (style) {
|
||||
case Typography.Style.Caption: return Theme.surfaceVariantText
|
||||
default: return Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,52 +10,64 @@ import qs.Common
|
||||
import qs.Modules.ControlCenter
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
import qs.Modules.ControlCenter.Details
|
||||
import qs.Modules.ControlCenter.Details 1.0 as Details
|
||||
import qs.Modules.DankBar
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Components
|
||||
import qs.Modules.ControlCenter.Models
|
||||
import "./utils/state.js" as StateUtils
|
||||
|
||||
DankPopout {
|
||||
id: root
|
||||
|
||||
property string expandedSection: ""
|
||||
property bool powerOptionsExpanded: false
|
||||
property string triggerSection: "right"
|
||||
property var triggerScreen: null
|
||||
|
||||
function setTriggerPosition(x, y, width, section, screen) {
|
||||
triggerX = x
|
||||
triggerY = y
|
||||
triggerWidth = width
|
||||
triggerSection = section
|
||||
triggerScreen = screen
|
||||
}
|
||||
|
||||
function openWithSection(section) {
|
||||
if (shouldBeVisible) {
|
||||
close()
|
||||
} else {
|
||||
expandedSection = section
|
||||
open()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSection(section) {
|
||||
if (expandedSection === section) {
|
||||
expandedSection = ""
|
||||
} else {
|
||||
expandedSection = section
|
||||
}
|
||||
}
|
||||
property bool editMode: false
|
||||
property int expandedWidgetIndex: -1
|
||||
property var expandedWidgetData: null
|
||||
|
||||
signal powerActionRequested(string action, string title, string message)
|
||||
signal lockRequested
|
||||
|
||||
function collapseAll() {
|
||||
expandedSection = ""
|
||||
expandedWidgetIndex = -1
|
||||
expandedWidgetData = null
|
||||
}
|
||||
|
||||
onEditModeChanged: {
|
||||
if (editMode) {
|
||||
collapseAll()
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!visible) {
|
||||
collapseAll()
|
||||
}
|
||||
}
|
||||
|
||||
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||
|
||||
function setTriggerPosition(x, y, width, section, screen) {
|
||||
StateUtils.setTriggerPosition(root, x, y, width, section, screen)
|
||||
}
|
||||
|
||||
function openWithSection(section) {
|
||||
StateUtils.openWithSection(root, section)
|
||||
}
|
||||
|
||||
function toggleSection(section) {
|
||||
StateUtils.toggleSection(root, section)
|
||||
}
|
||||
|
||||
popupWidth: 550
|
||||
popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400)
|
||||
triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingXS
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||
triggerWidth: 80
|
||||
positioning: "center"
|
||||
positioning: ""
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
visible: shouldBeVisible
|
||||
@@ -70,29 +82,33 @@ DankPopout {
|
||||
} else {
|
||||
Qt.callLater(() => {
|
||||
NetworkService.autoRefreshEnabled = false
|
||||
if (BluetoothService.adapter
|
||||
&& BluetoothService.adapter.discovering)
|
||||
if (BluetoothService.adapter && BluetoothService.adapter.discovering)
|
||||
BluetoothService.adapter.discovering = false
|
||||
editMode = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
WidgetModel {
|
||||
id: widgetModel
|
||||
}
|
||||
|
||||
content: Component {
|
||||
Rectangle {
|
||||
id: controlContent
|
||||
|
||||
|
||||
implicitHeight: mainColumn.implicitHeight + Theme.spacingM
|
||||
property alias bluetoothCodecSelector: bluetoothCodecSelector
|
||||
|
||||
|
||||
color: {
|
||||
const transparency = Theme.popupTransparency || 0.92
|
||||
const transparency = Theme.popupTransparency
|
||||
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
||||
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
||||
}
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
antialiasing: true
|
||||
smooth: true
|
||||
|
||||
@@ -103,614 +119,69 @@ DankPopout {
|
||||
y: Theme.spacingL
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
HeaderPane {
|
||||
id: headerPane
|
||||
width: parent.width
|
||||
height: 90
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r,
|
||||
Theme.surfaceVariant.g,
|
||||
Theme.surfaceVariant.b,
|
||||
Theme.getContentBackgroundAlpha() * 0.4)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Item {
|
||||
id: avatarContainer
|
||||
|
||||
property bool hasImage: profileImageLoader.status === Image.Ready
|
||||
|
||||
width: 64
|
||||
height: 64
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: Theme.primary
|
||||
border.width: 1
|
||||
visible: parent.hasImage
|
||||
}
|
||||
|
||||
Image {
|
||||
id: profileImageLoader
|
||||
|
||||
source: {
|
||||
if (PortalService.profileImage === "")
|
||||
return ""
|
||||
|
||||
if (PortalService.profileImage.startsWith(
|
||||
"/"))
|
||||
return "file://" + PortalService.profileImage
|
||||
|
||||
return PortalService.profileImage
|
||||
}
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
mipmap: true
|
||||
cache: true
|
||||
visible: false
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
source: profileImageLoader
|
||||
maskEnabled: true
|
||||
maskSource: circularMask
|
||||
visible: avatarContainer.hasImage
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
|
||||
Item {
|
||||
id: circularMask
|
||||
|
||||
width: 64 - 10
|
||||
height: 64 - 10
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "black"
|
||||
antialiasing: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: Theme.primary
|
||||
visible: !parent.hasImage
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "person"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "warning"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
visible: PortalService.profileImage !== ""
|
||||
&& profileImageLoader.status === Image.Error
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: UserInfoService.fullName
|
||||
|| UserInfoService.username || "User"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (UserInfoService.uptime
|
||||
|| "Unknown")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
font.weight: Font.Normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 40
|
||||
iconName: "lock"
|
||||
iconSize: Theme.iconSize - 2
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: Qt.rgba(
|
||||
Theme.surfaceVariant.r,
|
||||
Theme.surfaceVariant.g,
|
||||
Theme.surfaceVariant.b,
|
||||
0.5)
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.lockRequested()
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 40
|
||||
iconName: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
|
||||
iconSize: Theme.iconSize - 2
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: Qt.rgba(
|
||||
Theme.surfaceVariant.r,
|
||||
Theme.surfaceVariant.g,
|
||||
Theme.surfaceVariant.b,
|
||||
0.5)
|
||||
onClicked: {
|
||||
root.powerOptionsExpanded = !root.powerOptionsExpanded
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
buttonSize: 40
|
||||
iconName: "settings"
|
||||
iconSize: Theme.iconSize - 2
|
||||
iconColor: Theme.surfaceText
|
||||
backgroundColor: Qt.rgba(
|
||||
Theme.surfaceVariant.r,
|
||||
Theme.surfaceVariant.g,
|
||||
Theme.surfaceVariant.b,
|
||||
0.5)
|
||||
onClicked: {
|
||||
root.close()
|
||||
settingsModal.show()
|
||||
}
|
||||
}
|
||||
powerOptionsExpanded: root.powerOptionsExpanded
|
||||
editMode: root.editMode
|
||||
onPowerOptionsExpandedChanged: root.powerOptionsExpanded = powerOptionsExpanded
|
||||
onEditModeToggled: root.editMode = !root.editMode
|
||||
onPowerActionRequested: (action, title, message) => root.powerActionRequested(action, title, message)
|
||||
onLockRequested: {
|
||||
root.close()
|
||||
root.lockRequested()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
PowerOptionsPane {
|
||||
id: powerOptionsPane
|
||||
width: parent.width
|
||||
implicitHeight: root.powerOptionsExpanded ? 60 : 0
|
||||
height: implicitHeight
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r,
|
||||
Theme.surfaceVariant.g,
|
||||
Theme.surfaceVariant.b,
|
||||
Theme.getContentBackgroundAlpha() * 0.4)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: root.powerOptionsExpanded ? 1 : 0
|
||||
opacity: root.powerOptionsExpanded ? 1 : 0
|
||||
clip: true
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingL
|
||||
visible: root.powerOptionsExpanded
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 34
|
||||
radius: Theme.cornerRadius
|
||||
color: logoutButton.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)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "logout"
|
||||
size: Theme.fontSizeSmall
|
||||
color: logoutButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Logout"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: logoutButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: logoutButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.powerOptionsExpanded = false
|
||||
root.close()
|
||||
root.powerActionRequested(
|
||||
"logout", "Logout",
|
||||
"Are you sure you want to logout?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 34
|
||||
radius: Theme.cornerRadius
|
||||
color: rebootButton.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)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "restart_alt"
|
||||
size: Theme.fontSizeSmall
|
||||
color: rebootButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Restart"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: rebootButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rebootButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.powerOptionsExpanded = false
|
||||
root.close()
|
||||
root.powerActionRequested(
|
||||
"reboot", "Restart",
|
||||
"Are you sure you want to restart?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 34
|
||||
radius: Theme.cornerRadius
|
||||
color: suspendButton.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)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "bedtime"
|
||||
size: Theme.fontSizeSmall
|
||||
color: suspendButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Suspend"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: suspendButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: suspendButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.powerOptionsExpanded = false
|
||||
root.close()
|
||||
root.powerActionRequested(
|
||||
"suspend", "Suspend",
|
||||
"Are you sure you want to suspend?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 100
|
||||
height: 34
|
||||
radius: Theme.cornerRadius
|
||||
color: shutdownButton.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)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: "power_settings_new"
|
||||
size: Theme.fontSizeSmall
|
||||
color: shutdownButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Shutdown"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: shutdownButton.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: shutdownButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.powerOptionsExpanded = false
|
||||
root.close()
|
||||
root.powerActionRequested(
|
||||
"poweroff", "Shutdown",
|
||||
"Are you sure you want to shutdown?")
|
||||
}
|
||||
}
|
||||
}
|
||||
expanded: root.powerOptionsExpanded
|
||||
onPowerActionRequested: (action, title, message) => {
|
||||
root.powerOptionsExpanded = false
|
||||
root.close()
|
||||
root.powerActionRequested(action, title, message)
|
||||
}
|
||||
}
|
||||
|
||||
DragDropGrid {
|
||||
id: widgetGrid
|
||||
width: parent.width
|
||||
editMode: root.editMode
|
||||
expandedSection: root.expandedSection
|
||||
expandedWidgetIndex: root.expandedWidgetIndex
|
||||
expandedWidgetData: root.expandedWidgetData
|
||||
model: widgetModel
|
||||
bluetoothCodecSelector: bluetoothCodecSelector
|
||||
colorPickerModal: root.colorPickerModal
|
||||
onExpandClicked: (widgetData, globalIndex) => {
|
||||
root.expandedWidgetIndex = globalIndex
|
||||
root.expandedWidgetData = widgetData
|
||||
if (widgetData.id === "diskUsage") {
|
||||
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"))
|
||||
} else {
|
||||
root.toggleSection(widgetData.id)
|
||||
}
|
||||
}
|
||||
onRemoveWidget: (index) => widgetModel.removeWidget(index)
|
||||
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
|
||||
onToggleWidgetSize: (index) => widgetModel.toggleWidgetSize(index)
|
||||
}
|
||||
|
||||
Item {
|
||||
EditControls {
|
||||
width: parent.width
|
||||
height: audioSliderRow.implicitHeight
|
||||
|
||||
Row {
|
||||
id: audioSliderRow
|
||||
x: -Theme.spacingS
|
||||
width: parent.width + Theme.spacingS * 2
|
||||
spacing: Theme.spacingM
|
||||
|
||||
AudioSliderRow {
|
||||
width: SettingsData.hideBrightnessSlider ? parent.width - Theme.spacingM : (parent.width - Theme.spacingM) / 2
|
||||
}
|
||||
|
||||
Item {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: parent.height
|
||||
visible: !SettingsData.hideBrightnessSlider
|
||||
|
||||
BrightnessSliderRow {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
x: -Theme.spacingS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
NetworkPill {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
expanded: root.expandedSection === "network"
|
||||
onClicked: {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return
|
||||
}
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
if (NetworkService.ethernetConnected && !NetworkService.wifiEnabled) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
return
|
||||
}
|
||||
root.toggleSection("network")
|
||||
return
|
||||
}
|
||||
if (NetworkService.networkStatus === "wifi") {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
return
|
||||
}
|
||||
NetworkService.disconnectWifi()
|
||||
return
|
||||
}
|
||||
if (!NetworkService.wifiEnabled) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
return
|
||||
}
|
||||
if (NetworkService.wifiEnabled && NetworkService.networkStatus === "disconnected") {
|
||||
root.toggleSection("network")
|
||||
}
|
||||
}
|
||||
onExpandClicked: root.toggleSection("network")
|
||||
}
|
||||
|
||||
BluetoothPill {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
expanded: root.expandedSection === "bluetooth"
|
||||
onClicked: {
|
||||
if (BluetoothService.adapter)
|
||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
||||
}
|
||||
onExpandClicked: root.toggleSection("bluetooth")
|
||||
visible: BluetoothService.available
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
width: parent.width
|
||||
active: root.expandedSection === "network" || root.expandedSection === "bluetooth"
|
||||
visible: active
|
||||
sourceComponent: DetailView {
|
||||
width: parent.width
|
||||
isVisible: true
|
||||
title: {
|
||||
switch (root.expandedSection) {
|
||||
case "network": return "Network Settings"
|
||||
case "bluetooth": return "Bluetooth Settings"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
content: {
|
||||
switch (root.expandedSection) {
|
||||
case "network": return networkDetailComponent
|
||||
case "bluetooth": return bluetoothDetailComponent
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
contentHeight: 250
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
AudioOutputPill {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
expanded: root.expandedSection === "audio_output"
|
||||
onClicked: {
|
||||
if (AudioService.sink) {
|
||||
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
|
||||
}
|
||||
}
|
||||
onExpandClicked: root.toggleSection("audio_output")
|
||||
}
|
||||
|
||||
AudioInputPill {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
expanded: root.expandedSection === "audio_input"
|
||||
onClicked: {
|
||||
if (AudioService.source) {
|
||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
||||
}
|
||||
}
|
||||
onExpandClicked: root.toggleSection("audio_input")
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
width: parent.width
|
||||
active: root.expandedSection === "audio_output" || root.expandedSection === "audio_input"
|
||||
visible: active
|
||||
sourceComponent: DetailView {
|
||||
width: parent.width
|
||||
isVisible: true
|
||||
title: {
|
||||
switch (root.expandedSection) {
|
||||
case "audio_output": return "Audio Output"
|
||||
case "audio_input": return "Audio Input"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
content: {
|
||||
switch (root.expandedSection) {
|
||||
case "audio_output": return audioOutputDetailComponent
|
||||
case "audio_input": return audioInputDetailComponent
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
contentHeight: 250
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
ToggleButton {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
iconName: DisplayService.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
text: "Night Mode"
|
||||
secondaryText: SessionData.nightModeAutoEnabled ? "Auto" : (DisplayService.nightModeEnabled ? "On" : "Off")
|
||||
isActive: DisplayService.nightModeEnabled
|
||||
enabled: DisplayService.automationAvailable
|
||||
onClicked: DisplayService.toggleNightMode()
|
||||
|
||||
DankIcon {
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
name: "schedule"
|
||||
size: 12
|
||||
color: Theme.primary
|
||||
visible: SessionData.nightModeAutoEnabled
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
|
||||
ToggleButton {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
iconName: SessionData.isLightMode ? "light_mode" : "palette"
|
||||
text: "Theme"
|
||||
secondaryText: SessionData.isLightMode ? "Light" : "Dark"
|
||||
isActive: true
|
||||
onClicked: Theme.toggleLightMode()
|
||||
visible: editMode
|
||||
availableWidgets: {
|
||||
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
|
||||
return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id))
|
||||
}
|
||||
onAddWidget: (widgetId) => widgetModel.addWidget(widgetId)
|
||||
onResetToDefault: () => widgetModel.resetToDefault()
|
||||
onClearAll: () => widgetModel.clearAll()
|
||||
}
|
||||
}
|
||||
|
||||
Details.BluetoothCodecSelector {
|
||||
|
||||
BluetoothCodecSelector {
|
||||
id: bluetoothCodecSelector
|
||||
anchors.fill: parent
|
||||
z: 10000
|
||||
@@ -747,4 +218,11 @@ DankPopout {
|
||||
id: audioInputDetailComponent
|
||||
AudioInputDetail {}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: batteryDetailComponent
|
||||
BatteryDetail {}
|
||||
}
|
||||
|
||||
property var colorPickerModal: null
|
||||
}
|
||||
@@ -7,11 +7,16 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
implicitHeight: headerRow.height + volumeSlider.height + audioContent.height + Theme.spacingM
|
||||
property bool hasInputVolumeSliderInCC: {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
return widgets.some(widget => widget.id === "inputVolumeSlider")
|
||||
}
|
||||
|
||||
implicitHeight: headerRow.height + (hasInputVolumeSliderInCC ? 0 : volumeSlider.height) + audioContent.height + Theme.spacingM
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
@@ -33,7 +38,7 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
Row {
|
||||
id: volumeSlider
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -42,34 +47,60 @@ Rectangle {
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingXS
|
||||
height: 35
|
||||
value: AudioService.source && AudioService.source.audio ? Math.round(AudioService.source.audio.volume * 100) : 0
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
leftIcon: {
|
||||
if (!AudioService.source || !AudioService.source.audio) return "mic_off"
|
||||
let muted = AudioService.source.audio.muted
|
||||
return muted ? "mic_off" : "mic"
|
||||
}
|
||||
rightIcon: "volume_up"
|
||||
enabled: AudioService.source && AudioService.source.audio
|
||||
unit: "%"
|
||||
showValue: true
|
||||
visible: AudioService.source && AudioService.source.audio
|
||||
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (AudioService.source && AudioService.source.audio) {
|
||||
AudioService.source.audio.volume = newValue / 100
|
||||
spacing: 0
|
||||
visible: !hasInputVolumeSliderInCC
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
height: Theme.iconSize + Theme.spacingS * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (AudioService.source && AudioService.source.audio) {
|
||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!AudioService.source || !AudioService.source.audio) return "mic_off"
|
||||
let muted = AudioService.source.audio.muted
|
||||
return muted ? "mic_off" : "mic"
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: AudioService.source && AudioService.source.audio && !AudioService.source.audio.muted && AudioService.source.audio.volume > 0 ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.left: parent.left
|
||||
|
||||
DankSlider {
|
||||
readonly property real actualVolumePercent: AudioService.source && AudioService.source.audio ? Math.round(AudioService.source.audio.volume * 100) : 0
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
height: parent.height
|
||||
onClicked: {
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||
enabled: AudioService.source && AudioService.source.audio
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: AudioService.source && AudioService.source.audio ? Math.min(100, Math.round(AudioService.source.audio.volume * 100)) : 0
|
||||
showValue: true
|
||||
unit: "%"
|
||||
valueOverride: actualVolumePercent
|
||||
thumbOutlineColor: Theme.surfaceVariant
|
||||
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (AudioService.source && AudioService.source.audio) {
|
||||
AudioService.source.audio.muted = !AudioService.source.audio.muted
|
||||
AudioService.source.audio.volume = newValue / 100
|
||||
if (newValue > 0 && AudioService.source.audio.muted) {
|
||||
AudioService.source.audio.muted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,12 +108,12 @@ Rectangle {
|
||||
|
||||
DankFlickable {
|
||||
id: audioContent
|
||||
anchors.top: volumeSlider.bottom
|
||||
anchors.top: hasInputVolumeSliderInCC ? headerRow.bottom : volumeSlider.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingS
|
||||
anchors.topMargin: hasInputVolumeSliderInCC ? Theme.spacingM : Theme.spacingS
|
||||
contentHeight: audioColumn.height
|
||||
clip: true
|
||||
|
||||
@@ -103,9 +134,9 @@ Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: deviceMouseArea.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, index % 2 === 0 ? 0.3 : 0.2)
|
||||
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||
border.color: modelData === AudioService.source ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: modelData === AudioService.source ? 2 : 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -163,14 +194,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
implicitHeight: headerRow.height + audioContent.height + Theme.spacingM
|
||||
property bool hasVolumeSliderInCC: {
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
return widgets.some(widget => widget.id === "volumeSlider")
|
||||
}
|
||||
|
||||
implicitHeight: headerRow.height + (!hasVolumeSliderInCC ? volumeSlider.height : 0) + audioContent.height + Theme.spacingM
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
@@ -32,15 +37,88 @@ Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Row {
|
||||
id: volumeSlider
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: headerRow.bottom
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingXS
|
||||
height: 35
|
||||
spacing: 0
|
||||
visible: !hasVolumeSliderInCC
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
height: Theme.iconSize + Theme.spacingS * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = !AudioService.sink.audio.muted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!AudioService.sink || !AudioService.sink.audio) return "volume_off"
|
||||
let muted = AudioService.sink.audio.muted
|
||||
let volume = AudioService.sink.audio.volume
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: AudioService.sink && AudioService.sink.audio && !AudioService.sink.audio.muted && AudioService.sink.audio.volume > 0 ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
readonly property real actualVolumePercent: AudioService.sink && AudioService.sink.audio ? Math.round(AudioService.sink.audio.volume * 100) : 0
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||
enabled: AudioService.sink && AudioService.sink.audio
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: AudioService.sink && AudioService.sink.audio ? Math.min(100, Math.round(AudioService.sink.audio.volume * 100)) : 0
|
||||
showValue: true
|
||||
unit: "%"
|
||||
valueOverride: actualVolumePercent
|
||||
thumbOutlineColor: Theme.surfaceVariant
|
||||
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.volume = newValue / 100
|
||||
if (newValue > 0 && AudioService.sink.audio.muted) {
|
||||
AudioService.sink.audio.muted = false
|
||||
}
|
||||
AudioService.volumeChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
id: audioContent
|
||||
anchors.top: headerRow.bottom
|
||||
anchors.top: volumeSlider.visible ? volumeSlider.bottom : headerRow.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
anchors.topMargin: volumeSlider.visible ? Theme.spacingS : Theme.spacingM
|
||||
contentHeight: audioColumn.height
|
||||
clip: true
|
||||
|
||||
@@ -61,9 +139,9 @@ Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: deviceMouseArea.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, index % 2 === 0 ? 0.3 : 0.2)
|
||||
color: deviceMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||
border.color: modelData === AudioService.sink ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: modelData === AudioService.sink ? 2 : 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -123,14 +201,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
258
Modules/ControlCenter/Details/BatteryDetail.qml
Normal file
258
Modules/ControlCenter/Details/BatteryDetail.qml
Normal file
@@ -0,0 +1,258 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
|
||||
function isActiveProfile(profile) {
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
return false
|
||||
}
|
||||
return PowerProfiles.profile === profile
|
||||
}
|
||||
|
||||
function setProfile(profile) {
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
ToastService.showError("power-profiles-daemon not available")
|
||||
return
|
||||
}
|
||||
PowerProfiles.profile = profile
|
||||
if (PowerProfiles.profile !== profile) {
|
||||
ToastService.showError("Failed to set power profile")
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
width: parent.width
|
||||
height: 48
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: BatteryService.getBatteryIcon()
|
||||
size: Theme.iconSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||
return Theme.error
|
||||
if (BatteryService.isCharging || BatteryService.isPluggedIn)
|
||||
return Theme.primary
|
||||
return Theme.surfaceText
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSizeLarge - Theme.spacingM
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : "Power"
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
return Theme.primary
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? BatteryService.batteryStatus : "Management"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
return Theme.primary
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!BatteryService.batteryAvailable) return "Power profile management available"
|
||||
const time = BatteryService.formatTimeRemaining()
|
||||
if (time !== "Unknown") {
|
||||
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`
|
||||
}
|
||||
return ""
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: BatteryService.batteryAvailable
|
||||
|
||||
StyledRect {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: 64
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Health"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryHealth
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
if (BatteryService.batteryHealth === "N/A") {
|
||||
return Theme.surfaceText
|
||||
}
|
||||
const healthNum = parseInt(BatteryService.batteryHealth)
|
||||
return healthNum < 80 ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
font.weight: Font.Bold
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: 64
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Capacity"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryCapacity > 0 ? `${BatteryService.batteryCapacity.toFixed(1)} Wh` : "Unknown"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Bold
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
property var profileModel: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
||||
property int currentProfileIndex: {
|
||||
if (typeof PowerProfiles === "undefined") return 1
|
||||
return profileModel.findIndex(profile => isActiveProfile(profile))
|
||||
}
|
||||
|
||||
model: profileModel.map(profile => Theme.getPowerProfileLabel(profile))
|
||||
currentIndex: currentProfileIndex
|
||||
selectionMode: "single"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected) return
|
||||
setProfile(profileModel[index])
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: degradationContent.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
||||
border.width: 0
|
||||
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
||||
|
||||
Column {
|
||||
id: degradationContent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "warning"
|
||||
size: Theme.iconSize
|
||||
color: Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: "Power Profile Degradation"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.error
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (typeof PowerProfiles !== "undefined") ? PerformanceDegradationReason.toString(PowerProfiles.degradationReason) : ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ Item {
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
opacity: modalVisible ? 1 : 0
|
||||
scale: modalVisible ? 1 : 0.9
|
||||
|
||||
@@ -206,14 +206,14 @@ Item {
|
||||
radius: Theme.cornerRadius
|
||||
color: {
|
||||
if (modelData.name === currentCodec)
|
||||
return Theme.surfaceContainerHigh;
|
||||
return Theme.surfaceContainerHighest;
|
||||
else if (codecMouseArea.containsMouse)
|
||||
return Theme.surfaceHover;
|
||||
else
|
||||
return "transparent";
|
||||
}
|
||||
border.color: "transparent"
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -272,12 +272,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
implicitHeight: BluetoothService.adapter && BluetoothService.adapter.enabled ? headerRow.height + bluetoothContent.height + Theme.spacingM : headerRow.height
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
|
||||
property var bluetoothCodecModalRef: null
|
||||
|
||||
@@ -58,11 +58,11 @@ Rectangle {
|
||||
radius: 18
|
||||
color: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
return Theme.surfaceContainerHigh
|
||||
return scanMouseArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
|
||||
}
|
||||
border.color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||
|
||||
Row {
|
||||
@@ -96,13 +96,6 @@ Rectangle {
|
||||
BluetoothService.adapter.discovering = !BluetoothService.adapter.discovering
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +152,7 @@ Rectangle {
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
|
||||
if (deviceMouseArea.containsMouse)
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
|
||||
return Theme.surfaceContainerHighest
|
||||
}
|
||||
border.color: {
|
||||
if (modelData.state === BluetoothDeviceState.Connecting)
|
||||
@@ -168,7 +161,7 @@ Rectangle {
|
||||
return Theme.primary
|
||||
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
border.width: (modelData.connected || modelData.state === BluetoothDeviceState.Connecting) ? 2 : 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -284,14 +277,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,9 +332,9 @@ Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: availableMouseArea.containsMouse && !isBusy ? 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.15)
|
||||
color: availableMouseArea.containsMouse && !isBusy ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
opacity: canConnect ? 1 : 0.6
|
||||
|
||||
Row {
|
||||
@@ -427,9 +412,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,7 +440,7 @@ Rectangle {
|
||||
background: Rectangle {
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
|
||||
|
||||
166
Modules/ControlCenter/Details/DiskUsageDetail.qml
Normal file
166
Modules/ControlCenter/Details/DiskUsageDetail.qml
Normal file
@@ -0,0 +1,166 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string currentMountPath: "/"
|
||||
property string instanceId: ""
|
||||
|
||||
signal mountPathChanged(string newMountPath)
|
||||
|
||||
implicitHeight: diskContent.height + Theme.spacingM
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["diskmounts"])
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["diskmounts"])
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
id: diskContent
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Theme.spacingM
|
||||
anchors.topMargin: Theme.spacingM
|
||||
contentHeight: diskColumn.height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
id: diskColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 100
|
||||
visible: !DgopService.dgopAvailable || !DgopService.diskMounts || DgopService.diskMounts.length === 0
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
name: DgopService.dgopAvailable ? "storage" : "error"
|
||||
size: 32
|
||||
color: DgopService.dgopAvailable ? Theme.primary : Theme.error
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: DgopService.dgopAvailable ? "No disk data available" : "dgop not available"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: DgopService.diskMounts || []
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: parent.width
|
||||
height: 80
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHighest
|
||||
border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: modelData.mount === currentMountPath ? 2 : 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
DankIcon {
|
||||
name: "storage"
|
||||
size: Theme.iconSize
|
||||
color: {
|
||||
const percentStr = modelData.percent?.replace("%", "") || "0"
|
||||
const percent = parseFloat(percentStr) || 0
|
||||
if (percent > 90) return Theme.error
|
||||
if (percent > 75) return Theme.warning
|
||||
return modelData.mount === currentMountPath ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const percentStr = modelData.percent?.replace("%", "") || "0"
|
||||
const percent = parseFloat(percentStr) || 0
|
||||
return percent.toFixed(0) + "%"
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - 50 - Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: modelData.mount === "/" ? "Root Filesystem" : modelData.mount
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: modelData.mount === currentMountPath ? Font.Medium : Font.Normal
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: modelData.mount
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
visible: modelData.mount !== "/"
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `${modelData.used || "?"} / ${modelData.size || "?"}`
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
currentMountPath = modelData.mount
|
||||
mountPathChanged(modelData.mount)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ Rectangle {
|
||||
return headerRow.height + wifiOffContent.height + Theme.spacingM
|
||||
}
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
|
||||
Component.onCompleted: {
|
||||
NetworkService.addRef()
|
||||
@@ -27,20 +27,11 @@ Rectangle {
|
||||
NetworkService.scanWifi()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component.onDestruction: {
|
||||
NetworkService.removeRef()
|
||||
}
|
||||
|
||||
property var wifiPasswordModalRef: {
|
||||
wifiPasswordModalLoader.active = true
|
||||
return wifiPasswordModalLoader.item
|
||||
}
|
||||
property var networkInfoModalRef: {
|
||||
networkInfoModalLoader.active = true
|
||||
return networkInfoModalLoader.item
|
||||
}
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
anchors.left: parent.left
|
||||
@@ -65,74 +56,19 @@ Rectangle {
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Row {
|
||||
DankButtonGroup {
|
||||
id: preferenceControls
|
||||
spacing: Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: NetworkService.ethernetConnected && NetworkService.wifiConnected
|
||||
|
||||
Rectangle {
|
||||
property bool isActive: NetworkService.userPreference === "ethernet"
|
||||
|
||||
width: 90
|
||||
height: 36
|
||||
radius: 18
|
||||
color: isActive ? Theme.surfaceContainerHigh : ethernetMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: "Ethernet"
|
||||
color: parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: parent.isActive ? Font.Medium : Font.Normal
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ethernetMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: NetworkService.setNetworkPreference("ethernet")
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property bool isActive: NetworkService.userPreference === "wifi"
|
||||
|
||||
width: 70
|
||||
height: 36
|
||||
radius: 18
|
||||
color: isActive ? Theme.surfaceContainerHigh : wifiMouseArea.containsMouse ? Theme.surfaceHover : "transparent"
|
||||
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: "WiFi"
|
||||
color: parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: parent.isActive ? Font.Medium : Font.Normal
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: NetworkService.setNetworkPreference("wifi")
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
property int currentPreferenceIndex: NetworkService.userPreference === "ethernet" ? 0 : 1
|
||||
|
||||
model: ["Ethernet", "WiFi"]
|
||||
currentIndex: currentPreferenceIndex
|
||||
selectionMode: "single"
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected) return
|
||||
NetworkService.setNetworkPreference(index === 0 ? "ethernet" : "wifi")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +149,7 @@ Rectangle {
|
||||
height: 36
|
||||
radius: 18
|
||||
color: enableWifiButton.containsMouse ? 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.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Theme.primary
|
||||
|
||||
StyledText {
|
||||
@@ -232,12 +168,6 @@ Rectangle {
|
||||
onClicked: NetworkService.toggleWifiRadio()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,9 +227,9 @@ Rectangle {
|
||||
width: parent.width
|
||||
height: 50
|
||||
radius: Theme.cornerRadius
|
||||
color: networkMouseArea.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, index % 2 === 0 ? 0.3 : 0.2)
|
||||
color: networkMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.surfaceContainerHighest
|
||||
border.color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
border.width: modelData.ssid === NetworkService.currentWifiSSID ? 2 : 1
|
||||
border.width: 0
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
@@ -310,11 +240,9 @@ Rectangle {
|
||||
DankIcon {
|
||||
name: {
|
||||
let strength = modelData.signal || 0
|
||||
if (strength >= 70) return "signal_wifi_4_bar"
|
||||
if (strength >= 50) return "network_wifi_3_bar"
|
||||
if (strength >= 25) return "network_wifi_2_bar"
|
||||
if (strength >= 10) return "network_wifi_1_bar"
|
||||
return "signal_wifi_bad"
|
||||
if (strength >= 50) return "wifi"
|
||||
if (strength >= 25) return "wifi_2_bar"
|
||||
return "wifi_1_bar"
|
||||
}
|
||||
size: Theme.iconSize - 4
|
||||
color: modelData.ssid === NetworkService.currentWifiSSID ? Theme.primary : Theme.surfaceText
|
||||
@@ -389,9 +317,7 @@ Rectangle {
|
||||
onClicked: function(event) {
|
||||
if (modelData.ssid !== NetworkService.currentWifiSSID) {
|
||||
if (modelData.secured && !modelData.saved) {
|
||||
if (wifiPasswordModalRef) {
|
||||
wifiPasswordModalRef.show(modelData.ssid)
|
||||
}
|
||||
wifiPasswordModal.show(modelData.ssid)
|
||||
} else {
|
||||
NetworkService.connectToWifi(modelData.ssid)
|
||||
}
|
||||
@@ -400,13 +326,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,7 +345,7 @@ Rectangle {
|
||||
background: Rectangle {
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
|
||||
@@ -452,9 +371,7 @@ Rectangle {
|
||||
NetworkService.disconnectWifi()
|
||||
} else {
|
||||
if (networkContextMenu.currentSecured && !networkContextMenu.currentSaved) {
|
||||
if (wifiPasswordModalRef) {
|
||||
wifiPasswordModalRef.show(networkContextMenu.currentSSID)
|
||||
}
|
||||
wifiPasswordModal.show(networkContextMenu.currentSSID)
|
||||
} else {
|
||||
NetworkService.connectToWifi(networkContextMenu.currentSSID)
|
||||
}
|
||||
@@ -480,10 +397,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
onTriggered: {
|
||||
if (networkInfoModalRef) {
|
||||
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
||||
networkInfoModalRef.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
||||
}
|
||||
let networkData = NetworkService.getNetworkInfo(networkContextMenu.currentSSID)
|
||||
networkInfoModal.showNetworkInfo(networkContextMenu.currentSSID, networkData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,22 +426,12 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: wifiPasswordModalLoader
|
||||
active: false
|
||||
|
||||
WifiPasswordModal {
|
||||
id: wifiPasswordModal
|
||||
}
|
||||
WifiPasswordModal {
|
||||
id: wifiPasswordModal
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: networkInfoModalLoader
|
||||
active: false
|
||||
|
||||
NetworkInfoModal {
|
||||
id: networkInfoModal
|
||||
}
|
||||
|
||||
NetworkInfoModal {
|
||||
id: networkInfoModal
|
||||
}
|
||||
|
||||
|
||||
|
||||
157
Modules/ControlCenter/Models/WidgetModel.qml
Normal file
157
Modules/ControlCenter/Models/WidgetModel.qml
Normal file
@@ -0,0 +1,157 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../utils/widgets.js" as WidgetUtils
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property var baseWidgetDefinitions: [
|
||||
{
|
||||
"id": "nightMode",
|
||||
"text": "Night Mode",
|
||||
"description": "Blue light filter",
|
||||
"icon": "nightlight",
|
||||
"type": "toggle",
|
||||
"enabled": DisplayService.automationAvailable,
|
||||
"warning": !DisplayService.automationAvailable ? "Requires night mode support" : undefined
|
||||
},
|
||||
{
|
||||
"id": "darkMode",
|
||||
"text": "Dark Mode",
|
||||
"description": "System theme toggle",
|
||||
"icon": "contrast",
|
||||
"type": "toggle",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "doNotDisturb",
|
||||
"text": "Do Not Disturb",
|
||||
"description": "Block notifications",
|
||||
"icon": "do_not_disturb_on",
|
||||
"type": "toggle",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "idleInhibitor",
|
||||
"text": "Keep Awake",
|
||||
"description": "Prevent screen timeout",
|
||||
"icon": "motion_sensor_active",
|
||||
"type": "toggle",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "wifi",
|
||||
"text": "Network",
|
||||
"description": "Wi-Fi and Ethernet connection",
|
||||
"icon": "wifi",
|
||||
"type": "connection",
|
||||
"enabled": NetworkService.wifiAvailable,
|
||||
"warning": !NetworkService.wifiAvailable ? "Wi-Fi not available" : undefined
|
||||
},
|
||||
{
|
||||
"id": "bluetooth",
|
||||
"text": "Bluetooth",
|
||||
"description": "Device connections",
|
||||
"icon": "bluetooth",
|
||||
"type": "connection",
|
||||
"enabled": BluetoothService.available,
|
||||
"warning": !BluetoothService.available ? "Bluetooth not available" : undefined
|
||||
},
|
||||
{
|
||||
"id": "audioOutput",
|
||||
"text": "Audio Output",
|
||||
"description": "Speaker settings",
|
||||
"icon": "volume_up",
|
||||
"type": "connection",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "audioInput",
|
||||
"text": "Audio Input",
|
||||
"description": "Microphone settings",
|
||||
"icon": "mic",
|
||||
"type": "connection",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "volumeSlider",
|
||||
"text": "Volume Slider",
|
||||
"description": "Audio volume control",
|
||||
"icon": "volume_up",
|
||||
"type": "slider",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "brightnessSlider",
|
||||
"text": "Brightness Slider",
|
||||
"description": "Display brightness control",
|
||||
"icon": "brightness_6",
|
||||
"type": "slider",
|
||||
"enabled": DisplayService.brightnessAvailable,
|
||||
"warning": !DisplayService.brightnessAvailable ? "Brightness control not available" : undefined
|
||||
},
|
||||
{
|
||||
"id": "inputVolumeSlider",
|
||||
"text": "Input Volume Slider",
|
||||
"description": "Microphone volume control",
|
||||
"icon": "mic",
|
||||
"type": "slider",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "battery",
|
||||
"text": "Battery",
|
||||
"description": "Battery and power management",
|
||||
"icon": "battery_std",
|
||||
"type": "action",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"id": "diskUsage",
|
||||
"text": "Disk Usage",
|
||||
"description": "Filesystem usage monitoring",
|
||||
"icon": "storage",
|
||||
"type": "action",
|
||||
"enabled": DgopService.dgopAvailable,
|
||||
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
|
||||
"allowMultiple": true
|
||||
},
|
||||
{
|
||||
"id": "colorPicker",
|
||||
"text": "Color Picker",
|
||||
"description": "Choose colors from palette",
|
||||
"icon": "palette",
|
||||
"type": "action",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
|
||||
function getWidgetForId(widgetId) {
|
||||
return WidgetUtils.getWidgetForId(baseWidgetDefinitions, widgetId)
|
||||
}
|
||||
|
||||
function addWidget(widgetId) {
|
||||
WidgetUtils.addWidget(widgetId)
|
||||
}
|
||||
|
||||
function removeWidget(index) {
|
||||
WidgetUtils.removeWidget(index)
|
||||
}
|
||||
|
||||
function toggleWidgetSize(index) {
|
||||
WidgetUtils.toggleWidgetSize(index)
|
||||
}
|
||||
|
||||
function moveWidget(fromIndex, toIndex) {
|
||||
WidgetUtils.moveWidget(fromIndex, toIndex)
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
WidgetUtils.resetToDefault()
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
WidgetUtils.clearAll()
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ PanelWindow {
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
|
||||
Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
opacity: powerMenuVisible ? 1 : 0
|
||||
scale: powerMenuVisible ? 1 : 0.85
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
BasePill {
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSource: AudioService.source
|
||||
|
||||
iconName: {
|
||||
if (!defaultSource) return "mic_off"
|
||||
|
||||
|
||||
let volume = defaultSource.audio.volume
|
||||
let muted = defaultSource.audio.muted
|
||||
|
||||
|
||||
if (muted || volume === 0.0) return "mic_off"
|
||||
return "mic"
|
||||
}
|
||||
@@ -41,6 +41,12 @@ BasePill {
|
||||
return Math.round(defaultSource.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSource && defaultSource.audio) {
|
||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSource || !defaultSource.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
|
||||
@@ -7,17 +7,17 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
BasePill {
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var defaultSink: AudioService.sink
|
||||
|
||||
iconName: {
|
||||
if (!defaultSink) return "volume_off"
|
||||
|
||||
|
||||
let volume = defaultSink.audio.volume
|
||||
let muted = defaultSink.audio.muted
|
||||
|
||||
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
@@ -43,6 +43,12 @@ BasePill {
|
||||
return Math.round(defaultSink.audio.volume * 100) + "%"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (defaultSink && defaultSink.audio) {
|
||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||
}
|
||||
}
|
||||
|
||||
onWheelEvent: function (wheelEvent) {
|
||||
if (!defaultSink || !defaultSink.audio) return
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
SimpleSlider {
|
||||
id: root
|
||||
|
||||
property var defaultSink: AudioService.sink
|
||||
|
||||
iconName: {
|
||||
if (!defaultSink) return "volume_off"
|
||||
|
||||
let volume = defaultSink.audio.volume
|
||||
let muted = defaultSink.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
return "volume_up"
|
||||
}
|
||||
|
||||
iconColor: defaultSink && !defaultSink.audio.muted && defaultSink.audio.volume > 0 ? Theme.primary : Theme.surfaceText
|
||||
|
||||
enabled: defaultSink !== null
|
||||
allowIconClick: defaultSink !== null
|
||||
|
||||
value: defaultSink ? defaultSink.audio.volume : 0.0
|
||||
maximumValue: 1.0
|
||||
minimumValue: 0.0
|
||||
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (defaultSink) {
|
||||
defaultSink.audio.volume = newValue
|
||||
if (newValue > 0 && defaultSink.audio.muted) {
|
||||
defaultSink.audio.muted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onIconClicked: function() {
|
||||
if (defaultSink) {
|
||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,20 +10,17 @@ Row {
|
||||
id: root
|
||||
|
||||
property var defaultSink: AudioService.sink
|
||||
property color sliderTrackColor: "transparent"
|
||||
|
||||
height: 40
|
||||
spacing: Theme.spacingS
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
height: Theme.iconSize + Theme.spacingS * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 // Make it circular
|
||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
@@ -33,7 +30,9 @@ Row {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (defaultSink) {
|
||||
AudioService.suppressOSD = true
|
||||
defaultSink.audio.muted = !defaultSink.audio.muted
|
||||
AudioService.suppressOSD = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +41,10 @@ Row {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!defaultSink) return "volume_off"
|
||||
|
||||
|
||||
let volume = defaultSink.audio.volume
|
||||
let muted = defaultSink.audio.muted
|
||||
|
||||
|
||||
if (muted || volume === 0.0) return "volume_off"
|
||||
if (volume <= 0.33) return "volume_down"
|
||||
if (volume <= 0.66) return "volume_up"
|
||||
@@ -57,12 +56,22 @@ Row {
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
readonly property real actualVolumePercent: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||
enabled: defaultSink !== null
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0
|
||||
value: defaultSink ? Math.min(100, Math.round(defaultSink.audio.volume * 100)) : 0
|
||||
showValue: true
|
||||
unit: "%"
|
||||
valueOverride: actualVolumePercent
|
||||
thumbOutlineColor: Theme.surfaceContainer
|
||||
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
|
||||
onIsDraggingChanged: {
|
||||
AudioService.suppressOSD = isDragging
|
||||
}
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (defaultSink) {
|
||||
defaultSink.audio.volume = newValue / 100.0
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property color iconColor: Theme.surfaceText
|
||||
property string primaryText: ""
|
||||
property string secondaryText: ""
|
||||
property bool expanded: false
|
||||
property bool isActive: false
|
||||
|
||||
signal clicked()
|
||||
signal expandClicked()
|
||||
signal wheelEvent(var wheelEvent)
|
||||
|
||||
width: parent ? parent.width : 200
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
|
||||
Rectangle {
|
||||
id: mainArea
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width - expandArea.width
|
||||
topLeftRadius: Theme.cornerRadius
|
||||
bottomLeftRadius: Theme.cornerRadius
|
||||
topRightRadius: 0
|
||||
bottomRightRadius: 0
|
||||
color: mainAreaMouse.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
"transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: root.isActive ? Theme.primary : root.iconColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - Theme.spacingS
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.primaryText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mainAreaMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.clicked()
|
||||
onWheel: function (wheelEvent) {
|
||||
root.wheelEvent(wheelEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: expandArea
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: Theme.iconSize + Theme.spacingM * 2
|
||||
topLeftRadius: 0
|
||||
bottomLeftRadius: 0
|
||||
topRightRadius: Theme.cornerRadius
|
||||
bottomRightRadius: Theme.cornerRadius
|
||||
color: expandAreaMouse.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
"transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: expandIcon
|
||||
anchors.centerIn: parent
|
||||
name: expanded ? "expand_less" : "expand_more"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: expandAreaMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.expandClicked()
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Modules/ControlCenter/Widgets/BatteryPill.qml
Normal file
48
Modules/ControlCenter/Widgets/BatteryPill.qml
Normal file
@@ -0,0 +1,48 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
iconName: BatteryService.getBatteryIcon()
|
||||
|
||||
isActive: BatteryService.batteryAvailable && (BatteryService.isCharging || BatteryService.isPluggedIn)
|
||||
|
||||
primaryText: {
|
||||
if (!BatteryService.batteryAvailable) {
|
||||
return "No battery"
|
||||
}
|
||||
return "Battery"
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!BatteryService.batteryAvailable) {
|
||||
return "Not available"
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
return `${BatteryService.batteryLevel}% • Charging`
|
||||
}
|
||||
if (BatteryService.isPluggedIn) {
|
||||
return `${BatteryService.batteryLevel}% • Plugged in`
|
||||
}
|
||||
return `${BatteryService.batteryLevel}%`
|
||||
}
|
||||
|
||||
iconColor: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||
return Theme.primary
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
expandClicked()
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,14 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
BasePill {
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var primaryDevice: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
let devices = [...BluetoothService.adapter.devices.values.filter(dev => dev && (dev.paired || dev.trusted))]
|
||||
for (let device of devices) {
|
||||
if (device && device.connected) {
|
||||
@@ -30,17 +30,15 @@ BasePill {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
||||
return "bluetooth_disabled"
|
||||
}
|
||||
if (primaryDevice) {
|
||||
return BluetoothService.getDeviceIcon(primaryDevice)
|
||||
}
|
||||
return "bluetooth"
|
||||
}
|
||||
|
||||
isActive: !!(BluetoothService.available && BluetoothService.adapter && BluetoothService.adapter.enabled)
|
||||
showExpandArea: BluetoothService.available
|
||||
|
||||
primaryText: {
|
||||
if (!BluetoothService.available) {
|
||||
return "Bluetooth unavailable"
|
||||
return "Bluetooth"
|
||||
}
|
||||
if (!BluetoothService.adapter) {
|
||||
return "No adapter"
|
||||
@@ -53,7 +51,7 @@ BasePill {
|
||||
|
||||
secondaryText: {
|
||||
if (!BluetoothService.available) {
|
||||
return "Hardware not found"
|
||||
return "No adapters"
|
||||
}
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) {
|
||||
return "Off"
|
||||
@@ -63,4 +61,10 @@ BasePill {
|
||||
}
|
||||
return "No devices"
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (BluetoothService.available && BluetoothService.adapter) {
|
||||
BluetoothService.adapter.enabled = !BluetoothService.adapter.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
SimpleSlider {
|
||||
id: root
|
||||
|
||||
iconName: {
|
||||
if (!DisplayService.brightnessAvailable) return "brightness_low"
|
||||
|
||||
let brightness = DisplayService.brightnessLevel
|
||||
if (brightness <= 33) return "brightness_low"
|
||||
if (brightness <= 66) return "brightness_medium"
|
||||
return "brightness_high"
|
||||
}
|
||||
|
||||
iconColor: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
|
||||
|
||||
enabled: DisplayService.brightnessAvailable
|
||||
|
||||
value: DisplayService.brightnessLevel
|
||||
maximumValue: 100.0
|
||||
minimumValue: 0.0
|
||||
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (DisplayService.brightnessAvailable) {
|
||||
DisplayService.brightnessLevel = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ Row {
|
||||
id: root
|
||||
|
||||
height: 40
|
||||
spacing: Theme.spacingS
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
@@ -17,19 +17,15 @@ Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||
color: iconArea.containsMouse
|
||||
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
: "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
||||
: Theme.withAlpha(Theme.primary, 0)
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
|
||||
onClicked: function(event) {
|
||||
if (DisplayService.devices.length > 1) {
|
||||
if (deviceMenu.visible) {
|
||||
@@ -40,26 +36,42 @@ Row {
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!DisplayService.brightnessAvailable) return "brightness_low"
|
||||
|
||||
let brightness = DisplayService.brightnessLevel
|
||||
if (brightness <= 33) return "brightness_low"
|
||||
if (brightness <= 66) return "brightness_medium"
|
||||
return "brightness_high"
|
||||
onEntered: {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
const tooltipText = DisplayService.currentDevice ? "bl device: " + DisplayService.currentDevice : "Backlight Control"
|
||||
const p = iconArea.mapToItem(null, iconArea.width / 2, 0)
|
||||
tooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
|
||||
}
|
||||
}
|
||||
|
||||
onExited: {
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hide()
|
||||
}
|
||||
tooltipLoader.active = false
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!DisplayService.brightnessAvailable) return "brightness_low"
|
||||
|
||||
let brightness = DisplayService.brightnessLevel
|
||||
if (brightness <= 33) return "brightness_low"
|
||||
if (brightness <= 66) return "brightness_medium"
|
||||
return "brightness_high"
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: DisplayService.brightnessAvailable && DisplayService.brightnessLevel > 0 ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2) - Theme.spacingM
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||
enabled: DisplayService.brightnessAvailable
|
||||
minimum: 1
|
||||
maximum: 100
|
||||
@@ -79,6 +91,8 @@ Row {
|
||||
DisplayService.setBrightness(newValue)
|
||||
}
|
||||
}
|
||||
thumbOutlineColor: Theme.surfaceContainer
|
||||
trackColor: Theme.surfaceContainerHigh
|
||||
}
|
||||
|
||||
Menu {
|
||||
@@ -89,7 +103,7 @@ Row {
|
||||
background: Rectangle {
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
|
||||
@@ -139,4 +153,10 @@ Row {
|
||||
onObjectRemoved: (index, object) => deviceMenu.removeItem(object)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
}
|
||||
33
Modules/ControlCenter/Widgets/ColorPickerPill.qml
Normal file
33
Modules/ControlCenter/Widgets/ColorPickerPill.qml
Normal file
@@ -0,0 +1,33 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property var colorPickerModal: null
|
||||
|
||||
isActive: true
|
||||
iconName: "palette"
|
||||
iconColor: Theme.primary
|
||||
primaryText: "Color Picker"
|
||||
secondaryText: "Choose a color"
|
||||
|
||||
onToggled: {
|
||||
console.log("ColorPickerPill toggled, modal:", colorPickerModal)
|
||||
if (colorPickerModal) {
|
||||
colorPickerModal.show()
|
||||
}
|
||||
}
|
||||
|
||||
onExpandClicked: {
|
||||
console.log("ColorPickerPill expandClicked, modal:", colorPickerModal)
|
||||
if (colorPickerModal) {
|
||||
colorPickerModal.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,9 @@ Rectangle {
|
||||
width: parent ? parent.width : 200
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
Row {
|
||||
|
||||
170
Modules/ControlCenter/Widgets/CompoundPill.qml
Normal file
170
Modules/ControlCenter/Widgets/CompoundPill.qml
Normal file
@@ -0,0 +1,170 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property color iconColor: Theme.surfaceText
|
||||
property string primaryText: ""
|
||||
property string secondaryText: ""
|
||||
property bool expanded: false
|
||||
property bool isActive: false
|
||||
property bool showExpandArea: true
|
||||
|
||||
signal toggled()
|
||||
signal expandClicked()
|
||||
signal wheelEvent(var wheelEvent)
|
||||
|
||||
width: parent ? parent.width : 220
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
}
|
||||
|
||||
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||
|
||||
color: {
|
||||
const baseColor = bodyMouse.containsMouse ? Theme.widgetBaseHoverColor : _containerBg
|
||||
return baseColor
|
||||
}
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.10)
|
||||
border.width: 0
|
||||
antialiasing: true
|
||||
|
||||
readonly property color _labelPrimary: Theme.surfaceText
|
||||
readonly property color _labelSecondary: Theme.surfaceVariantText
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive: {
|
||||
const transparency = Theme.popupTransparency
|
||||
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
|
||||
return Qt.rgba(surface.r, surface.g, surface.b, transparency)
|
||||
}
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileRingInactive:
|
||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.18)
|
||||
readonly property color _tileIconActive: Theme.primaryContainer
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
|
||||
property int _padH: Theme.spacingS
|
||||
property int _tileSize: 48
|
||||
property int _tileRadius: Theme.cornerRadius
|
||||
|
||||
Rectangle {
|
||||
id: rightHoverOverlay
|
||||
anchors.fill: parent
|
||||
radius: root.radius
|
||||
z: 0
|
||||
visible: false
|
||||
color: hoverTint(_containerBg)
|
||||
opacity: 0.08
|
||||
antialiasing: true
|
||||
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: _padH
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
id: iconTile
|
||||
z: 1
|
||||
width: _tileSize
|
||||
height: _tileSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: _tileRadius
|
||||
color: isActive ? _tileBgActive : _tileBgInactive
|
||||
border.color: isActive ? _tileRingActive : "transparent"
|
||||
border.width: isActive ? 1 : 0
|
||||
antialiasing: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: _tileRadius
|
||||
color: hoverTint(iconTile.color)
|
||||
opacity: tileMouse.pressed ? 0.3 : (tileMouse.containsMouse ? 0.2 : 0.0)
|
||||
visible: opacity > 0
|
||||
antialiasing: true
|
||||
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? _tileIconActive : _tileIconInactive
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: tileMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggled()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: body
|
||||
width: row.width - iconTile.width - row.spacing
|
||||
height: row.height
|
||||
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.primaryText
|
||||
color: _labelPrimary
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
color: _labelSecondary
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: bodyMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: { rightHoverOverlay.visible = true; rightHoverOverlay.opacity = 0.08 }
|
||||
onExited: { rightHoverOverlay.opacity = 0.0; rightHoverOverlay.visible = false }
|
||||
onPressed: rightHoverOverlay.opacity = 0.16
|
||||
onReleased: rightHoverOverlay.opacity = containsMouse ? 0.08 : 0.0
|
||||
onClicked: root.expandClicked()
|
||||
onWheel: function (ev) {
|
||||
root.wheelEvent(ev)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
Keys.onPressed: function (ev) {
|
||||
if (ev.key === Qt.Key_Space || ev.key === Qt.Key_Return) { root.toggled(); ev.accepted = true }
|
||||
else if (ev.key === Qt.Key_Right) { root.expandClicked(); ev.accepted = true }
|
||||
}
|
||||
}
|
||||
78
Modules/ControlCenter/Widgets/DiskUsagePill.qml
Normal file
78
Modules/ControlCenter/Widgets/DiskUsagePill.qml
Normal file
@@ -0,0 +1,78 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
property string mountPath: "/"
|
||||
property string instanceId: ""
|
||||
|
||||
iconName: "storage"
|
||||
|
||||
property var selectedMount: {
|
||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const targetMount = DgopService.diskMounts.find(mount => mount.mount === mountPath)
|
||||
return targetMount || DgopService.diskMounts.find(mount => mount.mount === "/") || DgopService.diskMounts[0]
|
||||
}
|
||||
|
||||
property real usagePercent: {
|
||||
if (!selectedMount || !selectedMount.percent) {
|
||||
return 0
|
||||
}
|
||||
const percentStr = selectedMount.percent.replace("%", "")
|
||||
return parseFloat(percentStr) || 0
|
||||
}
|
||||
|
||||
isActive: DgopService.dgopAvailable && selectedMount !== null
|
||||
|
||||
primaryText: {
|
||||
if (!DgopService.dgopAvailable) {
|
||||
return "Disk Usage"
|
||||
}
|
||||
if (!selectedMount) {
|
||||
return "No disk data"
|
||||
}
|
||||
return selectedMount.mount
|
||||
}
|
||||
|
||||
secondaryText: {
|
||||
if (!DgopService.dgopAvailable) {
|
||||
return "dgop not available"
|
||||
}
|
||||
if (!selectedMount) {
|
||||
return "No disk data available"
|
||||
}
|
||||
return `${selectedMount.used} / ${selectedMount.size} (${usagePercent.toFixed(0)}%)`
|
||||
}
|
||||
|
||||
iconColor: {
|
||||
if (!DgopService.dgopAvailable || !selectedMount) {
|
||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
}
|
||||
if (usagePercent > 90) {
|
||||
return Theme.error
|
||||
}
|
||||
if (usagePercent > 75) {
|
||||
return Theme.warning
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["diskmounts"])
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["diskmounts"])
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
expandClicked()
|
||||
}
|
||||
}
|
||||
82
Modules/ControlCenter/Widgets/InputAudioSliderRow.qml
Normal file
82
Modules/ControlCenter/Widgets/InputAudioSliderRow.qml
Normal file
@@ -0,0 +1,82 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property var defaultSource: AudioService.source
|
||||
property color sliderTrackColor: "transparent"
|
||||
|
||||
height: 40
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize + Theme.spacingS * 2
|
||||
height: Theme.iconSize + Theme.spacingS * 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: (Theme.iconSize + Theme.spacingS * 2) / 2
|
||||
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
|
||||
|
||||
MouseArea {
|
||||
id: iconArea
|
||||
anchors.fill: parent
|
||||
visible: defaultSource !== null
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (defaultSource) {
|
||||
AudioService.suppressOSD = true
|
||||
defaultSource.audio.muted = !defaultSource.audio.muted
|
||||
AudioService.suppressOSD = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: {
|
||||
if (!defaultSource) return "mic_off"
|
||||
|
||||
let volume = defaultSource.audio.volume
|
||||
let muted = defaultSource.audio.muted
|
||||
|
||||
if (muted || volume === 0.0) return "mic_off"
|
||||
return "mic"
|
||||
}
|
||||
size: Theme.iconSize
|
||||
color: defaultSource && !defaultSource.audio.muted && defaultSource.audio.volume > 0 ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
readonly property real actualVolumePercent: defaultSource ? Math.round(defaultSource.audio.volume * 100) : 0
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - (Theme.iconSize + Theme.spacingS * 2)
|
||||
enabled: defaultSource !== null
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: defaultSource ? Math.min(100, Math.round(defaultSource.audio.volume * 100)) : 0
|
||||
showValue: true
|
||||
unit: "%"
|
||||
valueOverride: actualVolumePercent
|
||||
thumbOutlineColor: Theme.surfaceContainer
|
||||
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
|
||||
onIsDraggingChanged: {
|
||||
AudioService.suppressOSD = isDragging
|
||||
}
|
||||
onSliderValueChanged: function(newValue) {
|
||||
if (defaultSource) {
|
||||
defaultSource.audio.volume = newValue / 100.0
|
||||
if (newValue > 0 && defaultSource.audio.muted) {
|
||||
defaultSource.audio.muted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.ControlCenter.Widgets
|
||||
|
||||
BasePill {
|
||||
CompoundPill {
|
||||
id: root
|
||||
|
||||
isActive: {
|
||||
@@ -21,7 +21,7 @@ BasePill {
|
||||
}
|
||||
return NetworkService.wifiEnabled
|
||||
}
|
||||
|
||||
|
||||
iconName: {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return "sync"
|
||||
@@ -33,7 +33,7 @@ BasePill {
|
||||
return NetworkService.wifiSignalIcon
|
||||
}
|
||||
if (NetworkService.wifiEnabled) {
|
||||
return "signal_wifi_off"
|
||||
return "wifi_off"
|
||||
}
|
||||
return "wifi_off"
|
||||
}
|
||||
@@ -67,6 +67,12 @@ BasePill {
|
||||
if (NetworkService.wifiEnabled) {
|
||||
return "Select network"
|
||||
}
|
||||
return "Tap to enable"
|
||||
return ""
|
||||
}
|
||||
|
||||
onToggled: {
|
||||
if (NetworkService.networkStatus !== "ethernet" && !NetworkService.wifiToggling) {
|
||||
NetworkService.toggleWifiRadio()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property color iconColor: Theme.surfaceText
|
||||
property real value: 0.0
|
||||
property real maximumValue: 1.0
|
||||
property real minimumValue: 0.0
|
||||
property bool enabled: true
|
||||
property bool allowIconClick: false
|
||||
|
||||
signal sliderValueChanged(real value)
|
||||
signal iconClicked()
|
||||
|
||||
height: 60
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: root.iconColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
visible: root.allowIconClick
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.iconClicked()
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: {
|
||||
if (parent.width <= 0) return 80
|
||||
return Math.max(80, Math.min(400, parent.width - Theme.iconSize - Theme.spacingM))
|
||||
}
|
||||
enabled: root.enabled
|
||||
minimum: Math.round(root.minimumValue * 100)
|
||||
maximum: Math.round(root.maximumValue * 100)
|
||||
value: Math.round(root.value * 100)
|
||||
onSliderValueChanged: function(newValue) { root.sliderValueChanged(newValue / 100.0) }
|
||||
}
|
||||
}
|
||||
100
Modules/ControlCenter/Widgets/SmallBatteryButton.qml
Normal file
100
Modules/ControlCenter/Widgets/SmallBatteryButton.qml
Normal file
@@ -0,0 +1,100 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isActive: BatteryService.batteryAvailable && (BatteryService.isCharging || BatteryService.isPluggedIn)
|
||||
property bool enabled: BatteryService.batteryAvailable
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
|
||||
height: 48
|
||||
radius: {
|
||||
if (Theme.cornerRadius === 0) return 0
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
|
||||
}
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileIconActive: Theme.primaryContainer
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
|
||||
color: {
|
||||
if (isActive) return _tileBgActive
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||
return baseColor
|
||||
}
|
||||
border.color: isActive ? _tileRingActive : "transparent"
|
||||
border.width: isActive ? 1 : 0
|
||||
antialiasing: true
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: hoverTint(root.color)
|
||||
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
|
||||
visible: opacity > 0
|
||||
antialiasing: true
|
||||
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
DankIcon {
|
||||
name: BatteryService.getBatteryIcon()
|
||||
size: parent.parent.width * 0.25
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
return isActive ? _tileIconActive : _tileIconInactive
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : ""
|
||||
font.pixelSize: parent.parent.width * 0.15
|
||||
font.weight: Font.Medium
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
return isActive ? _tileIconActive : _tileIconInactive
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: root.enabled
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Modules/ControlCenter/Widgets/SmallToggleButton.qml
Normal file
80
Modules/ControlCenter/Widgets/SmallToggleButton.qml
Normal file
@@ -0,0 +1,80 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property string iconName: ""
|
||||
property bool isActive: false
|
||||
property bool enabled: true
|
||||
property real iconRotation: 0
|
||||
|
||||
signal clicked()
|
||||
signal iconRotationCompleted()
|
||||
|
||||
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
|
||||
height: 48
|
||||
radius: {
|
||||
if (Theme.cornerRadius === 0) return 0
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
|
||||
}
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
readonly property color _tileIconActive: Theme.primaryContainer
|
||||
readonly property color _tileIconInactive: Theme.primary
|
||||
|
||||
color: {
|
||||
if (isActive) return _tileBgActive
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||
return baseColor
|
||||
}
|
||||
border.color: isActive ? _tileRingActive : "transparent"
|
||||
border.width: isActive ? 1 : 0
|
||||
antialiasing: true
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: hoverTint(root.color)
|
||||
opacity: mouseArea.pressed ? 0.3 : (mouseArea.containsMouse ? 0.2 : 0.0)
|
||||
visible: opacity > 0
|
||||
antialiasing: true
|
||||
Behavior on opacity { NumberAnimation { duration: Theme.shortDuration } }
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? _tileIconActive : _tileIconInactive
|
||||
rotation: iconRotation
|
||||
onRotationCompleted: root.iconRotationCompleted()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
enabled: root.enabled
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
@@ -12,67 +11,94 @@ Rectangle {
|
||||
property bool isActive: false
|
||||
property bool enabled: true
|
||||
property string secondaryText: ""
|
||||
|
||||
property real iconRotation: 0
|
||||
|
||||
signal clicked()
|
||||
signal iconRotationCompleted()
|
||||
|
||||
width: parent ? parent.width : 200
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
radius: {
|
||||
if (Theme.cornerRadius === 0) return 0
|
||||
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
|
||||
}
|
||||
|
||||
readonly property color _tileBgActive: Theme.primary
|
||||
readonly property color _tileBgInactive: Theme.surfaceContainerHigh
|
||||
readonly property color _tileRingActive:
|
||||
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
|
||||
|
||||
color: {
|
||||
if (isActive) return _tileBgActive
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : _tileBgInactive
|
||||
return baseColor
|
||||
}
|
||||
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 0
|
||||
opacity: enabled ? 1.0 : 0.6
|
||||
|
||||
function hoverTint(base) {
|
||||
const factor = 1.2
|
||||
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
|
||||
}
|
||||
|
||||
readonly property color _containerBg: Theme.surfaceContainerHigh
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: mouseArea.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
"transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Theme.shortDuration }
|
||||
color: mouseArea.containsMouse ? hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0)
|
||||
opacity: mouseArea.containsMouse ? 0.08 : 0.0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Theme.shortDuration }
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Theme.spacingL + 2
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: root.iconName
|
||||
size: Theme.iconSize
|
||||
color: root.isActive ? Theme.primary : Theme.surfaceText
|
||||
color: isActive ? Theme.primaryContainer : Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
rotation: root.iconRotation
|
||||
onRotationCompleted: root.iconRotationCompleted()
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - Theme.spacingS
|
||||
spacing: 2
|
||||
Item {
|
||||
width: parent.width - Theme.iconSize - parent.spacing
|
||||
height: parent.height
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.text
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
Column {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.text
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: isActive ? Theme.primaryContainer : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
|
||||
StyledText {
|
||||
width: parent.width
|
||||
text: root.secondaryText
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: isActive ? Theme.primaryContainer : Theme.surfaceVariantText
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,8 +112,8 @@ Rectangle {
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
Behavior on radius {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
45
Modules/ControlCenter/utils/layout.js
Normal file
45
Modules/ControlCenter/utils/layout.js
Normal file
@@ -0,0 +1,45 @@
|
||||
function calculateRowsAndWidgets(controlCenterColumn, expandedSection, expandedWidgetIndex) {
|
||||
var rows = []
|
||||
var currentRow = []
|
||||
var currentWidth = 0
|
||||
var expandedRow = -1
|
||||
|
||||
const widgets = SettingsData.controlCenterWidgets || []
|
||||
const baseWidth = controlCenterColumn.width
|
||||
const spacing = Theme.spacingS
|
||||
|
||||
for (var i = 0; i < widgets.length; i++) {
|
||||
const widget = widgets[i]
|
||||
const widgetWidth = widget.width || 50
|
||||
|
||||
var itemWidth
|
||||
if (widgetWidth <= 25) {
|
||||
itemWidth = (baseWidth - spacing * 3) / 4
|
||||
} else if (widgetWidth <= 50) {
|
||||
itemWidth = (baseWidth - spacing) / 2
|
||||
} else if (widgetWidth <= 75) {
|
||||
itemWidth = (baseWidth - spacing * 2) * 0.75
|
||||
} else {
|
||||
itemWidth = baseWidth
|
||||
}
|
||||
|
||||
if (currentRow.length > 0 && (currentWidth + spacing + itemWidth > baseWidth)) {
|
||||
rows.push([...currentRow])
|
||||
currentRow = [widget]
|
||||
currentWidth = itemWidth
|
||||
} else {
|
||||
currentRow.push(widget)
|
||||
currentWidth += (currentRow.length > 1 ? spacing : 0) + itemWidth
|
||||
}
|
||||
|
||||
if (expandedWidgetIndex === i) {
|
||||
expandedRow = rows.length
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow)
|
||||
}
|
||||
|
||||
return { rows: rows, expandedRowIndex: expandedRow }
|
||||
}
|
||||
25
Modules/ControlCenter/utils/state.js
Normal file
25
Modules/ControlCenter/utils/state.js
Normal file
@@ -0,0 +1,25 @@
|
||||
function setTriggerPosition(root, x, y, width, section, screen) {
|
||||
root.triggerX = x
|
||||
root.triggerY = y
|
||||
root.triggerWidth = width
|
||||
root.triggerSection = section
|
||||
root.triggerScreen = screen
|
||||
}
|
||||
|
||||
function openWithSection(root, section) {
|
||||
if (root.shouldBeVisible) {
|
||||
root.close()
|
||||
} else {
|
||||
root.expandedSection = section
|
||||
root.open()
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSection(root, section) {
|
||||
if (root.expandedSection === section) {
|
||||
root.expandedSection = ""
|
||||
root.expandedWidgetIndex = -1
|
||||
} else {
|
||||
root.expandedSection = section
|
||||
}
|
||||
}
|
||||
85
Modules/ControlCenter/utils/widgets.js
Normal file
85
Modules/ControlCenter/utils/widgets.js
Normal file
@@ -0,0 +1,85 @@
|
||||
function getWidgetForId(baseWidgetDefinitions, widgetId) {
|
||||
return baseWidgetDefinitions.find(w => w.id === widgetId)
|
||||
}
|
||||
|
||||
function addWidget(widgetId) {
|
||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
||||
var widget = {
|
||||
"id": widgetId,
|
||||
"enabled": true,
|
||||
"width": 50
|
||||
}
|
||||
|
||||
if (widgetId === "diskUsage") {
|
||||
widget.instanceId = generateUniqueId()
|
||||
widget.mountPath = "/"
|
||||
}
|
||||
|
||||
widgets.push(widget)
|
||||
SettingsData.setControlCenterWidgets(widgets)
|
||||
}
|
||||
|
||||
function generateUniqueId() {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substr(2)
|
||||
}
|
||||
|
||||
function removeWidget(index) {
|
||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
||||
if (index >= 0 && index < widgets.length) {
|
||||
widgets.splice(index, 1)
|
||||
SettingsData.setControlCenterWidgets(widgets)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleWidgetSize(index) {
|
||||
var widgets = SettingsData.controlCenterWidgets.slice()
|
||||
if (index >= 0 && index < widgets.length) {
|
||||
const currentWidth = widgets[index].width || 50
|
||||
const id = widgets[index].id || ""
|
||||
|
||||
if (id === "wifi" || id === "bluetooth" || id === "audioOutput" || id === "audioInput") {
|
||||
widgets[index].width = currentWidth <= 50 ? 100 : 50
|
||||
} else {
|
||||
if (currentWidth <= 25) {
|
||||
widgets[index].width = 50
|
||||
} else if (currentWidth <= 50) {
|
||||
widgets[index].width = 100
|
||||
} else {
|
||||
widgets[index].width = 25
|
||||
}
|
||||
}
|
||||
|
||||
SettingsData.setControlCenterWidgets(widgets)
|
||||
}
|
||||
}
|
||||
|
||||
function reorderWidgets(newOrder) {
|
||||
SettingsData.setControlCenterWidgets(newOrder)
|
||||
}
|
||||
|
||||
function moveWidget(fromIndex, toIndex) {
|
||||
let widgets = [...(SettingsData.controlCenterWidgets || [])]
|
||||
if (fromIndex >= 0 && fromIndex < widgets.length && toIndex >= 0 && toIndex < widgets.length) {
|
||||
const movedWidget = widgets.splice(fromIndex, 1)[0]
|
||||
widgets.splice(toIndex, 0, movedWidget)
|
||||
SettingsData.setControlCenterWidgets(widgets)
|
||||
}
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
const defaultWidgets = [
|
||||
{"id": "volumeSlider", "enabled": true, "width": 50},
|
||||
{"id": "brightnessSlider", "enabled": true, "width": 50},
|
||||
{"id": "wifi", "enabled": true, "width": 50},
|
||||
{"id": "bluetooth", "enabled": true, "width": 50},
|
||||
{"id": "audioOutput", "enabled": true, "width": 50},
|
||||
{"id": "audioInput", "enabled": true, "width": 50},
|
||||
{"id": "nightMode", "enabled": true, "width": 50},
|
||||
{"id": "darkMode", "enabled": true, "width": 50}
|
||||
]
|
||||
SettingsData.setControlCenterWidgets(defaultWidgets)
|
||||
}
|
||||
|
||||
function clearAll() {
|
||||
SettingsData.setControlCenterWidgets([])
|
||||
}
|
||||
179
Modules/DankBar/AutoHideManager.qml
Normal file
179
Modules/DankBar/AutoHideManager.qml
Normal file
@@ -0,0 +1,179 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var barWindow
|
||||
required property var axis
|
||||
required property var appDrawerLoader
|
||||
required property var dankDashPopoutLoader
|
||||
required property var processListPopoutLoader
|
||||
required property var notificationCenterLoader
|
||||
required property var batteryPopoutLoader
|
||||
required property var vpnPopoutLoader
|
||||
required property var controlCenterLoader
|
||||
required property var clipboardHistoryModalPopup
|
||||
required property var systemUpdateLoader
|
||||
required property var notepadInstance
|
||||
|
||||
property alias reveal: core.reveal
|
||||
property alias autoHide: core.autoHide
|
||||
property alias backgroundTransparency: core.backgroundTransparency
|
||||
property alias hasActivePopout: core.hasActivePopout
|
||||
property alias mouseArea: topBarMouseArea
|
||||
|
||||
Item {
|
||||
id: inputMask
|
||||
|
||||
readonly property int barThickness: barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing)
|
||||
|
||||
readonly property bool showing: SettingsData.dankBarVisible && (core.reveal
|
||||
|| (CompositorService.isNiri && NiriService.inOverview && SettingsData.dankBarOpenOnOverview)
|
||||
|| !core.autoHide)
|
||||
|
||||
readonly property int maskThickness: showing ? barThickness : 1
|
||||
|
||||
x: {
|
||||
if (!axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Left: return 0
|
||||
case SettingsData.Position.Right: return parent.width - maskThickness
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
y: {
|
||||
if (axis.isVertical) {
|
||||
return 0
|
||||
} else {
|
||||
switch (SettingsData.dankBarPosition) {
|
||||
case SettingsData.Position.Top: return 0
|
||||
case SettingsData.Position.Bottom: return parent.height - maskThickness
|
||||
default: return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
width: axis.isVertical ? maskThickness : parent.width
|
||||
height: axis.isVertical ? parent.height : maskThickness
|
||||
}
|
||||
|
||||
Region {
|
||||
id: mask
|
||||
item: inputMask
|
||||
}
|
||||
|
||||
property alias maskRegion: mask
|
||||
|
||||
QtObject {
|
||||
id: core
|
||||
|
||||
property real backgroundTransparency: SettingsData.dankBarTransparency
|
||||
property bool autoHide: SettingsData.dankBarAutoHide
|
||||
property bool revealSticky: false
|
||||
|
||||
property bool notepadInstanceVisible: notepadInstance?.isVisible ?? false
|
||||
|
||||
readonly property bool hasActivePopout: {
|
||||
const loaders = [{
|
||||
"loader": appDrawerLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": dankDashPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": processListPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": notificationCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": batteryPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": vpnPopoutLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": controlCenterLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}, {
|
||||
"loader": clipboardHistoryModalPopup,
|
||||
"prop": "visible"
|
||||
}, {
|
||||
"loader": systemUpdateLoader,
|
||||
"prop": "shouldBeVisible"
|
||||
}]
|
||||
return notepadInstanceVisible || loaders.some(item => {
|
||||
if (item.loader) {
|
||||
return item.loader?.item?.[item.prop]
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
property bool reveal: {
|
||||
if (CompositorService.isNiri && NiriService.inOverview) {
|
||||
return SettingsData.dankBarOpenOnOverview
|
||||
}
|
||||
return SettingsData.dankBarVisible && (!autoHide || topBarMouseArea.containsMouse || hasActivePopout || revealSticky)
|
||||
}
|
||||
|
||||
onHasActivePopoutChanged: {
|
||||
if (!hasActivePopout && autoHide && !topBarMouseArea.containsMouse) {
|
||||
revealSticky = true
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: revealHold
|
||||
interval: 250
|
||||
repeat: false
|
||||
onTriggered: core.revealSticky = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onDankBarTransparencyChanged() {
|
||||
core.backgroundTransparency = SettingsData.dankBarTransparency
|
||||
}
|
||||
|
||||
target: SettingsData
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: topBarMouseArea
|
||||
function onContainsMouseChanged() {
|
||||
if (topBarMouseArea.containsMouse) {
|
||||
core.revealSticky = true
|
||||
revealHold.stop()
|
||||
} else {
|
||||
if (core.autoHide && !core.hasActivePopout) {
|
||||
revealHold.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: topBarMouseArea
|
||||
y: !barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Bottom ? parent.height - height : 0) : 0
|
||||
x: barWindow.isVertical ? (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.width - width : 0) : 0
|
||||
height: !barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||
width: barWindow.isVertical ? barWindow.px(barWindow.effectiveBarThickness + SettingsData.dankBarSpacing) : undefined
|
||||
anchors {
|
||||
left: !barWindow.isVertical ? parent.left : (SettingsData.dankBarPosition === SettingsData.Position.Left ? parent.left : undefined)
|
||||
right: !barWindow.isVertical ? parent.right : (SettingsData.dankBarPosition === SettingsData.Position.Right ? parent.right : undefined)
|
||||
top: barWindow.isVertical ? parent.top : undefined
|
||||
bottom: barWindow.isVertical ? parent.bottom : undefined
|
||||
}
|
||||
hoverEnabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||
acceptedButtons: Qt.NoButton
|
||||
enabled: SettingsData.dankBarAutoHide && !core.reveal
|
||||
}
|
||||
}
|
||||
57
Modules/DankBar/AxisContext.qml
Normal file
57
Modules/DankBar/AxisContext.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
property string edge: "top"
|
||||
|
||||
readonly property string orientation: isVertical ? "vertical" : "horizontal"
|
||||
readonly property bool isVertical: edge === "left" || edge === "right"
|
||||
readonly property bool isHorizontal: !isVertical
|
||||
|
||||
function primarySize(item) {
|
||||
return isVertical ? item.height : item.width
|
||||
}
|
||||
|
||||
function crossSize(item) {
|
||||
return isVertical ? item.width : item.height
|
||||
}
|
||||
|
||||
function setPrimaryPos(item, value) {
|
||||
if (isVertical) {
|
||||
item.y = value
|
||||
} else {
|
||||
item.x = value
|
||||
}
|
||||
}
|
||||
|
||||
function getPrimaryPos(item) {
|
||||
return isVertical ? item.y : item.x
|
||||
}
|
||||
|
||||
function primaryAnchor(anchors) {
|
||||
return isVertical ? anchors.verticalCenter : anchors.horizontalCenter
|
||||
}
|
||||
|
||||
function crossAnchor(anchors) {
|
||||
return isVertical ? anchors.horizontalCenter : anchors.verticalCenter
|
||||
}
|
||||
|
||||
function outerVisualEdge() {
|
||||
if (edge === "bottom") return "bottom"
|
||||
if (edge === "left") return "right"
|
||||
if (edge === "right") return "left"
|
||||
if (edge === "top") return "top"
|
||||
return "bottom"
|
||||
}
|
||||
|
||||
signal axisEdgeChanged()
|
||||
signal axisOrientationChanged()
|
||||
signal changed() // Single coalesced signal
|
||||
|
||||
onEdgeChanged: {
|
||||
axisEdgeChanged()
|
||||
axisOrientationChanged()
|
||||
changed()
|
||||
}
|
||||
}
|
||||
210
Modules/DankBar/BarCanvas.qml
Normal file
210
Modules/DankBar/BarCanvas.qml
Normal file
@@ -0,0 +1,210 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property var barWindow
|
||||
required property var axis
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "right" ? barWindow._wingR : 0)
|
||||
anchors.rightMargin: -(SettingsData.dankBarGothCornersEnabled && axis.isVertical && axis.edge === "left" ? barWindow._wingR : 0)
|
||||
anchors.topMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "bottom" ? barWindow._wingR : 0)
|
||||
anchors.bottomMargin: -(SettingsData.dankBarGothCornersEnabled && !axis.isVertical && axis.edge === "top" ? barWindow._wingR : 0)
|
||||
|
||||
Canvas {
|
||||
id: barShape
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
renderTarget: Canvas.FramebufferObject
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
readonly property real correctWidth: root.width
|
||||
readonly property real correctHeight: root.height
|
||||
canvasSize: Qt.size(barWindow.px(correctWidth), barWindow.px(correctHeight))
|
||||
|
||||
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
|
||||
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
|
||||
|
||||
onWingChanged: requestPaint()
|
||||
onRtChanged: requestPaint()
|
||||
onCorrectWidthChanged: requestPaint()
|
||||
onCorrectHeightChanged: requestPaint()
|
||||
onVisibleChanged: if (visible) requestPaint()
|
||||
Component.onCompleted: requestPaint()
|
||||
|
||||
Connections {
|
||||
target: barWindow
|
||||
function on_BgColorChanged() { barShape.requestPaint() }
|
||||
function on_DprChanged() { barShape.requestPaint() }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Theme
|
||||
function onIsLightModeChanged() { barShape.requestPaint() }
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d")
|
||||
const scale = barWindow._dpr
|
||||
const W = barWindow.px(barWindow.isVertical ? correctHeight : correctWidth)
|
||||
const H_raw = barWindow.px(barWindow.isVertical ? correctWidth : correctHeight)
|
||||
const R = barWindow.px(wing)
|
||||
const RT = barWindow.px(rt)
|
||||
const H = H_raw - (R > 0 ? R : 0)
|
||||
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
|
||||
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
|
||||
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
|
||||
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
|
||||
|
||||
ctx.scale(scale, scale)
|
||||
|
||||
function drawTopPath() {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(RT, 0)
|
||||
ctx.lineTo(W - RT, 0)
|
||||
ctx.arcTo(W, 0, W, RT, RT)
|
||||
ctx.lineTo(W, H)
|
||||
|
||||
if (R > 0) {
|
||||
ctx.lineTo(W, H + R)
|
||||
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
|
||||
ctx.lineTo(R, H)
|
||||
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
|
||||
ctx.lineTo(0, H + R)
|
||||
} else {
|
||||
ctx.lineTo(W, H - RT)
|
||||
ctx.arcTo(W, H, W - RT, H, RT)
|
||||
ctx.lineTo(RT, H)
|
||||
ctx.arcTo(0, H, 0, H - RT, RT)
|
||||
}
|
||||
|
||||
ctx.lineTo(0, RT)
|
||||
ctx.arcTo(0, 0, RT, 0, RT)
|
||||
ctx.closePath()
|
||||
}
|
||||
|
||||
ctx.reset()
|
||||
ctx.clearRect(0, 0, W, H_raw)
|
||||
|
||||
ctx.save()
|
||||
if (isBottom) {
|
||||
ctx.translate(W, H_raw)
|
||||
ctx.rotate(Math.PI)
|
||||
} else if (isLeft) {
|
||||
ctx.translate(0, W)
|
||||
ctx.rotate(-Math.PI / 2)
|
||||
} else if (isRight) {
|
||||
ctx.translate(H_raw, 0)
|
||||
ctx.rotate(Math.PI / 2)
|
||||
}
|
||||
|
||||
drawTopPath()
|
||||
ctx.restore()
|
||||
|
||||
ctx.fillStyle = barWindow._bgColor
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: barTint
|
||||
anchors.fill: parent
|
||||
antialiasing: true
|
||||
renderTarget: Canvas.FramebufferObject
|
||||
renderStrategy: Canvas.Cooperative
|
||||
|
||||
readonly property real correctWidth: root.width
|
||||
readonly property real correctHeight: root.height
|
||||
canvasSize: Qt.size(barWindow.px(correctWidth), barWindow.px(correctHeight))
|
||||
|
||||
property real wing: SettingsData.dankBarGothCornersEnabled ? barWindow._wingR : 0
|
||||
property real rt: SettingsData.dankBarSquareCorners ? 0 : Theme.cornerRadius
|
||||
property real alphaTint: (barWindow._bgColor?.a ?? 1) < 0.99 ? (Theme.stateLayerOpacity ?? 0) : 0
|
||||
|
||||
onWingChanged: requestPaint()
|
||||
onRtChanged: requestPaint()
|
||||
onAlphaTintChanged: requestPaint()
|
||||
onCorrectWidthChanged: requestPaint()
|
||||
onCorrectHeightChanged: requestPaint()
|
||||
onVisibleChanged: if (visible) requestPaint()
|
||||
Component.onCompleted: requestPaint()
|
||||
|
||||
Connections {
|
||||
target: barWindow
|
||||
function on_BgColorChanged() { barTint.requestPaint() }
|
||||
function on_DprChanged() { barTint.requestPaint() }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Theme
|
||||
function onIsLightModeChanged() { barTint.requestPaint() }
|
||||
}
|
||||
|
||||
onPaint: {
|
||||
const ctx = getContext("2d")
|
||||
const scale = barWindow._dpr
|
||||
const W = barWindow.px(barWindow.isVertical ? correctHeight : correctWidth)
|
||||
const H_raw = barWindow.px(barWindow.isVertical ? correctWidth : correctHeight)
|
||||
const R = barWindow.px(wing)
|
||||
const RT = barWindow.px(rt)
|
||||
const H = H_raw - (R > 0 ? R : 0)
|
||||
const isTop = SettingsData.dankBarPosition === SettingsData.Position.Top
|
||||
const isBottom = SettingsData.dankBarPosition === SettingsData.Position.Bottom
|
||||
const isLeft = SettingsData.dankBarPosition === SettingsData.Position.Left
|
||||
const isRight = SettingsData.dankBarPosition === SettingsData.Position.Right
|
||||
|
||||
ctx.scale(scale, scale)
|
||||
|
||||
function drawTopPath() {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(RT, 0)
|
||||
ctx.lineTo(W - RT, 0)
|
||||
ctx.arcTo(W, 0, W, RT, RT)
|
||||
ctx.lineTo(W, H)
|
||||
|
||||
if (R > 0) {
|
||||
ctx.lineTo(W, H + R)
|
||||
ctx.arc(W - R, H + R, R, 0, -Math.PI / 2, true)
|
||||
ctx.lineTo(R, H)
|
||||
ctx.arc(R, H + R, R, -Math.PI / 2, -Math.PI, true)
|
||||
ctx.lineTo(0, H + R)
|
||||
} else {
|
||||
ctx.lineTo(W, H - RT)
|
||||
ctx.arcTo(W, H, W - RT, H, RT)
|
||||
ctx.lineTo(RT, H)
|
||||
ctx.arcTo(0, H, 0, H - RT, RT)
|
||||
}
|
||||
|
||||
ctx.lineTo(0, RT)
|
||||
ctx.arcTo(0, 0, RT, 0, RT)
|
||||
ctx.closePath()
|
||||
}
|
||||
|
||||
ctx.reset()
|
||||
ctx.clearRect(0, 0, W, H_raw)
|
||||
|
||||
ctx.save()
|
||||
if (isBottom) {
|
||||
ctx.translate(W, H_raw)
|
||||
ctx.rotate(Math.PI)
|
||||
} else if (isLeft) {
|
||||
ctx.translate(0, W)
|
||||
ctx.rotate(-Math.PI / 2)
|
||||
} else if (isRight) {
|
||||
ctx.translate(H_raw, 0)
|
||||
ctx.rotate(Math.PI / 2)
|
||||
}
|
||||
|
||||
drawTopPath()
|
||||
ctx.restore()
|
||||
|
||||
ctx.fillStyle = Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, alphaTint)
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
382
Modules/DankBar/CenterSection.qml
Normal file
382
Modules/DankBar/CenterSection.qml
Normal file
@@ -0,0 +1,382 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var widgetsModel: null
|
||||
property var components: null
|
||||
property bool noBackground: false
|
||||
required property var axis
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
readonly property real spacing: noBackground ? 2 : Theme.spacingXS
|
||||
|
||||
property var centerWidgets: []
|
||||
property int totalWidgets: 0
|
||||
property real totalSize: 0
|
||||
|
||||
function updateLayout() {
|
||||
const containerSize = isVertical ? height : width
|
||||
if (containerSize <= 0 || !visible) {
|
||||
return
|
||||
}
|
||||
|
||||
centerWidgets = []
|
||||
totalWidgets = 0
|
||||
totalSize = 0
|
||||
|
||||
let configuredWidgets = 0
|
||||
for (var i = 0; i < centerRepeater.count; i++) {
|
||||
const item = centerRepeater.itemAt(i)
|
||||
if (item && getWidgetVisible(item.widgetId)) {
|
||||
configuredWidgets++
|
||||
if (item.active && item.item) {
|
||||
centerWidgets.push(item.item)
|
||||
totalWidgets++
|
||||
totalSize += isVertical ? item.item.height : item.item.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalWidgets > 1) {
|
||||
totalSize += spacing * (totalWidgets - 1)
|
||||
}
|
||||
|
||||
positionWidgets(configuredWidgets)
|
||||
}
|
||||
|
||||
function positionWidgets(configuredWidgets) {
|
||||
if (totalWidgets === 0 || (isVertical ? height : width) <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const parentCenter = (isVertical ? height : width) / 2
|
||||
const isOdd = configuredWidgets % 2 === 1
|
||||
|
||||
centerWidgets.forEach(widget => {
|
||||
if (isVertical) {
|
||||
widget.anchors.verticalCenter = undefined
|
||||
} else {
|
||||
widget.anchors.horizontalCenter = undefined
|
||||
}
|
||||
})
|
||||
|
||||
if (isOdd) {
|
||||
const middleIndex = Math.floor(configuredWidgets / 2)
|
||||
let currentActiveIndex = 0
|
||||
let middleWidget = null
|
||||
|
||||
for (var i = 0; i < centerRepeater.count; i++) {
|
||||
const item = centerRepeater.itemAt(i)
|
||||
if (item && getWidgetVisible(item.widgetId)) {
|
||||
if (currentActiveIndex === middleIndex && item.active && item.item) {
|
||||
middleWidget = item.item
|
||||
break
|
||||
}
|
||||
currentActiveIndex++
|
||||
}
|
||||
}
|
||||
|
||||
if (middleWidget) {
|
||||
const middleSize = isVertical ? middleWidget.height : middleWidget.width
|
||||
if (isVertical) {
|
||||
middleWidget.y = parentCenter - (middleSize / 2)
|
||||
} else {
|
||||
middleWidget.x = parentCenter - (middleSize / 2)
|
||||
}
|
||||
|
||||
let leftWidgets = []
|
||||
let rightWidgets = []
|
||||
let foundMiddle = false
|
||||
|
||||
for (var i = 0; i < centerWidgets.length; i++) {
|
||||
if (centerWidgets[i] === middleWidget) {
|
||||
foundMiddle = true
|
||||
continue
|
||||
}
|
||||
if (!foundMiddle) {
|
||||
leftWidgets.push(centerWidgets[i])
|
||||
} else {
|
||||
rightWidgets.push(centerWidgets[i])
|
||||
}
|
||||
}
|
||||
|
||||
let currentPos = isVertical ? middleWidget.y : middleWidget.x
|
||||
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||
currentPos -= (spacing + size)
|
||||
if (isVertical) {
|
||||
leftWidgets[i].y = currentPos
|
||||
} else {
|
||||
leftWidgets[i].x = currentPos
|
||||
}
|
||||
}
|
||||
|
||||
currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize
|
||||
for (var i = 0; i < rightWidgets.length; i++) {
|
||||
currentPos += spacing
|
||||
if (isVertical) {
|
||||
rightWidgets[i].y = currentPos
|
||||
} else {
|
||||
rightWidgets[i].x = currentPos
|
||||
}
|
||||
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let configuredLeftIndex = (configuredWidgets / 2) - 1
|
||||
let configuredRightIndex = configuredWidgets / 2
|
||||
const halfSpacing = spacing / 2
|
||||
|
||||
let leftWidget = null
|
||||
let rightWidget = null
|
||||
let leftWidgets = []
|
||||
let rightWidgets = []
|
||||
|
||||
let currentConfigIndex = 0
|
||||
for (var i = 0; i < centerRepeater.count; i++) {
|
||||
const item = centerRepeater.itemAt(i)
|
||||
if (item && getWidgetVisible(item.widgetId)) {
|
||||
if (item.active && item.item) {
|
||||
if (currentConfigIndex < configuredLeftIndex) {
|
||||
leftWidgets.push(item.item)
|
||||
} else if (currentConfigIndex === configuredLeftIndex) {
|
||||
leftWidget = item.item
|
||||
} else if (currentConfigIndex === configuredRightIndex) {
|
||||
rightWidget = item.item
|
||||
} else {
|
||||
rightWidgets.push(item.item)
|
||||
}
|
||||
}
|
||||
currentConfigIndex++
|
||||
}
|
||||
}
|
||||
|
||||
if (leftWidget && rightWidget) {
|
||||
const leftSize = isVertical ? leftWidget.height : leftWidget.width
|
||||
if (isVertical) {
|
||||
leftWidget.y = parentCenter - halfSpacing - leftSize
|
||||
rightWidget.y = parentCenter + halfSpacing
|
||||
} else {
|
||||
leftWidget.x = parentCenter - halfSpacing - leftSize
|
||||
rightWidget.x = parentCenter + halfSpacing
|
||||
}
|
||||
|
||||
let currentPos = isVertical ? leftWidget.y : leftWidget.x
|
||||
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||
currentPos -= (spacing + size)
|
||||
if (isVertical) {
|
||||
leftWidgets[i].y = currentPos
|
||||
} else {
|
||||
leftWidgets[i].x = currentPos
|
||||
}
|
||||
}
|
||||
|
||||
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
|
||||
for (var i = 0; i < rightWidgets.length; i++) {
|
||||
currentPos += spacing
|
||||
if (isVertical) {
|
||||
rightWidgets[i].y = currentPos
|
||||
} else {
|
||||
rightWidgets[i].x = currentPos
|
||||
}
|
||||
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||
}
|
||||
} else if (leftWidget && !rightWidget) {
|
||||
const leftSize = isVertical ? leftWidget.height : leftWidget.width
|
||||
if (isVertical) {
|
||||
leftWidget.y = parentCenter - halfSpacing - leftSize
|
||||
} else {
|
||||
leftWidget.x = parentCenter - halfSpacing - leftSize
|
||||
}
|
||||
|
||||
let currentPos = isVertical ? leftWidget.y : leftWidget.x
|
||||
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||
currentPos -= (spacing + size)
|
||||
if (isVertical) {
|
||||
leftWidgets[i].y = currentPos
|
||||
} else {
|
||||
leftWidgets[i].x = currentPos
|
||||
}
|
||||
}
|
||||
|
||||
currentPos = (isVertical ? leftWidget.y + leftWidget.height : leftWidget.x + leftWidget.width) + spacing
|
||||
for (var i = 0; i < rightWidgets.length; i++) {
|
||||
currentPos += spacing
|
||||
if (isVertical) {
|
||||
rightWidgets[i].y = currentPos
|
||||
} else {
|
||||
rightWidgets[i].x = currentPos
|
||||
}
|
||||
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||
}
|
||||
} else if (!leftWidget && rightWidget) {
|
||||
if (isVertical) {
|
||||
rightWidget.y = parentCenter + halfSpacing
|
||||
} else {
|
||||
rightWidget.x = parentCenter + halfSpacing
|
||||
}
|
||||
|
||||
let currentPos = (isVertical ? rightWidget.y : rightWidget.x) - spacing
|
||||
for (var i = leftWidgets.length - 1; i >= 0; i--) {
|
||||
const size = isVertical ? leftWidgets[i].height : leftWidgets[i].width
|
||||
currentPos -= size
|
||||
if (isVertical) {
|
||||
leftWidgets[i].y = currentPos
|
||||
} else {
|
||||
leftWidgets[i].x = currentPos
|
||||
}
|
||||
currentPos -= spacing
|
||||
}
|
||||
|
||||
currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width)
|
||||
for (var i = 0; i < rightWidgets.length; i++) {
|
||||
currentPos += spacing
|
||||
if (isVertical) {
|
||||
rightWidgets[i].y = currentPos
|
||||
} else {
|
||||
rightWidgets[i].x = currentPos
|
||||
}
|
||||
currentPos += isVertical ? rightWidgets[i].height : rightWidgets[i].width
|
||||
}
|
||||
} else if (totalWidgets === 1 && centerWidgets[0]) {
|
||||
const size = isVertical ? centerWidgets[0].height : centerWidgets[0].width
|
||||
if (isVertical) {
|
||||
centerWidgets[0].y = parentCenter - (size / 2)
|
||||
} else {
|
||||
centerWidgets[0].x = parentCenter - (size / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWidgetVisible(widgetId) {
|
||||
const widgetVisibility = {
|
||||
"cpuUsage": DgopService.dgopAvailable,
|
||||
"memUsage": DgopService.dgopAvailable,
|
||||
"cpuTemp": DgopService.dgopAvailable,
|
||||
"gpuTemp": DgopService.dgopAvailable,
|
||||
"network_speed_monitor": DgopService.dgopAvailable
|
||||
}
|
||||
return widgetVisibility[widgetId] ?? true
|
||||
}
|
||||
|
||||
function getWidgetComponent(widgetId) {
|
||||
const componentMap = {
|
||||
"launcherButton": "launcherButtonComponent",
|
||||
"workspaceSwitcher": "workspaceSwitcherComponent",
|
||||
"focusedWindow": "focusedWindowComponent",
|
||||
"runningApps": "runningAppsComponent",
|
||||
"clock": "clockComponent",
|
||||
"music": "mediaComponent",
|
||||
"weather": "weatherComponent",
|
||||
"systemTray": "systemTrayComponent",
|
||||
"privacyIndicator": "privacyIndicatorComponent",
|
||||
"clipboard": "clipboardComponent",
|
||||
"cpuUsage": "cpuUsageComponent",
|
||||
"memUsage": "memUsageComponent",
|
||||
"diskUsage": "diskUsageComponent",
|
||||
"cpuTemp": "cpuTempComponent",
|
||||
"gpuTemp": "gpuTempComponent",
|
||||
"notificationButton": "notificationButtonComponent",
|
||||
"battery": "batteryComponent",
|
||||
"controlCenterButton": "controlCenterButtonComponent",
|
||||
"idleInhibitor": "idleInhibitorComponent",
|
||||
"spacer": "spacerComponent",
|
||||
"separator": "separatorComponent",
|
||||
"network_speed_monitor": "networkComponent",
|
||||
"keyboard_layout_name": "keyboardLayoutNameComponent",
|
||||
"vpn": "vpnComponent",
|
||||
"notepadButton": "notepadButtonComponent",
|
||||
"colorPicker": "colorPickerComponent",
|
||||
"systemUpdate": "systemUpdateComponent"
|
||||
}
|
||||
|
||||
const componentKey = componentMap[widgetId]
|
||||
return componentKey ? root.components[componentKey] : null
|
||||
}
|
||||
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
anchors.centerIn: parent
|
||||
|
||||
Timer {
|
||||
id: layoutTimer
|
||||
interval: 0
|
||||
repeat: false
|
||||
onTriggered: root.updateLayout()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
|
||||
onWidthChanged: {
|
||||
if (width > 0) {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
onHeightChanged: {
|
||||
if (height > 0) {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible && (isVertical ? height : width) > 0) {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: centerRepeater
|
||||
model: root.widgetsModel
|
||||
|
||||
Loader {
|
||||
property string widgetId: model.widgetId
|
||||
property var widgetData: model
|
||||
property int spacerSize: model.size || 20
|
||||
|
||||
anchors.verticalCenter: !root.isVertical ? parent.verticalCenter : undefined
|
||||
anchors.horizontalCenter: root.isVertical ? parent.horizontalCenter : undefined
|
||||
active: root.getWidgetVisible(model.widgetId) && (model.widgetId !== "music" || MprisController.activePlayer !== null)
|
||||
sourceComponent: root.getWidgetComponent(model.widgetId)
|
||||
opacity: (model.enabled !== false) ? 1 : 0
|
||||
asynchronous: false
|
||||
|
||||
onLoaded: {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
item.widthChanged.connect(() => layoutTimer.restart())
|
||||
item.heightChanged.connect(() => layoutTimer.restart())
|
||||
if (model.widgetId === "spacer") {
|
||||
item.spacerSize = Qt.binding(() => model.size || 20)
|
||||
}
|
||||
if (root.axis && "axis" in item) {
|
||||
item.axis = root.axis
|
||||
}
|
||||
if (root.axis && "isVertical" in item) {
|
||||
item.isVertical = root.axis.isVertical
|
||||
}
|
||||
layoutTimer.restart()
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: widgetsModel
|
||||
function onCountChanged() {
|
||||
layoutTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
1026
Modules/DankBar/DankBar.qml
Normal file
1026
Modules/DankBar/DankBar.qml
Normal file
File diff suppressed because it is too large
Load Diff
71
Modules/DankBar/LeftSection.qml
Normal file
71
Modules/DankBar/LeftSection.qml
Normal file
@@ -0,0 +1,71 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var widgetsModel: null
|
||||
property var components: null
|
||||
property bool noBackground: false
|
||||
required property var axis
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
|
||||
implicitHeight: layoutLoader.item ? (layoutLoader.item.implicitHeight || layoutLoader.item.height) : 0
|
||||
implicitWidth: layoutLoader.item ? (layoutLoader.item.implicitWidth || layoutLoader.item.width) : 0
|
||||
|
||||
Loader {
|
||||
id: layoutLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: root.isVertical ? columnComp : rowComp
|
||||
}
|
||||
|
||||
Component {
|
||||
id: rowComp
|
||||
Row {
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
Repeater {
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
id: widgetLoader
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
widgetId: model.widgetId
|
||||
widgetData: model
|
||||
spacerSize: model.size || 20
|
||||
components: root.components
|
||||
isInColumn: false
|
||||
axis: root.axis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: columnComp
|
||||
Column {
|
||||
width: Math.max(parent.width, 200)
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
Repeater {
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
width: parent.width
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
id: widgetLoader
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
widgetId: model.widgetId
|
||||
widgetData: model
|
||||
spacerSize: model.size || 20
|
||||
components: root.components
|
||||
isInColumn: true
|
||||
axis: root.axis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
439
Modules/DankBar/Popouts/BatteryPopout.qml
Normal file
439
Modules/DankBar/Popouts/BatteryPopout.qml
Normal file
@@ -0,0 +1,439 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.UPower
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankPopout {
|
||||
id: root
|
||||
|
||||
property var triggerScreen: null
|
||||
|
||||
function setTriggerPosition(x, y, width, section, screen) {
|
||||
triggerX = x;
|
||||
triggerY = y;
|
||||
triggerWidth = width;
|
||||
triggerSection = section;
|
||||
triggerScreen = screen;
|
||||
}
|
||||
|
||||
function isActiveProfile(profile) {
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PowerProfiles.profile === profile;
|
||||
}
|
||||
|
||||
function setProfile(profile) {
|
||||
if (typeof PowerProfiles === "undefined") {
|
||||
ToastService.showError("power-profiles-daemon not available");
|
||||
return ;
|
||||
}
|
||||
PowerProfiles.profile = profile;
|
||||
if (PowerProfiles.profile !== profile) {
|
||||
ToastService.showError("Failed to set power profile");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
popupWidth: 400
|
||||
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
|
||||
triggerX: Screen.width - 380 - Theme.spacingL
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||
triggerWidth: 70
|
||||
positioning: ""
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
visible: shouldBeVisible
|
||||
|
||||
content: Component {
|
||||
Rectangle {
|
||||
id: batteryContent
|
||||
|
||||
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: 0
|
||||
antialiasing: true
|
||||
smooth: true
|
||||
focus: true
|
||||
Component.onCompleted: {
|
||||
if (root.shouldBeVisible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
|
||||
}
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
root.close();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onShouldBeVisibleChanged() {
|
||||
if (root.shouldBeVisible) {
|
||||
Qt.callLater(function() {
|
||||
batteryContent.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
target: root
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -3
|
||||
color: "transparent"
|
||||
radius: parent.radius + 3
|
||||
border.color: Qt.rgba(0, 0, 0, 0.05)
|
||||
border.width: 0
|
||||
z: -3
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -2
|
||||
color: "transparent"
|
||||
radius: parent.radius + 2
|
||||
border.color: Theme.shadowMedium
|
||||
border.width: 0
|
||||
z: -2
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: Theme.outlineStrong
|
||||
border.width: 0
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
height: 48
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: {
|
||||
if (!BatteryService.batteryAvailable)
|
||||
return "power";
|
||||
|
||||
if (!BatteryService.isCharging && BatteryService.isPluggedIn) {
|
||||
if (BatteryService.batteryLevel >= 90) {
|
||||
return "battery_charging_full";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 80) {
|
||||
return "battery_charging_90";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 60) {
|
||||
return "battery_charging_80";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 50) {
|
||||
return "battery_charging_60";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 30) {
|
||||
return "battery_charging_50";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 20) {
|
||||
return "battery_charging_30";
|
||||
}
|
||||
return "battery_charging_20";
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
if (BatteryService.batteryLevel >= 90) {
|
||||
return "battery_charging_full";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 80) {
|
||||
return "battery_charging_90";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 60) {
|
||||
return "battery_charging_80";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 50) {
|
||||
return "battery_charging_60";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 30) {
|
||||
return "battery_charging_50";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 20) {
|
||||
return "battery_charging_30";
|
||||
}
|
||||
return "battery_charging_20";
|
||||
} else {
|
||||
if (BatteryService.batteryLevel >= 95) {
|
||||
return "battery_full";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 85) {
|
||||
return "battery_6_bar";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 70) {
|
||||
return "battery_5_bar";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 55) {
|
||||
return "battery_4_bar";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 40) {
|
||||
return "battery_3_bar";
|
||||
}
|
||||
if (BatteryService.batteryLevel >= 25) {
|
||||
return "battery_2_bar";
|
||||
}
|
||||
return "battery_1_bar";
|
||||
}
|
||||
}
|
||||
size: Theme.iconSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||
return Theme.error;
|
||||
if (BatteryService.isCharging || BatteryService.isPluggedIn)
|
||||
return Theme.primary;
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : "Power"
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error;
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
return Theme.primary;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryAvailable ? BatteryService.batteryStatus : "Management"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error;
|
||||
}
|
||||
if (BatteryService.isCharging) {
|
||||
return Theme.primary;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!BatteryService.batteryAvailable) return "Power profile management available"
|
||||
const time = BatteryService.formatTimeRemaining();
|
||||
if (time !== "Unknown") {
|
||||
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceTextMedium
|
||||
visible: text.length > 0
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: closeBatteryArea.containsMouse ? Theme.errorHover : "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "close"
|
||||
size: Theme.iconSize - 4
|
||||
color: closeBatteryArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeBatteryArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
visible: BatteryService.batteryAvailable
|
||||
|
||||
StyledRect {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: 64
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Health"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryHealth
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: {
|
||||
if (BatteryService.batteryHealth === "N/A") {
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
const healthNum = parseInt(BatteryService.batteryHealth);
|
||||
return healthNum < 80 ? Theme.error : Theme.surfaceText;
|
||||
}
|
||||
font.weight: Font.Bold
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
height: 64
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceContainerHigh
|
||||
border.width: 0
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: "Capacity"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryCapacity > 0 ? `${BatteryService.batteryCapacity.toFixed(1)} Wh` : "Unknown"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Bold
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
property var profileModel: (typeof PowerProfiles !== "undefined") ? [PowerProfile.PowerSaver, PowerProfile.Balanced].concat(PowerProfiles.hasPerformanceProfile ? [PowerProfile.Performance] : []) : [PowerProfile.PowerSaver, PowerProfile.Balanced, PowerProfile.Performance]
|
||||
property int currentProfileIndex: {
|
||||
if (typeof PowerProfiles === "undefined") return 1
|
||||
return profileModel.findIndex(profile => root.isActiveProfile(profile))
|
||||
}
|
||||
|
||||
model: profileModel.map(profile => Theme.getPowerProfileLabel(profile))
|
||||
currentIndex: currentProfileIndex
|
||||
selectionMode: "single"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected) return
|
||||
root.setProfile(profileModel[index])
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
width: parent.width
|
||||
height: degradationContent.implicitHeight + Theme.spacingL * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
border.color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3)
|
||||
border.width: 0
|
||||
visible: (typeof PowerProfiles !== "undefined") && PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
||||
|
||||
Column {
|
||||
id: degradationContent
|
||||
width: parent.width - Theme.spacingL * 2
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "warning"
|
||||
size: Theme.iconSize
|
||||
color: Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
spacing: Theme.spacingXS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - Theme.iconSize - Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: "Power Profile Degradation"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.error
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: (typeof PowerProfiles !== "undefined") ? PerformanceDegradationReason.toString(PowerProfiles.degradationReason) : ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.8)
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import qs.Widgets
|
||||
DankPopout {
|
||||
id: root
|
||||
|
||||
property string triggerSection: "right"
|
||||
property var triggerScreen: null
|
||||
|
||||
function setTriggerPosition(x, y, width, section, screen) {
|
||||
@@ -27,9 +26,9 @@ DankPopout {
|
||||
popupWidth: 360
|
||||
popupHeight: Math.min(Screen.height - 100, contentLoader.item ? contentLoader.item.implicitHeight : 260)
|
||||
triggerX: Screen.width - 380 - Theme.spacingL
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.spacingS
|
||||
triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
|
||||
triggerWidth: 70
|
||||
positioning: "center"
|
||||
positioning: ""
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
visible: shouldBeVisible
|
||||
@@ -42,7 +41,7 @@ DankPopout {
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadius
|
||||
border.color: Theme.outlineMedium
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
antialiasing: true
|
||||
smooth: true
|
||||
focus: true
|
||||
@@ -60,7 +59,7 @@ DankPopout {
|
||||
color: "transparent"
|
||||
radius: parent.radius + 3
|
||||
border.color: Qt.rgba(0, 0, 0, 0.05)
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
z: -3
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ DankPopout {
|
||||
color: "transparent"
|
||||
radius: parent.radius + 2
|
||||
border.color: Theme.shadowMedium
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
z: -2
|
||||
}
|
||||
|
||||
@@ -78,7 +77,7 @@ DankPopout {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: Theme.outlineStrong
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
radius: parent.radius
|
||||
z: -1
|
||||
}
|
||||
@@ -142,7 +141,7 @@ DankPopout {
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, Theme.getContentBackgroundAlpha() * 0.6)
|
||||
border.color: Theme.outlineStrong
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
@@ -193,7 +192,7 @@ DankPopout {
|
||||
visible: VpnService.connected
|
||||
width: 130
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
|
||||
border.width: 1
|
||||
border.width: 0
|
||||
border.color: Theme.outlineLight
|
||||
|
||||
Row {
|
||||
73
Modules/DankBar/RightSection.qml
Normal file
73
Modules/DankBar/RightSection.qml
Normal file
@@ -0,0 +1,73 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var widgetsModel: null
|
||||
property var components: null
|
||||
property bool noBackground: false
|
||||
required property var axis
|
||||
|
||||
readonly property bool isVertical: axis?.isVertical ?? false
|
||||
|
||||
implicitHeight: layoutLoader.item ? layoutLoader.item.implicitHeight : 0
|
||||
implicitWidth: layoutLoader.item ? layoutLoader.item.implicitWidth : 0
|
||||
|
||||
Loader {
|
||||
id: layoutLoader
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
sourceComponent: root.isVertical ? columnComp : rowComp
|
||||
}
|
||||
|
||||
Component {
|
||||
id: rowComp
|
||||
Row {
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
anchors.right: parent ? parent.right : undefined
|
||||
Repeater {
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
width: widgetLoader.item ? widgetLoader.item.width : 0
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
id: widgetLoader
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
widgetId: model.widgetId
|
||||
widgetData: model
|
||||
spacerSize: model.size || 20
|
||||
components: root.components
|
||||
isInColumn: false
|
||||
axis: root.axis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: columnComp
|
||||
Column {
|
||||
width: parent ? parent.width : 0
|
||||
spacing: noBackground ? 2 : Theme.spacingXS
|
||||
Repeater {
|
||||
model: root.widgetsModel
|
||||
Item {
|
||||
width: parent.width
|
||||
height: widgetLoader.item ? widgetLoader.item.height : 0
|
||||
WidgetHost {
|
||||
id: widgetLoader
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
widgetId: model.widgetId
|
||||
widgetData: model
|
||||
spacerSize: model.size || 20
|
||||
components: root.components
|
||||
isInColumn: true
|
||||
axis: root.axis
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Modules/DankBar/WidgetHost.qml
Normal file
88
Modules/DankBar/WidgetHost.qml
Normal file
@@ -0,0 +1,88 @@
|
||||
import QtQuick
|
||||
import Quickshell.Services.Mpris
|
||||
import qs.Services
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
property string widgetId: ""
|
||||
property var widgetData: null
|
||||
property int spacerSize: 20
|
||||
property var components: null
|
||||
property bool isInColumn: false
|
||||
property var axis: null
|
||||
|
||||
asynchronous: false
|
||||
|
||||
active: getWidgetVisible(widgetId, DgopService.dgopAvailable) &&
|
||||
(widgetId !== "music" || MprisController.activePlayer !== null)
|
||||
sourceComponent: getWidgetComponent(widgetId, components)
|
||||
opacity: getWidgetEnabled(widgetData?.enabled) ? 1 : 0
|
||||
|
||||
signal contentItemReady(var item)
|
||||
|
||||
onLoaded: {
|
||||
if (item) {
|
||||
contentItemReady(item)
|
||||
if (widgetId === "spacer") {
|
||||
item.spacerSize = Qt.binding(() => spacerSize)
|
||||
}
|
||||
if (axis && "axis" in item) {
|
||||
item.axis = axis
|
||||
}
|
||||
if (axis && "isVertical" in item) {
|
||||
item.isVertical = axis.isVertical
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWidgetComponent(widgetId, components) {
|
||||
const componentMap = {
|
||||
"launcherButton": components.launcherButtonComponent,
|
||||
"workspaceSwitcher": components.workspaceSwitcherComponent,
|
||||
"focusedWindow": components.focusedWindowComponent,
|
||||
"runningApps": components.runningAppsComponent,
|
||||
"clock": components.clockComponent,
|
||||
"music": components.mediaComponent,
|
||||
"weather": components.weatherComponent,
|
||||
"systemTray": components.systemTrayComponent,
|
||||
"privacyIndicator": components.privacyIndicatorComponent,
|
||||
"clipboard": components.clipboardComponent,
|
||||
"cpuUsage": components.cpuUsageComponent,
|
||||
"memUsage": components.memUsageComponent,
|
||||
"diskUsage": components.diskUsageComponent,
|
||||
"cpuTemp": components.cpuTempComponent,
|
||||
"gpuTemp": components.gpuTempComponent,
|
||||
"notificationButton": components.notificationButtonComponent,
|
||||
"battery": components.batteryComponent,
|
||||
"controlCenterButton": components.controlCenterButtonComponent,
|
||||
"idleInhibitor": components.idleInhibitorComponent,
|
||||
"spacer": components.spacerComponent,
|
||||
"separator": components.separatorComponent,
|
||||
"network_speed_monitor": components.networkComponent,
|
||||
"keyboard_layout_name": components.keyboardLayoutNameComponent,
|
||||
"vpn": components.vpnComponent,
|
||||
"notepadButton": components.notepadButtonComponent,
|
||||
"colorPicker": components.colorPickerComponent,
|
||||
"systemUpdate": components.systemUpdateComponent
|
||||
}
|
||||
|
||||
return componentMap[widgetId] || null
|
||||
}
|
||||
|
||||
function getWidgetVisible(widgetId, dgopAvailable) {
|
||||
const widgetVisibility = {
|
||||
"cpuUsage": dgopAvailable,
|
||||
"memUsage": dgopAvailable,
|
||||
"cpuTemp": dgopAvailable,
|
||||
"gpuTemp": dgopAvailable,
|
||||
"network_speed_monitor": dgopAvailable
|
||||
}
|
||||
|
||||
return widgetVisibility[widgetId] ?? true
|
||||
}
|
||||
|
||||
function getWidgetEnabled(enabled) {
|
||||
return enabled !== false
|
||||
}
|
||||
}
|
||||
126
Modules/DankBar/Widgets/Battery.qml
Normal file
126
Modules/DankBar/Widgets/Battery.qml
Normal file
@@ -0,0 +1,126 @@
|
||||
import QtQuick
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: battery
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool batteryPopupVisible: false
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal toggleBatteryPopup()
|
||||
|
||||
width: isVertical ? widgetThickness : (batteryContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (batteryColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = batteryArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
visible: true
|
||||
|
||||
Column {
|
||||
id: batteryColumn
|
||||
visible: battery.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: BatteryService.getBatteryIcon()
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (!BatteryService.batteryAvailable) {
|
||||
return Theme.surfaceText
|
||||
}
|
||||
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error
|
||||
}
|
||||
|
||||
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||
return Theme.primary
|
||||
}
|
||||
|
||||
return Theme.surfaceText
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: BatteryService.batteryLevel.toString()
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: batteryContent
|
||||
visible: !battery.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: SettingsData.dankBarNoBackground ? 1 : 2
|
||||
|
||||
DankIcon {
|
||||
name: BatteryService.getBatteryIcon()
|
||||
size: Theme.iconSize - 6
|
||||
color: {
|
||||
if (!BatteryService.batteryAvailable) {
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
|
||||
return Theme.error;
|
||||
}
|
||||
|
||||
if (BatteryService.isCharging || BatteryService.isPluggedIn) {
|
||||
return Theme.primary;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: `${BatteryService.batteryLevel}%`
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BatteryService.batteryAvailable
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: batteryArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
toggleBatteryPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Modules/DankBar/Widgets/ClipboardButton.qml
Normal file
57
Modules/DankBar/Widgets/ClipboardButton.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool isActive: false
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "right"
|
||||
property var clipboardHistoryModal: null
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: widgetThickness
|
||||
height: widgetThickness
|
||||
|
||||
MouseArea {
|
||||
id: clipboardArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: clipboardContent
|
||||
|
||||
anchors.fill: parent
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
const baseColor = clipboardArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: "content_paste"
|
||||
size: widgetThickness - 8
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
267
Modules/DankBar/Widgets/Clock.qml
Normal file
267
Modules/DankBar/Widgets/Clock.qml
Normal file
@@ -0,0 +1,267 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool compactMode: false
|
||||
property string section: "center"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||
|
||||
signal clockClicked
|
||||
|
||||
width: isVertical ? widgetThickness : (clockRow.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (clockColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = clockMouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
Column {
|
||||
id: clockColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: -2
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.use24HourClock) {
|
||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0)
|
||||
} else {
|
||||
const hours = systemClock?.date?.getHours()
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
||||
return String(display).padStart(2, '0').charAt(0)
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.use24HourClock) {
|
||||
return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1)
|
||||
} else {
|
||||
const hours = systemClock?.date?.getHours()
|
||||
const display = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours
|
||||
return String(display).padStart(2, '0').charAt(1)
|
||||
}
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.primary
|
||||
font.weight: Font.Normal
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 12
|
||||
height: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Rectangle {
|
||||
width: 12
|
||||
height: 1
|
||||
color: Theme.outlineButton
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
||||
return value.charAt(0)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
return dayFirst ? Font.Normal : Font.Light
|
||||
}
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0')
|
||||
return value.charAt(1)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
return dayFirst ? Font.Normal : Font.Light
|
||||
}
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
||||
return value.charAt(0)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
return dayFirst ? Font.Light : Font.Normal
|
||||
}
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
const value = dayFirst ? String(systemClock?.date?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0')
|
||||
return value.charAt(1)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: {
|
||||
const locale = Qt.locale()
|
||||
const dateFormatShort = locale.dateFormat(Locale.ShortFormat)
|
||||
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M')
|
||||
return dayFirst ? Font.Light : Font.Normal
|
||||
}
|
||||
width: 9
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: clockRow
|
||||
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const format = SettingsData.use24HourClock ? "HH:mm" : "h:mm AP"
|
||||
return systemClock?.date?.toLocaleTimeString(Qt.locale(), format)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium - 1
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "•"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outlineButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !SettingsData.clockCompactMode
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (SettingsData.clockDateFormat && SettingsData.clockDateFormat.length > 0) {
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), SettingsData.clockDateFormat)
|
||||
}
|
||||
|
||||
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d")
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium - 1
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !SettingsData.clockCompactMode
|
||||
}
|
||||
}
|
||||
|
||||
SystemClock {
|
||||
id: systemClock
|
||||
precision: SystemClock.Seconds
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clockMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
root.clockClicked()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
54
Modules/DankBar/Widgets/ColorPicker.qml
Normal file
54
Modules/DankBar/Widgets/ColorPicker.qml
Normal file
@@ -0,0 +1,54 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool isActive: false
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: isVertical ? widgetThickness : (colorPickerIcon.width + horizontalPadding * 2)
|
||||
height: isVertical ? (colorPickerIcon.height + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = colorPickerArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: colorPickerIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
name: "palette"
|
||||
size: Theme.iconSize - 6
|
||||
color: colorPickerArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: colorPickerArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
root.colorPickerRequested();
|
||||
}
|
||||
}
|
||||
|
||||
signal colorPickerRequested()
|
||||
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool isActive: false
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
@@ -14,27 +16,124 @@ Rectangle {
|
||||
property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon
|
||||
property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon
|
||||
property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon
|
||||
property real widgetHeight: 30
|
||||
property real barHeight: 48
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: controlIndicators.implicitWidth + horizontalPadding * 2
|
||||
height: widgetHeight
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
width: isVertical ? widgetThickness : (controlIndicators.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (controlColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = controlCenterArea.containsMouse || root.isActive ? Theme.primaryPressed : Theme.secondaryHover;
|
||||
const baseColor = controlCenterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
Column {
|
||||
id: controlColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return "sync"
|
||||
}
|
||||
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return "lan"
|
||||
}
|
||||
|
||||
return NetworkService.wifiSignalIcon
|
||||
}
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (NetworkService.wifiToggling) {
|
||||
return Theme.primary
|
||||
}
|
||||
|
||||
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: root.showNetworkIcon
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "bluetooth"
|
||||
size: Theme.iconSize - 8
|
||||
color: BluetoothService.enabled ? Theme.primary : Theme.outlineButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: audioIconV.implicitWidth + 4
|
||||
height: audioIconV.implicitHeight + 4
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: root.showAudioIcon
|
||||
|
||||
DankIcon {
|
||||
id: audioIconV
|
||||
|
||||
name: {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
|
||||
return "volume_off"
|
||||
} else if (AudioService.sink.audio.volume * 100 < 33) {
|
||||
return "volume_down"
|
||||
} else {
|
||||
return "volume_up"
|
||||
}
|
||||
}
|
||||
return "volume_up"
|
||||
}
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onWheel: function(wheelEvent) {
|
||||
let delta = wheelEvent.angleDelta.y
|
||||
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0
|
||||
let newVolume
|
||||
if (delta > 0) {
|
||||
newVolume = Math.min(100, currentVolume + 5)
|
||||
} else {
|
||||
newVolume = Math.max(0, currentVolume - 5)
|
||||
}
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = false
|
||||
AudioService.sink.audio.volume = newVolume / 100
|
||||
AudioService.volumeChanged()
|
||||
}
|
||||
wheelEvent.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "settings"
|
||||
size: Theme.iconSize - 8
|
||||
color: controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: controlIndicators
|
||||
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
@@ -63,13 +162,6 @@ Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: root.showNetworkIcon
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: NetworkService.wifiToggling
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1000
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -163,22 +255,14 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const screenX = currentScreen.x || 0;
|
||||
const relativeX = globalPos.x - screenX;
|
||||
popupTarget.setTriggerPosition(relativeX, barHeight + Theme.spacingXS, width, section, currentScreen);
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
156
Modules/DankBar/Widgets/CpuMonitor.qml
Normal file
156
Modules/DankBar/Widgets/CpuMonitor.qml
Normal file
@@ -0,0 +1,156 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool showPercentage: true
|
||||
property bool showIcon: true
|
||||
property var toggleProcessList
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (cpuColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = cpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["cpu"]);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["cpu"]);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cpuArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
DgopService.setSortBy("cpu");
|
||||
if (root.toggleProcessList) {
|
||||
root.toggleProcessList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: cpuColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: "memory"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.cpuUsage > 80) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.cpuUsage > 60) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
||||
return "--";
|
||||
}
|
||||
|
||||
return DgopService.cpuUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: cpuContent
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
DankIcon {
|
||||
name: "memory"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.cpuUsage > 80) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.cpuUsage > 60) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) {
|
||||
return "--%";
|
||||
}
|
||||
|
||||
return DgopService.cpuUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
|
||||
StyledTextMetrics {
|
||||
id: cpuBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
width: Math.max(cpuBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
157
Modules/DankBar/Widgets/CpuTemperature.qml
Normal file
157
Modules/DankBar/Widgets/CpuTemperature.qml
Normal file
@@ -0,0 +1,157 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool showPercentage: true
|
||||
property bool showIcon: true
|
||||
property var toggleProcessList
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (cpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = cpuTempArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["cpu"]);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["cpu"]);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: cpuTempArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
DgopService.setSortBy("cpu");
|
||||
if (root.toggleProcessList) {
|
||||
root.toggleProcessList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: cpuTempColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: "memory"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.cpuTemperature > 85) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.cpuTemperature > 69) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
||||
return "--";
|
||||
}
|
||||
|
||||
return Math.round(DgopService.cpuTemperature).toString();
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: cpuTempContent
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
DankIcon {
|
||||
name: "memory"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.cpuTemperature > 85) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.cpuTemperature > 69) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) {
|
||||
return "--°";
|
||||
}
|
||||
|
||||
return Math.round(DgopService.cpuTemperature) + "°";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
|
||||
StyledTextMetrics {
|
||||
id: tempBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
width: Math.max(tempBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
241
Modules/DankBar/Widgets/DiskUsage.qml
Normal file
241
Modules/DankBar/Widgets/DiskUsage.qml
Normal file
@@ -0,0 +1,241 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property var widgetData: null
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
property var selectedMount: {
|
||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Force re-evaluation when mountPath changes
|
||||
const currentMountPath = root.mountPath || "/"
|
||||
|
||||
// First try to find exact match
|
||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
||||
return DgopService.diskMounts[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to root
|
||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||
if (DgopService.diskMounts[i].mount === "/") {
|
||||
return DgopService.diskMounts[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort - first mount
|
||||
return DgopService.diskMounts[0] || null
|
||||
}
|
||||
|
||||
property real diskUsagePercent: {
|
||||
if (!selectedMount || !selectedMount.percent) {
|
||||
return 0
|
||||
}
|
||||
const percentStr = selectedMount.percent.replace("%", "")
|
||||
return parseFloat(percentStr) || 0
|
||||
}
|
||||
|
||||
width: isVertical ? widgetThickness : (diskContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (diskColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
const baseColor = Theme.widgetBaseBackgroundColor
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["diskmounts"])
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["diskmounts"])
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onWidgetDataChanged() {
|
||||
// Force property re-evaluation by triggering change detection
|
||||
root.mountPath = Qt.binding(() => {
|
||||
return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/"
|
||||
})
|
||||
|
||||
root.selectedMount = Qt.binding(() => {
|
||||
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const currentMountPath = root.mountPath || "/"
|
||||
|
||||
// First try to find exact match
|
||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||
if (DgopService.diskMounts[i].mount === currentMountPath) {
|
||||
return DgopService.diskMounts[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to root
|
||||
for (let i = 0; i < DgopService.diskMounts.length; i++) {
|
||||
if (DgopService.diskMounts[i].mount === "/") {
|
||||
return DgopService.diskMounts[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort - first mount
|
||||
return DgopService.diskMounts[0] || null
|
||||
})
|
||||
}
|
||||
|
||||
target: SettingsData
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: diskArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: root.isVertical
|
||||
onEntered: {
|
||||
if (root.isVertical && root.selectedMount) {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
const globalPos = mapToGlobal(width / 2, height / 2)
|
||||
const currentScreen = root.parentScreen || Screen
|
||||
const screenX = currentScreen ? currentScreen.x : 0
|
||||
const screenY = currentScreen ? currentScreen.y : 0
|
||||
const relativeY = globalPos.y - screenY
|
||||
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS) : (currentScreen.width - Theme.barHeight - SettingsData.dankBarSpacing - Theme.spacingXS)
|
||||
const isLeft = root.axis?.edge === "left"
|
||||
tooltipLoader.item.show(root.selectedMount.mount, screenX + tooltipX, relativeY, currentScreen, isLeft, !isLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hide()
|
||||
}
|
||||
tooltipLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: diskColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: "storage"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (root.diskUsagePercent > 90) {
|
||||
return Theme.tempDanger
|
||||
}
|
||||
if (root.diskUsagePercent > 75) {
|
||||
return Theme.tempWarning
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||
return "--"
|
||||
}
|
||||
return root.diskUsagePercent.toFixed(0)
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: diskContent
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
DankIcon {
|
||||
name: "storage"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (root.diskUsagePercent > 90) {
|
||||
return Theme.tempDanger
|
||||
}
|
||||
if (root.diskUsagePercent > 75) {
|
||||
return Theme.tempWarning
|
||||
}
|
||||
return Theme.surfaceText
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!root.selectedMount) {
|
||||
return "--"
|
||||
}
|
||||
return root.selectedMount.mount
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
|
||||
return "--%"
|
||||
}
|
||||
return root.diskUsagePercent.toFixed(0) + "%"
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
|
||||
StyledTextMetrics {
|
||||
id: diskBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
width: Math.max(diskBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
272
Modules/DankBar/Widgets/FocusedApp.qml
Normal file
272
Modules/DankBar/Widgets/FocusedApp.qml
Normal file
@@ -0,0 +1,272 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property var parentScreen
|
||||
property bool compactMode: SettingsData.focusedWindowCompactMode
|
||||
property int availableWidth: 400
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||
readonly property int baseWidth: contentRow.implicitWidth + horizontalPadding * 2
|
||||
readonly property int maxNormalWidth: 456
|
||||
readonly property int maxCompactWidth: 288
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
property var activeDesktopEntry: null
|
||||
|
||||
Component.onCompleted: {
|
||||
updateDesktopEntry()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopEntries
|
||||
function onApplicationsChanged() {
|
||||
root.updateDesktopEntry()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onActiveWindowChanged() {
|
||||
root.updateDesktopEntry()
|
||||
}
|
||||
}
|
||||
|
||||
function updateDesktopEntry() {
|
||||
if (activeWindow && activeWindow.appId) {
|
||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||
activeDesktopEntry = DesktopEntries.heuristicLookup(moddedId)
|
||||
} else {
|
||||
activeDesktopEntry = null
|
||||
}
|
||||
}
|
||||
readonly property bool hasWindowsOnCurrentWorkspace: {
|
||||
if (CompositorService.isNiri) {
|
||||
let currentWorkspaceId = null
|
||||
for (var i = 0; i < NiriService.allWorkspaces.length; i++) {
|
||||
const ws = NiriService.allWorkspaces[i]
|
||||
if (ws.is_focused) {
|
||||
currentWorkspaceId = ws.id
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentWorkspaceId) {
|
||||
return false
|
||||
}
|
||||
|
||||
const workspaceWindows = NiriService.windows.filter(w => w.workspace_id === currentWorkspaceId)
|
||||
return workspaceWindows.length > 0 && activeWindow && activeWindow.title
|
||||
}
|
||||
|
||||
if (CompositorService.isHyprland) {
|
||||
if (!Hyprland.focusedWorkspace || !activeWindow || !activeWindow.title) {
|
||||
return false
|
||||
}
|
||||
|
||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
||||
const activeHyprToplevel = hyprlandToplevels.find(t => t.wayland === activeWindow)
|
||||
|
||||
if (!activeHyprToplevel || !activeHyprToplevel.workspace) {
|
||||
return false
|
||||
}
|
||||
|
||||
return activeHyprToplevel.workspace.id === Hyprland.focusedWorkspace.id
|
||||
}
|
||||
|
||||
return activeWindow && activeWindow.title
|
||||
}
|
||||
|
||||
width: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : (compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)))
|
||||
height: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : widgetThickness)
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (!activeWindow || !activeWindow.title) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
clip: true
|
||||
visible: hasWindowsOnCurrentWorkspace
|
||||
|
||||
IconImage {
|
||||
id: appIcon
|
||||
anchors.centerIn: parent
|
||||
width: 18
|
||||
height: 18
|
||||
visible: root.isVertical && activeWindow && status === Image.Ready
|
||||
source: {
|
||||
if (!activeWindow || !activeWindow.appId) return ""
|
||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||
if (moddedId.toLowerCase().includes("steam_app")) return ""
|
||||
return Quickshell.iconPath(activeDesktopEntry?.icon, true)
|
||||
}
|
||||
smooth: true
|
||||
mipmap: true
|
||||
asynchronous: true
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
size: 18
|
||||
name: "sports_esports"
|
||||
color: Theme.surfaceText
|
||||
visible: {
|
||||
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
|
||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||
return moddedId.toLowerCase().includes("steam_app")
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
visible: {
|
||||
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
|
||||
if (appIcon.status === Image.Ready) return false
|
||||
const moddedId = Paths.moddedAppId(activeWindow.appId)
|
||||
return !moddedId.toLowerCase().includes("steam_app")
|
||||
}
|
||||
text: {
|
||||
if (!activeWindow || !activeWindow.appId) return "?"
|
||||
if (activeDesktopEntry && activeDesktopEntry.name) {
|
||||
return activeDesktopEntry.name.charAt(0).toUpperCase()
|
||||
}
|
||||
return activeWindow.appId.charAt(0).toUpperCase()
|
||||
}
|
||||
font.pixelSize: 10
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
visible: !root.isVertical
|
||||
|
||||
StyledText {
|
||||
id: appText
|
||||
|
||||
text: {
|
||||
if (!activeWindow || !activeWindow.appId) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId);
|
||||
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId;
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
width: Math.min(implicitWidth, compactMode ? 80 : 180)
|
||||
visible: !compactMode && text.length > 0
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "•"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outlineButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: !compactMode && appText.text && titleText.text
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: titleText
|
||||
|
||||
text: {
|
||||
const title = activeWindow && activeWindow.title ? activeWindow.title : "";
|
||||
const appName = appText.text;
|
||||
if (!title || !appName) {
|
||||
return title;
|
||||
}
|
||||
|
||||
if (title.endsWith(" - " + appName)) {
|
||||
return title.substring(0, title.length - (" - " + appName).length);
|
||||
}
|
||||
|
||||
if (title.endsWith(appName)) {
|
||||
return title.substring(0, title.length - appName.length).replace(/ - $/, "");
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
width: Math.min(implicitWidth, compactMode ? 280 : 250)
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: root.isVertical
|
||||
onEntered: {
|
||||
if (root.isVertical && activeWindow && activeWindow.appId && root.parentScreen) {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item) {
|
||||
const globalPos = mapToGlobal(width / 2, height / 2)
|
||||
const currentScreen = root.parentScreen
|
||||
const screenX = currentScreen ? currentScreen.x : 0
|
||||
const screenY = currentScreen ? currentScreen.y : 0
|
||||
const relativeY = globalPos.y - screenY
|
||||
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS) : (currentScreen.width - Theme.barHeight - SettingsData.dankBarSpacing - Theme.spacingXS)
|
||||
|
||||
const appName = activeDesktopEntry && activeDesktopEntry.name ? activeDesktopEntry.name : activeWindow.appId
|
||||
const title = activeWindow.title || ""
|
||||
const tooltipText = appName + (title ? " • " + title : "")
|
||||
|
||||
const isLeft = root.axis?.edge === "left"
|
||||
tooltipLoader.item.show(tooltipText, screenX + tooltipX, relativeY, currentScreen, isLeft, !isLeft)
|
||||
}
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hide()
|
||||
}
|
||||
tooltipLoader.active = false
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool showPercentage: true
|
||||
property bool showIcon: true
|
||||
property var toggleProcessList
|
||||
@@ -14,10 +16,10 @@ Rectangle {
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property var widgetData: null
|
||||
property real barHeight: 48
|
||||
property real widgetHeight: 30
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
property real displayTemp: {
|
||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
||||
return 0;
|
||||
@@ -37,11 +39,11 @@ Rectangle {
|
||||
const sectionId = sections[s];
|
||||
let widgets = [];
|
||||
if (sectionId === "left") {
|
||||
widgets = SettingsData.topBarLeftWidgets.slice();
|
||||
widgets = SettingsData.dankBarLeftWidgets.slice();
|
||||
} else if (sectionId === "center") {
|
||||
widgets = SettingsData.topBarCenterWidgets.slice();
|
||||
widgets = SettingsData.dankBarCenterWidgets.slice();
|
||||
} else if (sectionId === "right") {
|
||||
widgets = SettingsData.topBarRightWidgets.slice();
|
||||
widgets = SettingsData.dankBarRightWidgets.slice();
|
||||
}
|
||||
for (let i = 0; i < widgets.length; i++) {
|
||||
const widget = widgets[i];
|
||||
@@ -53,11 +55,11 @@ Rectangle {
|
||||
"pciId": pciId
|
||||
};
|
||||
if (sectionId === "left") {
|
||||
SettingsData.setTopBarLeftWidgets(widgets);
|
||||
SettingsData.setDankBarLeftWidgets(widgets);
|
||||
} else if (sectionId === "center") {
|
||||
SettingsData.setTopBarCenterWidgets(widgets);
|
||||
SettingsData.setDankBarCenterWidgets(widgets);
|
||||
} else if (sectionId === "right") {
|
||||
SettingsData.setTopBarRightWidgets(widgets);
|
||||
SettingsData.setDankBarRightWidgets(widgets);
|
||||
}
|
||||
return ;
|
||||
}
|
||||
@@ -65,33 +67,27 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
width: gpuTempContent.implicitWidth + horizontalPadding * 2
|
||||
height: widgetHeight
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
width: isVertical ? widgetThickness : (gpuTempContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (gpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = gpuArea.containsMouse ? Theme.primaryPressed : Theme.secondaryHover;
|
||||
const baseColor = gpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["gpu"]);
|
||||
console.log("GpuTemperature widget - pciId:", widgetData ? widgetData.pciId : "no widgetData", "selectedGpuIndex:", widgetData ? widgetData.selectedGpuIndex : "no widgetData");
|
||||
// Add this widget's PCI ID to the service
|
||||
if (widgetData && widgetData.pciId) {
|
||||
console.log("Adding GPU PCI ID to service:", widgetData.pciId);
|
||||
DgopService.addGpuPciId(widgetData.pciId);
|
||||
} else {
|
||||
console.log("No PCI ID in widget data, starting auto-detection");
|
||||
// No PCI ID saved, auto-detect and save the first GPU
|
||||
autoSaveTimer.running = true;
|
||||
}
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["gpu"]);
|
||||
// Remove this widget's PCI ID from the service
|
||||
if (widgetData && widgetData.pciId) {
|
||||
DgopService.removeGpuPciId(widgetData.pciId);
|
||||
}
|
||||
@@ -117,11 +113,10 @@ Rectangle {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const screenX = currentScreen.x || 0;
|
||||
const relativeX = globalPos.x - screenX;
|
||||
popupTarget.setTriggerPosition(relativeX, barHeight + Theme.spacingXS, width, section, currentScreen);
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
DgopService.setSortBy("cpu");
|
||||
if (root.toggleProcessList) {
|
||||
@@ -131,9 +126,47 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: gpuTempColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: "auto_awesome_mosaic"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (root.displayTemp > 80) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (root.displayTemp > 65) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) {
|
||||
return "--";
|
||||
}
|
||||
|
||||
return Math.round(root.displayTemp).toString();
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: gpuTempContent
|
||||
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
@@ -166,6 +199,24 @@ Rectangle {
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
|
||||
StyledTextMetrics {
|
||||
id: gpuTempBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "100°"
|
||||
}
|
||||
|
||||
width: Math.max(gpuTempBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -187,12 +238,5 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,21 +8,23 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetHeight: 30
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
width: idleIcon.width + horizontalPadding * 2
|
||||
height: widgetHeight
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
width: isVertical ? widgetThickness : (idleIcon.width + horizontalPadding * 2)
|
||||
height: isVertical ? (idleIcon.height + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.primaryPressed : (SessionService.idleInhibited ? Theme.primaryHover : Theme.secondaryHover);
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
@@ -46,12 +48,5 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
144
Modules/DankBar/Widgets/KeyboardLayoutName.qml
Normal file
144
Modules/DankBar/Widgets/KeyboardLayoutName.qml
Normal file
@@ -0,0 +1,144 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Modules.ProcessList
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
property string currentLayout: ""
|
||||
property string hyprlandKeyboard: ""
|
||||
|
||||
width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (CompositorService.isNiri) {
|
||||
NiriService.cycleKeyboardLayout()
|
||||
} else if (CompositorService.isHyprland) {
|
||||
Quickshell.execDetached([
|
||||
"hyprctl",
|
||||
"switchxkblayout",
|
||||
root.hyprlandKeyboard,
|
||||
"next"
|
||||
])
|
||||
updateLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
visible: root.isVertical
|
||||
|
||||
DankIcon {
|
||||
name: "keyboard"
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (!currentLayout) return ""
|
||||
const parts = currentLayout.split(" ")
|
||||
if (parts.length > 0) {
|
||||
return parts[0].substring(0, 2).toUpperCase()
|
||||
}
|
||||
return currentLayout.substring(0, 2).toUpperCase()
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
visible: !root.isVertical
|
||||
|
||||
StyledText {
|
||||
text: currentLayout
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Process {
|
||||
id: hyprlandLayoutProcess
|
||||
running: false
|
||||
command: ["hyprctl", "-j", "devices"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const data = JSON.parse(text)
|
||||
// Find the main keyboard and get its active keymap
|
||||
const mainKeyboard = data.keyboards.find(kb => kb.main === true)
|
||||
root.hyprlandKeyboard = mainKeyboard.name
|
||||
if (mainKeyboard && mainKeyboard.active_keymap) {
|
||||
root.currentLayout = mainKeyboard.active_keymap
|
||||
} else {
|
||||
root.currentLayout = "Unknown"
|
||||
}
|
||||
} catch (e) {
|
||||
root.currentLayout = "Unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: updateTimer
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
updateLayout()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updateLayout()
|
||||
}
|
||||
|
||||
function updateLayout() {
|
||||
if (CompositorService.isNiri) {
|
||||
root.currentLayout = NiriService.getCurrentKeyboardLayoutName()
|
||||
} else if (CompositorService.isHyprland) {
|
||||
hyprlandLayoutProcess.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,17 +7,19 @@ Item {
|
||||
id: root
|
||||
|
||||
property bool isActive: false
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "left"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetHeight: 30
|
||||
property real barHeight: 48
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: Theme.iconSize + horizontalPadding * 2
|
||||
height: widgetHeight
|
||||
width: widgetThickness
|
||||
height: widgetThickness
|
||||
|
||||
MouseArea {
|
||||
id: launcherArea
|
||||
@@ -27,14 +29,13 @@ Item {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
root.clicked();
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0);
|
||||
const currentScreen = parentScreen || Screen;
|
||||
const screenX = currentScreen.x || 0;
|
||||
const relativeX = globalPos.x - screenX;
|
||||
popupTarget.setTriggerPosition(relativeX, barHeight + Theme.spacingXS, width, section, currentScreen);
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width);
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen);
|
||||
}
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,21 +43,21 @@ Item {
|
||||
id: launcherContent
|
||||
|
||||
anchors.fill: parent
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = launcherArea.containsMouse ? Theme.primaryPressed : (SessionService.idleInhibited ? Theme.primaryHover : Theme.secondaryHover);
|
||||
const baseColor = launcherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
SystemLogo {
|
||||
visible: SettingsData.useOSLogo
|
||||
anchors.centerIn: parent
|
||||
width: Theme.iconSize - 3
|
||||
height: Theme.iconSize - 3
|
||||
width: widgetThickness - 8
|
||||
height: widgetThickness - 8
|
||||
colorOverride: SettingsData.osLogoColorOverride
|
||||
brightnessOverride: SettingsData.osLogoBrightness
|
||||
contrastOverride: SettingsData.osLogoContrast
|
||||
@@ -64,20 +65,12 @@ Item {
|
||||
|
||||
DankIcon {
|
||||
visible: !SettingsData.useOSLogo
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
name: "apps"
|
||||
size: Theme.iconSize - 6
|
||||
size: widgetThickness - 8
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||
readonly property bool playerAvailable: activePlayer !== null
|
||||
property bool compactMode: false
|
||||
@@ -21,31 +23,40 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
readonly property int currentContentWidth: {
|
||||
// Calculate actual content width:
|
||||
// AudioViz (20) + spacing + [text + spacing] + controls (prev:20 + spacing + play:24 + spacing + next:20) + padding
|
||||
if (isVertical) {
|
||||
return widgetThickness;
|
||||
}
|
||||
const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20;
|
||||
// ~72px total
|
||||
const audioVizWidth = 20;
|
||||
const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth;
|
||||
return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0) + horizontalPadding * 2;
|
||||
}
|
||||
readonly property int currentContentHeight: {
|
||||
if (!isVertical) {
|
||||
return widgetThickness;
|
||||
}
|
||||
const audioVizHeight = 20;
|
||||
const playButtonHeight = 24;
|
||||
return audioVizHeight + Theme.spacingXS + playButtonHeight + horizontalPadding * 2;
|
||||
}
|
||||
property string section: "center"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real barHeight: 48
|
||||
property real widgetHeight: 30
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
height: widgetHeight
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
width: currentContentWidth
|
||||
height: currentContentHeight
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = Theme.surfaceTextHover;
|
||||
const baseColor = Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
states: [
|
||||
@@ -57,6 +68,7 @@ Rectangle {
|
||||
target: root
|
||||
opacity: 1
|
||||
width: currentContentWidth
|
||||
height: currentContentHeight
|
||||
}
|
||||
|
||||
},
|
||||
@@ -67,7 +79,8 @@ Rectangle {
|
||||
PropertyChanges {
|
||||
target: root
|
||||
opacity: 0
|
||||
width: 0
|
||||
width: isVertical ? widgetThickness : 0
|
||||
height: isVertical ? 0 : widgetThickness
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,7 +96,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
properties: "opacity,width"
|
||||
properties: isVertical ? "opacity,height" : "opacity,width"
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
@@ -96,7 +109,7 @@ Rectangle {
|
||||
to: "shown"
|
||||
|
||||
NumberAnimation {
|
||||
properties: "opacity,width"
|
||||
properties: isVertical ? "opacity,height" : "opacity,width"
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
@@ -104,9 +117,107 @@ Rectangle {
|
||||
}
|
||||
]
|
||||
|
||||
Column {
|
||||
id: verticalLayout
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
AudioVisualization {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (root.popupTarget && root.popupTarget.setTriggerPosition) {
|
||||
const globalPos = parent.mapToGlobal(0, 0)
|
||||
const currentScreen = root.parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width)
|
||||
root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||
}
|
||||
root.clicked()
|
||||
}
|
||||
onEntered: {
|
||||
tooltipLoader.active = true
|
||||
if (tooltipLoader.item && activePlayer) {
|
||||
const globalPos = parent.mapToGlobal(parent.width / 2, parent.height / 2)
|
||||
const screenX = root.parentScreen ? root.parentScreen.x : 0
|
||||
const screenY = root.parentScreen ? root.parentScreen.y : 0
|
||||
const relativeY = globalPos.y - screenY
|
||||
const tooltipX = root.axis?.edge === "left" ? (Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS) : (root.parentScreen.width - Theme.barHeight - SettingsData.dankBarSpacing - Theme.spacingXS)
|
||||
|
||||
let identity = activePlayer.identity || ""
|
||||
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium")
|
||||
let title = activePlayer.trackTitle || "Unknown Track"
|
||||
let subtitle = ""
|
||||
if (isWebMedia && activePlayer.trackTitle) {
|
||||
subtitle = activePlayer.trackArtist || identity
|
||||
} else {
|
||||
subtitle = activePlayer.trackArtist || ""
|
||||
}
|
||||
let tooltipText = subtitle.length > 0 ? title + " • " + subtitle : title
|
||||
|
||||
const isLeft = root.axis?.edge === "left"
|
||||
tooltipLoader.item.show(tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft)
|
||||
}
|
||||
}
|
||||
onExited: {
|
||||
if (tooltipLoader.item) {
|
||||
tooltipLoader.item.hide()
|
||||
}
|
||||
tooltipLoader.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
radius: 12
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover
|
||||
visible: root.playerAvailable
|
||||
opacity: activePlayer ? 1 : 0.3
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||
size: 14
|
||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.playerAvailable
|
||||
hoverEnabled: enabled
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||
onClicked: (mouse) => {
|
||||
if (!activePlayer) return
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
activePlayer.togglePlaying()
|
||||
} else if (mouse.button === Qt.MiddleButton) {
|
||||
activePlayer.previous()
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
activePlayer.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: tooltipLoader
|
||||
active: false
|
||||
sourceComponent: DankTooltip {}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: mediaRow
|
||||
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
@@ -208,13 +319,12 @@ Rectangle {
|
||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onPressed: {
|
||||
if (root.popupTarget && root.popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0);
|
||||
const currentScreen = root.parentScreen || Screen;
|
||||
const screenX = currentScreen.x || 0;
|
||||
const relativeX = globalPos.x - screenX;
|
||||
root.popupTarget.setTriggerPosition(relativeX, barHeight + Theme.spacingXS, root.width, root.section, currentScreen);
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = root.parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.width)
|
||||
root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
|
||||
}
|
||||
root.clicked();
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,20 +434,19 @@ Rectangle {
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
193
Modules/DankBar/Widgets/NetworkMonitor.qml
Normal file
193
Modules/DankBar/Widgets/NetworkMonitor.qml
Normal file
@@ -0,0 +1,193 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Modules.ProcessList
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property int availableWidth: 400
|
||||
readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2
|
||||
readonly property int maxNormalWidth: 456
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
function formatNetworkSpeed(bytesPerSec) {
|
||||
if (bytesPerSec < 1024) {
|
||||
return bytesPerSec.toFixed(0) + " B/s";
|
||||
} else if (bytesPerSec < 1024 * 1024) {
|
||||
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
|
||||
} else if (bytesPerSec < 1024 * 1024 * 1024) {
|
||||
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
|
||||
} else {
|
||||
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s";
|
||||
}
|
||||
}
|
||||
|
||||
width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = networkArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["network"]);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["network"]);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: networkArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 2
|
||||
visible: root.isVertical
|
||||
|
||||
DankIcon {
|
||||
name: "network_check"
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const rate = DgopService.networkRxRate
|
||||
if (rate < 1024) return rate.toFixed(0)
|
||||
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.info
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
const rate = DgopService.networkTxRate
|
||||
if (rate < 1024) return rate.toFixed(0)
|
||||
if (rate < 1024 * 1024) return (rate / 1024).toFixed(0) + "K"
|
||||
return (rate / (1024 * 1024)).toFixed(0) + "M"
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
visible: !root.isVertical
|
||||
|
||||
DankIcon {
|
||||
name: "network_check"
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: "↓"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.info
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: DgopService.networkRxRate > 0 ? formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
wrapMode: Text.NoWrap
|
||||
|
||||
StyledTextMetrics {
|
||||
id: rxBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
width: Math.max(rxBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 4
|
||||
|
||||
StyledText {
|
||||
text: "↑"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.error
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: DgopService.networkTxRate > 0 ? formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
wrapMode: Text.NoWrap
|
||||
|
||||
StyledTextMetrics {
|
||||
id: txBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "88.8 MB/s"
|
||||
}
|
||||
|
||||
width: Math.max(txBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
96
Modules/DankBar/Widgets/NotepadButton.qml
Normal file
96
Modules/DankBar/Widgets/NotepadButton.qml
Normal file
@@ -0,0 +1,96 @@
|
||||
import QtQuick
|
||||
import Quickshell.Hyprland
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "right"
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
readonly property string focusedScreenName: (
|
||||
CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") :
|
||||
CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : ""
|
||||
)
|
||||
|
||||
function resolveNotepadInstance() {
|
||||
if (typeof notepadSlideoutVariants === "undefined" || !notepadSlideoutVariants || !notepadSlideoutVariants.instances) {
|
||||
return null
|
||||
}
|
||||
|
||||
const targetScreen = focusedScreenName
|
||||
if (targetScreen) {
|
||||
for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) {
|
||||
var slideout = notepadSlideoutVariants.instances[i]
|
||||
if (slideout.modelData && slideout.modelData.name === targetScreen) {
|
||||
return slideout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return notepadSlideoutVariants.instances.length > 0 ? notepadSlideoutVariants.instances[0] : null
|
||||
}
|
||||
|
||||
readonly property var notepadInstance: resolveNotepadInstance()
|
||||
readonly property bool isActive: notepadInstance?.isVisible ?? false
|
||||
|
||||
width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2)
|
||||
height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = notepadArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: notepadIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
name: "assignment"
|
||||
size: Theme.iconSize - 6
|
||||
color: notepadArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Theme.primary
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4
|
||||
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: notepadArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
const inst = root.notepadInstance
|
||||
if (inst) {
|
||||
inst.toggle()
|
||||
}
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
76
Modules/DankBar/Widgets/NotificationCenterButton.qml
Normal file
76
Modules/DankBar/Widgets/NotificationCenterButton.qml
Normal file
@@ -0,0 +1,76 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property bool hasUnread: false
|
||||
property bool isActive: false
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetThickness: 30
|
||||
property real barThickness: 48
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: widgetThickness
|
||||
height: widgetThickness
|
||||
|
||||
MouseArea {
|
||||
id: notificationArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: notificationContent
|
||||
|
||||
anchors.fill: parent
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent"
|
||||
}
|
||||
|
||||
const baseColor = notificationArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
|
||||
size: widgetThickness - 8
|
||||
color: SessionData.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
color: Theme.error
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
||||
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
|
||||
visible: root.hasUnread
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,23 +7,26 @@ import qs.Widgets
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real widgetHeight: 30
|
||||
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
|
||||
readonly property bool hasActivePrivacy: PrivacyService.anyPrivacyActive
|
||||
readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive
|
||||
readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
||||
readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
|
||||
|
||||
width: hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0
|
||||
height: hasActivePrivacy ? widgetHeight : 0
|
||||
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
|
||||
width: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0)
|
||||
height: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : (hasActivePrivacy ? widgetThickness : 0)
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
visible: hasActivePrivacy
|
||||
opacity: hasActivePrivacy ? 1 : 0
|
||||
enabled: hasActivePrivacy
|
||||
color: {
|
||||
if (SettingsData.topBarNoBackground) {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
@@ -43,10 +46,76 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
visible: root.isVertical && hasActivePrivacy
|
||||
|
||||
Item {
|
||||
width: 18
|
||||
height: 18
|
||||
visible: PrivacyService.microphoneActive
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "mic"
|
||||
size: Theme.iconSizeSmall
|
||||
color: Theme.error
|
||||
filled: true
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 18
|
||||
height: 18
|
||||
visible: PrivacyService.cameraActive
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "camera_video"
|
||||
size: Theme.iconSizeSmall
|
||||
color: Theme.surfaceText
|
||||
filled: true
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 6
|
||||
height: 6
|
||||
radius: 3
|
||||
color: Theme.error
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: -2
|
||||
anchors.topMargin: -1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
width: 18
|
||||
height: 18
|
||||
visible: PrivacyService.screensharingActive
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
DankIcon {
|
||||
name: "screen_share"
|
||||
size: Theme.iconSizeSmall
|
||||
color: Theme.warning
|
||||
filled: true
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
visible: hasActivePrivacy
|
||||
visible: !root.isVertical && hasActivePrivacy
|
||||
|
||||
Item {
|
||||
width: 18
|
||||
@@ -158,7 +227,17 @@ Rectangle {
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
enabled: hasActivePrivacy && visible
|
||||
enabled: hasActivePrivacy && visible && !isVertical
|
||||
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
enabled: hasActivePrivacy && visible && isVertical
|
||||
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
156
Modules/DankBar/Widgets/RamMonitor.qml
Normal file
156
Modules/DankBar/Widgets/RamMonitor.qml
Normal file
@@ -0,0 +1,156 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool isVertical: axis?.isVertical ?? false
|
||||
property var axis: null
|
||||
property bool showPercentage: true
|
||||
property bool showIcon: true
|
||||
property var toggleProcessList
|
||||
property string section: "right"
|
||||
property var popupTarget: null
|
||||
property var parentScreen: null
|
||||
property real barThickness: 48
|
||||
property real widgetThickness: 30
|
||||
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
|
||||
|
||||
width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2)
|
||||
height: isVertical ? (ramColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
|
||||
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
|
||||
color: {
|
||||
if (SettingsData.dankBarNoBackground) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
const baseColor = ramArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
|
||||
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
|
||||
}
|
||||
Component.onCompleted: {
|
||||
DgopService.addRef(["memory"]);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
DgopService.removeRef(["memory"]);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ramArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onPressed: {
|
||||
if (popupTarget && popupTarget.setTriggerPosition) {
|
||||
const globalPos = mapToGlobal(0, 0)
|
||||
const currentScreen = parentScreen || Screen
|
||||
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
|
||||
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
|
||||
}
|
||||
DgopService.setSortBy("memory");
|
||||
if (root.toggleProcessList) {
|
||||
root.toggleProcessList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: ramColumn
|
||||
visible: root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 1
|
||||
|
||||
DankIcon {
|
||||
name: "developer_board"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.memoryUsage > 90) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.memoryUsage > 75) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
||||
return "--";
|
||||
}
|
||||
|
||||
return DgopService.memoryUsage.toFixed(0);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: ramContent
|
||||
visible: !root.isVertical
|
||||
anchors.centerIn: parent
|
||||
spacing: 3
|
||||
|
||||
DankIcon {
|
||||
name: "developer_board"
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (DgopService.memoryUsage > 90) {
|
||||
return Theme.tempDanger;
|
||||
}
|
||||
|
||||
if (DgopService.memoryUsage > 75) {
|
||||
return Theme.tempWarning;
|
||||
}
|
||||
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: {
|
||||
if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) {
|
||||
return "--%";
|
||||
}
|
||||
|
||||
return DgopService.memoryUsage.toFixed(0) + "%";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideNone
|
||||
|
||||
StyledTextMetrics {
|
||||
id: ramBaseline
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
text: "100%"
|
||||
}
|
||||
|
||||
width: Math.max(ramBaseline.width, paintedWidth)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user