1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Compare commits

...

42 Commits

Author SHA1 Message Date
purian23
0a038fa3c9 Revert "wip binds"
This reverts commit 841e55d37f.
2025-10-01 11:49:29 -04:00
bbedward
841e55d37f wip binds 2025-09-30 17:31:17 -04:00
bbedward
287dda5675 niri: generate colors, layout, binds 2025-09-30 17:31:17 -04:00
bbedward
37120776be configurable animation speeds 2025-09-30 17:30:03 -04:00
bbedward
d67bcb66c9 Keep SpotlightModal always loaded.
Tbh, probably minimal impact on memory - makes app launching snappier.
2025-09-30 17:07:57 -04:00
bbedward
acf6e72f35 emacs/vim style navigation everywhere 2025-09-30 16:54:50 -04:00
Alex Birdsall
0964b271f5 Add emacs-/readline- and vim-style list navigation to app launcher (#278)
* Add emacs- and vim-style nav keys to appLauncher

* Minor typo fix in contributing docs
2025-09-30 16:46:37 -04:00
bbedward
1f7998fc44 Disable scale from DankModal 2025-09-30 16:45:30 -04:00
bbedward
7ac59f7bd0 Revert antialiasing addition 2025-09-30 16:43:04 -04:00
xdenotte
bf238640af fix(popouts): disable layer compositing and scale to keep text sharp at 1.25 (#277) 2025-09-30 16:42:39 -04:00
bbedward
6e638cadb7 slightly thinner workspace pills + more forgiving mousearea 2025-09-30 15:49:13 -04:00
bbedward
ab0759f441 antialiasing: true on rectangles 2025-09-30 13:28:07 -04:00
bbedward
123ec5c78a Flip text to native rendering 2025-09-30 13:17:09 -04:00
bbedward
d262c67832 Fix VPN tooltip 2025-09-30 13:06:21 -04:00
bbedward
fcb9a2838d Disable VPN icon rotation 2025-09-30 12:59:10 -04:00
bbedward
82d1672741 search field focus only 2025-09-30 12:36:58 -04:00
bbedward
657a8b6094 Per light-mode/dark-mode wallpaper option 2025-09-30 12:29:08 -04:00
bbedward
165f7e0720 fix exit anim 2025-09-30 11:05:15 -04:00
bbedward
91aba84c29 Tie modal loader to shouldBeVisible 2025-09-30 11:01:51 -04:00
bbedward
02c4ac1bc7 fix bar radius 2025-09-30 10:56:18 -04:00
bbedward
c529959027 Tie volume OSD to dbus event 2025-09-30 10:11:35 -04:00
bbedward
e875d1a5d7 meta: Vertical Bar, Notification Popup Position Options, ++
- CC Color picker widget
- Tooltips in more places
- Attempt to improve niri screen transitiosn
2025-09-30 09:51:18 -04:00
bbedward
d280505b9f Merge branch 'master' of github.com:bbedward/DankMaterialShell 2025-09-29 16:50:24 -04:00
bbedward
fdd9d0000b Dont do dumb mono font filtering 2025-09-29 16:50:13 -04:00
purian23
74c793eedf fix: Notepad refactor w/Dual displays 2025-09-29 14:38:58 -04:00
purian23
bd8976c620 feat: Docks refactor - Top/Bottom options 2025-09-28 23:55:59 -04:00
purian23
a3e2563f9b Smoother notepad closure animation 2025-09-28 22:41:15 -04:00
bbedward
a310e3d8fa Fix contrast of monochrome theme 2025-09-28 13:26:18 -04:00
purian23
ae9da35af1 fix: Dank Bar per/Display options 2025-09-27 21:52:06 -04:00
bbedward
b45837ff5c Tab and backtab keyboard navigation on supported content 2025-09-27 09:08:41 -04:00
Parthiv Seetharaman
3dd9ab8a29 add option to always show dock in overview (#257) 2025-09-27 08:55:21 -04:00
bbedward
5ebd2f6b8e Smaller top bar auto hide mask 2025-09-27 08:53:28 -04:00
purian23
977043ac92 feat: Long Live the DankBar > Top/Bottom positioning 2025-09-26 23:45:19 -04:00
bbedward
4c6182b79c Allow 0 spacing 2025-09-26 13:49:00 -04:00
bbedward
01125e092c Don't allow popouts to go off screen 2025-09-26 13:47:40 -04:00
bbedward
99ef447a52 Restore bluetooth audio codec functionality 2025-09-26 12:37:10 -04:00
purian23
4722ff9b97 Merge pull request #255 from sezaru/fix_nix_documentation
doc: Fixes DMS input URL for nix flake
2025-09-26 12:21:26 -04:00
Eduardo B. A.
23a19df79a doc: Fixes DMS input url for nix flake 2025-09-26 13:10:10 -03:00
Eduardo B. A.
97b86c6faa Optimize Nix Flake for Niri / Hyprland (#242)
* feat: Make niri stuff optional

* doc: Add some documentation about the flake

* Simplify modules.

* Overlay pkgs and simplify modules.

* Fix niri config levels.

* Fix documentation.

* Just pass the packages to the module

---------

Co-authored-by: Eduardo Barreto Alexandre <blibs@blobs.com>
Co-authored-by: Luis González <5774777+luis-agm@users.noreply.github.com>
2025-09-26 10:44:41 -04:00
bbedward
e6296f20b9 default opaque 2025-09-26 00:25:15 -04:00
bbedward
0ee9dcc255 Revise catpuccin palettes 2025-09-26 00:16:34 -04:00
bbedward
c7b4e2c49d wrap shaders in a loader 2025-09-25 23:33:57 -04:00
118 changed files with 8383 additions and 3705 deletions

View File

@@ -1,6 +1,6 @@
# Contributing # Contributing
Contributions are welcome and encourages. Contributions are welcome and encouraged.
## Formatting ## Formatting

View File

@@ -18,6 +18,11 @@ Singleton {
property string profileLastPath: "" property string profileLastPath: ""
property bool perMonitorWallpaper: false property bool perMonitorWallpaper: false
property var monitorWallpapers: ({}) 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 doNotDisturb: false
property bool nightModeEnabled: false property bool nightModeEnabled: false
property int nightModeTemperature: 4500 property int nightModeTemperature: 4500
@@ -83,6 +88,11 @@ Singleton {
profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : "" profileLastPath = settings.profileLastPath !== undefined ? settings.profileLastPath : ""
perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false perMonitorWallpaper = settings.perMonitorWallpaper !== undefined ? settings.perMonitorWallpaper : false
monitorWallpapers = settings.monitorWallpapers !== undefined ? settings.monitorWallpapers : {} 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 doNotDisturb = settings.doNotDisturb !== undefined ? settings.doNotDisturb : false
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false
nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500 nightModeTemperature = settings.nightModeTemperature !== undefined ? settings.nightModeTemperature : 4500
@@ -151,6 +161,11 @@ Singleton {
"profileLastPath": profileLastPath, "profileLastPath": profileLastPath,
"perMonitorWallpaper": perMonitorWallpaper, "perMonitorWallpaper": perMonitorWallpaper,
"monitorWallpapers": monitorWallpapers, "monitorWallpapers": monitorWallpapers,
"perModeWallpaper": perModeWallpaper,
"wallpaperPathLight": wallpaperPathLight,
"wallpaperPathDark": wallpaperPathDark,
"monitorWallpapersLight": monitorWallpapersLight,
"monitorWallpapersDark": monitorWallpapersDark,
"doNotDisturb": doNotDisturb, "doNotDisturb": doNotDisturb,
"nightModeEnabled": nightModeEnabled, "nightModeEnabled": nightModeEnabled,
"nightModeTemperature": nightModeTemperature, "nightModeTemperature": nightModeTemperature,
@@ -191,9 +206,21 @@ Singleton {
function setLightMode(lightMode) { function setLightMode(lightMode) {
isLightMode = lightMode isLightMode = lightMode
syncWallpaperForCurrentMode()
saveSettings() saveSettings()
} }
function syncWallpaperForCurrentMode() {
if (!perModeWallpaper) return
if (perMonitorWallpaper) {
monitorWallpapers = isLightMode ? Object.assign({}, monitorWallpapersLight) : Object.assign({}, monitorWallpapersDark)
return
}
wallpaperPath = isLightMode ? wallpaperPathLight : wallpaperPathDark
}
function setDoNotDisturb(enabled) { function setDoNotDisturb(enabled) {
doNotDisturb = enabled doNotDisturb = enabled
saveSettings() saveSettings()
@@ -264,6 +291,13 @@ Singleton {
function setWallpaper(imagePath) { function setWallpaper(imagePath) {
wallpaperPath = imagePath wallpaperPath = imagePath
if (perModeWallpaper) {
if (isLightMode) {
wallpaperPathLight = imagePath
} else {
wallpaperPathDark = imagePath
}
}
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
@@ -273,6 +307,13 @@ Singleton {
function setWallpaperColor(color) { function setWallpaperColor(color) {
wallpaperPath = color wallpaperPath = color
if (perModeWallpaper) {
if (isLightMode) {
wallpaperPathLight = color
} else {
wallpaperPathDark = color
}
}
saveSettings() saveSettings()
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
@@ -420,9 +461,51 @@ Singleton {
function setPerMonitorWallpaper(enabled) { function setPerMonitorWallpaper(enabled) {
perMonitorWallpaper = enabled perMonitorWallpaper = enabled
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() saveSettings()
// Refresh dynamic theming when per-monitor mode changes
if (typeof Theme !== "undefined") { if (typeof Theme !== "undefined") {
Theme.generateSystemThemesFromCurrentTheme() Theme.generateSystemThemesFromCurrentTheme()
} }
@@ -436,9 +519,29 @@ Singleton {
delete newMonitorWallpapers[screenName] delete newMonitorWallpapers[screenName]
} }
monitorWallpapers = newMonitorWallpapers 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() saveSettings()
// Trigger dynamic theming if this is the first monitor and dynamic theming is enabled
if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") { if (typeof Theme !== "undefined" && typeof Quickshell !== "undefined") {
var screens = Quickshell.screens var screens = Quickshell.screens
if (screens.length > 0 && screenName === screens[0].name) { if (screens.length > 0 && screenName === screens[0].name) {

View File

@@ -12,12 +12,27 @@ import qs.Services
Singleton { Singleton {
id: root id: root
enum Position {
Top,
Bottom,
Left,
Right
}
enum AnimationSpeed {
None,
Shortest,
Short,
Medium,
Long
}
// Theme settings // Theme settings
property string currentThemeName: "blue" property string currentThemeName: "blue"
property string customThemeFile: "" property string customThemeFile: ""
property string matugenScheme: "scheme-tonal-spot" property string matugenScheme: "scheme-tonal-spot"
property real topBarTransparency: 0.75 property real dankBarTransparency: 1.0
property real topBarWidgetTransparency: 0.85 property real dankBarWidgetTransparency: 1.0
property real popupTransparency: 1.0 property real popupTransparency: 1.0
property real dockTransparency: 1 property real dockTransparency: 1
property bool use24HourClock: true property bool use24HourClock: true
@@ -70,12 +85,13 @@ Singleton {
property string clockDateFormat: "" property string clockDateFormat: ""
property string lockDateFormat: "" property string lockDateFormat: ""
property int mediaSize: 1 property int mediaSize: 1
property var topBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"] property var dankBarLeftWidgets: ["launcherButton", "workspaceSwitcher", "focusedWindow"]
property var topBarCenterWidgets: ["music", "clock", "weather"] property var dankBarCenterWidgets: ["music", "clock", "weather"]
property var topBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"] property var dankBarRightWidgets: ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"]
property alias topBarLeftWidgetsModel: leftWidgetsModel property var dankBarWidgetOrder: []
property alias topBarCenterWidgetsModel: centerWidgetsModel property alias dankBarLeftWidgetsModel: leftWidgetsModel
property alias topBarRightWidgetsModel: rightWidgetsModel property alias dankBarCenterWidgetsModel: centerWidgetsModel
property alias dankBarRightWidgetsModel: rightWidgetsModel
property string appLauncherViewMode: "list" property string appLauncherViewMode: "list"
property string spotlightModalViewMode: "list" property string spotlightModalViewMode: "list"
property string networkPreference: "auto" property string networkPreference: "auto"
@@ -117,17 +133,23 @@ Singleton {
property bool showDock: false property bool showDock: false
property bool dockAutoHide: false property bool dockAutoHide: false
property bool dockGroupByApp: 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 real cornerRadius: 12
property bool notificationOverlayEnabled: false property bool notificationOverlayEnabled: false
property bool topBarAutoHide: false property bool dankBarAutoHide: false
property bool topBarOpenOnOverview: false property bool dankBarOpenOnOverview: false
property bool topBarVisible: true property bool dankBarVisible: true
property real topBarSpacing: 4 property real dankBarSpacing: 4
property real topBarBottomGap: 0 property real dankBarBottomGap: 0
property real topBarInnerPadding: 8 property real dankBarInnerPadding: 4
property bool topBarSquareCorners: false property bool dankBarSquareCorners: false
property bool topBarNoBackground: false property bool dankBarNoBackground: false
property bool topBarGothCornersEnabled: 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 lockScreenShowPowerActions: true
property bool hideBrightnessSlider: false property bool hideBrightnessSlider: false
property string widgetBackgroundColor: "sch" property string widgetBackgroundColor: "sch"
@@ -135,14 +157,17 @@ Singleton {
property int notificationTimeoutLow: 5000 property int notificationTimeoutLow: 5000
property int notificationTimeoutNormal: 5000 property int notificationTimeoutNormal: 5000
property int notificationTimeoutCritical: 0 property int notificationTimeoutCritical: 0
property int notificationPopupPosition: SettingsData.Position.Top
property var screenPreferences: ({}) property var screenPreferences: ({})
property int animationSpeed: SettingsData.AnimationSpeed.Short
readonly property string defaultFontFamily: "Inter Variable" readonly property string defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code" readonly property string defaultMonoFontFamily: "Fira Code"
readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation) readonly property string _homeUrl: StandardPaths.writableLocation(StandardPaths.HomeLocation)
readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation) readonly property string _configUrl: StandardPaths.writableLocation(StandardPaths.ConfigLocation)
readonly property string _configDir: Paths.strip(_configUrl) readonly property string _configDir: Paths.strip(_configUrl)
signal forceTopBarLayoutRefresh signal forceDankBarLayoutRefresh
signal forceDockLayoutRefresh
signal widgetDataChanged signal widgetDataChanged
signal workspaceIconsUpdated signal workspaceIconsUpdated
@@ -179,9 +204,9 @@ Singleton {
centerWidgetsModel.append(dummyItem) centerWidgetsModel.append(dummyItem)
rightWidgetsModel.append(dummyItem) rightWidgetsModel.append(dummyItem)
updateListModel(leftWidgetsModel, topBarLeftWidgets) updateListModel(leftWidgetsModel, dankBarLeftWidgets)
updateListModel(centerWidgetsModel, topBarCenterWidgets) updateListModel(centerWidgetsModel, dankBarCenterWidgets)
updateListModel(rightWidgetsModel, topBarRightWidgets) updateListModel(rightWidgetsModel, dankBarRightWidgets)
} }
function loadSettings() { function loadSettings() {
@@ -209,8 +234,8 @@ Singleton {
} }
customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : "" customThemeFile = settings.customThemeFile !== undefined ? settings.customThemeFile : ""
matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot" matugenScheme = settings.matugenScheme !== undefined ? settings.matugenScheme : "scheme-tonal-spot"
topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75 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)
topBarWidgetTransparency = settings.topBarWidgetTransparency !== undefined ? (settings.topBarWidgetTransparency > 1 ? settings.topBarWidgetTransparency / 100 : settings.topBarWidgetTransparency) : 0.85 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 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 dockTransparency = settings.dockTransparency !== undefined ? (settings.dockTransparency > 1 ? settings.dockTransparency / 100 : settings.dockTransparency) : 1
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true
@@ -264,23 +289,24 @@ Singleton {
clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : "" clockDateFormat = settings.clockDateFormat !== undefined ? settings.clockDateFormat : ""
lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : "" lockDateFormat = settings.lockDateFormat !== undefined ? settings.lockDateFormat : ""
mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1) mediaSize = settings.mediaSize !== undefined ? settings.mediaSize : (settings.mediaCompactMode !== undefined ? (settings.mediaCompactMode ? 0 : 1) : 1)
if (settings.topBarWidgetOrder) { if (settings.dankBarWidgetOrder || settings.topBarWidgetOrder) {
topBarLeftWidgets = settings.topBarWidgetOrder.filter(w => { var widgetOrder = settings.dankBarWidgetOrder || settings.topBarWidgetOrder
dankBarLeftWidgets = widgetOrder.filter(w => {
return ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w) return ["launcherButton", "workspaceSwitcher", "focusedWindow"].includes(w)
}) })
topBarCenterWidgets = settings.topBarWidgetOrder.filter(w => { dankBarCenterWidgets = widgetOrder.filter(w => {
return ["clock", "music", "weather"].includes(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) return ["systemTray", "clipboard", "systemResources", "notificationButton", "battery", "controlCenterButton"].includes(w)
}) })
} else { } else {
var leftWidgets = settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"] var leftWidgets = settings.dankBarLeftWidgets !== undefined ? settings.dankBarLeftWidgets : (settings.topBarLeftWidgets !== undefined ? settings.topBarLeftWidgets : ["launcherButton", "workspaceSwitcher", "focusedWindow"])
var centerWidgets = settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"] var centerWidgets = settings.dankBarCenterWidgets !== undefined ? settings.dankBarCenterWidgets : (settings.topBarCenterWidgets !== undefined ? settings.topBarCenterWidgets : ["music", "clock", "weather"])
var rightWidgets = settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"] var rightWidgets = settings.dankBarRightWidgets !== undefined ? settings.dankBarRightWidgets : (settings.topBarRightWidgets !== undefined ? settings.topBarRightWidgets : ["systemTray", "clipboard", "cpuUsage", "memUsage", "notificationButton", "battery", "controlCenterButton"])
topBarLeftWidgets = leftWidgets dankBarLeftWidgets = leftWidgets
topBarCenterWidgets = centerWidgets dankBarCenterWidgets = centerWidgets
topBarRightWidgets = rightWidgets dankBarRightWidgets = rightWidgets
updateListModel(leftWidgetsModel, leftWidgets) updateListModel(leftWidgetsModel, leftWidgets)
updateListModel(centerWidgetsModel, centerWidgets) updateListModel(centerWidgetsModel, centerWidgets)
updateListModel(rightWidgetsModel, rightWidgets) updateListModel(rightWidgetsModel, rightWidgets)
@@ -308,25 +334,32 @@ Singleton {
showDock = settings.showDock !== undefined ? settings.showDock : false showDock = settings.showDock !== undefined ? settings.showDock : false
dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false dockAutoHide = settings.dockAutoHide !== undefined ? settings.dockAutoHide : false
dockGroupByApp = settings.dockGroupByApp !== undefined ? settings.dockGroupByApp : 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 cornerRadius = settings.cornerRadius !== undefined ? settings.cornerRadius : 12
notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false notificationOverlayEnabled = settings.notificationOverlayEnabled !== undefined ? settings.notificationOverlayEnabled : false
topBarAutoHide = settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false dankBarAutoHide = settings.dankBarAutoHide !== undefined ? settings.dankBarAutoHide : (settings.topBarAutoHide !== undefined ? settings.topBarAutoHide : false)
topBarOpenOnOverview = settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false dankBarOpenOnOverview = settings.dankBarOpenOnOverview !== undefined ? settings.dankBarOpenOnOverview : (settings.topBarOpenOnOverview !== undefined ? settings.topBarOpenOnOverview : false)
topBarVisible = settings.topBarVisible !== undefined ? settings.topBarVisible : true 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 notificationTimeoutLow = settings.notificationTimeoutLow !== undefined ? settings.notificationTimeoutLow : 5000
notificationTimeoutNormal = settings.notificationTimeoutNormal !== undefined ? settings.notificationTimeoutNormal : 5000 notificationTimeoutNormal = settings.notificationTimeoutNormal !== undefined ? settings.notificationTimeoutNormal : 5000
notificationTimeoutCritical = settings.notificationTimeoutCritical !== undefined ? settings.notificationTimeoutCritical : 0 notificationTimeoutCritical = settings.notificationTimeoutCritical !== undefined ? settings.notificationTimeoutCritical : 0
topBarSpacing = settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4 notificationPopupPosition = settings.notificationPopupPosition !== undefined ? settings.notificationPopupPosition : SettingsData.Position.Top
topBarBottomGap = settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0 dankBarSpacing = settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : (settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4)
topBarInnerPadding = settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 8 dankBarBottomGap = settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : (settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0)
topBarSquareCorners = settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false dankBarInnerPadding = settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : (settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 4)
topBarNoBackground = settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : false dankBarSquareCorners = settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : (settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false)
topBarGothCornersEnabled = settings.topBarGothCornersEnabled !== undefined ? settings.topBarGothCornersEnabled : 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 lockScreenShowPowerActions = settings.lockScreenShowPowerActions !== undefined ? settings.lockScreenShowPowerActions : true
hideBrightnessSlider = settings.hideBrightnessSlider !== undefined ? settings.hideBrightnessSlider : false hideBrightnessSlider = settings.hideBrightnessSlider !== undefined ? settings.hideBrightnessSlider : false
widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch" widgetBackgroundColor = settings.widgetBackgroundColor !== undefined ? settings.widgetBackgroundColor : "sch"
surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s" surfaceBase = settings.surfaceBase !== undefined ? settings.surfaceBase : "s"
screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({}) screenPreferences = settings.screenPreferences !== undefined ? settings.screenPreferences : ({})
animationSpeed = settings.animationSpeed !== undefined ? settings.animationSpeed : SettingsData.AnimationSpeed.Short
applyStoredTheme() applyStoredTheme()
detectAvailableIconThemes() detectAvailableIconThemes()
detectQtTools() detectQtTools()
@@ -349,8 +382,8 @@ Singleton {
"currentThemeName": currentThemeName, "currentThemeName": currentThemeName,
"customThemeFile": customThemeFile, "customThemeFile": customThemeFile,
"matugenScheme": matugenScheme, "matugenScheme": matugenScheme,
"topBarTransparency": topBarTransparency, "dankBarTransparency": dankBarTransparency,
"topBarWidgetTransparency": topBarWidgetTransparency, "dankBarWidgetTransparency": dankBarWidgetTransparency,
"popupTransparency": popupTransparency, "popupTransparency": popupTransparency,
"dockTransparency": dockTransparency, "dockTransparency": dockTransparency,
"use24HourClock": use24HourClock, "use24HourClock": use24HourClock,
@@ -395,9 +428,9 @@ Singleton {
"clockDateFormat": clockDateFormat, "clockDateFormat": clockDateFormat,
"lockDateFormat": lockDateFormat, "lockDateFormat": lockDateFormat,
"mediaSize": mediaSize, "mediaSize": mediaSize,
"topBarLeftWidgets": topBarLeftWidgets, "dankBarLeftWidgets": dankBarLeftWidgets,
"topBarCenterWidgets": topBarCenterWidgets, "dankBarCenterWidgets": dankBarCenterWidgets,
"topBarRightWidgets": topBarRightWidgets, "dankBarRightWidgets": dankBarRightWidgets,
"appLauncherViewMode": appLauncherViewMode, "appLauncherViewMode": appLauncherViewMode,
"spotlightModalViewMode": spotlightModalViewMode, "spotlightModalViewMode": spotlightModalViewMode,
"networkPreference": networkPreference, "networkPreference": networkPreference,
@@ -421,17 +454,22 @@ Singleton {
"showDock": showDock, "showDock": showDock,
"dockAutoHide": dockAutoHide, "dockAutoHide": dockAutoHide,
"dockGroupByApp": dockGroupByApp, "dockGroupByApp": dockGroupByApp,
"dockOpenOnOverview": dockOpenOnOverview,
"dockPosition": dockPosition,
"dockSpacing": dockSpacing,
"dockBottomGap": dockBottomGap,
"cornerRadius": cornerRadius, "cornerRadius": cornerRadius,
"notificationOverlayEnabled": notificationOverlayEnabled, "notificationOverlayEnabled": notificationOverlayEnabled,
"topBarAutoHide": topBarAutoHide, "dankBarAutoHide": dankBarAutoHide,
"topBarOpenOnOverview": topBarOpenOnOverview, "dankBarOpenOnOverview": dankBarOpenOnOverview,
"topBarVisible": topBarVisible, "dankBarVisible": dankBarVisible,
"topBarSpacing": topBarSpacing, "dankBarSpacing": dankBarSpacing,
"topBarBottomGap": topBarBottomGap, "dankBarBottomGap": dankBarBottomGap,
"topBarInnerPadding": topBarInnerPadding, "dankBarInnerPadding": dankBarInnerPadding,
"topBarSquareCorners": topBarSquareCorners, "dankBarSquareCorners": dankBarSquareCorners,
"topBarNoBackground": topBarNoBackground, "dankBarNoBackground": dankBarNoBackground,
"topBarGothCornersEnabled": topBarGothCornersEnabled, "dankBarGothCornersEnabled": dankBarGothCornersEnabled,
"dankBarPosition": dankBarPosition,
"lockScreenShowPowerActions": lockScreenShowPowerActions, "lockScreenShowPowerActions": lockScreenShowPowerActions,
"hideBrightnessSlider": hideBrightnessSlider, "hideBrightnessSlider": hideBrightnessSlider,
"widgetBackgroundColor": widgetBackgroundColor, "widgetBackgroundColor": widgetBackgroundColor,
@@ -439,7 +477,9 @@ Singleton {
"notificationTimeoutLow": notificationTimeoutLow, "notificationTimeoutLow": notificationTimeoutLow,
"notificationTimeoutNormal": notificationTimeoutNormal, "notificationTimeoutNormal": notificationTimeoutNormal,
"notificationTimeoutCritical": notificationTimeoutCritical, "notificationTimeoutCritical": notificationTimeoutCritical,
"screenPreferences": screenPreferences "notificationPopupPosition": notificationPopupPosition,
"screenPreferences": screenPreferences,
"animationSpeed": animationSpeed
}, null, 2)) }, null, 2))
} }
@@ -555,11 +595,11 @@ Singleton {
function applyStoredTheme() { function applyStoredTheme() {
if (typeof Theme !== "undefined") if (typeof Theme !== "undefined")
Theme.switchTheme(currentThemeName, false) Theme.switchTheme(currentThemeName, false, false)
else else
Qt.callLater(() => { Qt.callLater(() => {
if (typeof Theme !== "undefined") if (typeof Theme !== "undefined")
Theme.switchTheme(currentThemeName, false) Theme.switchTheme(currentThemeName, false, false)
}) })
} }
@@ -586,13 +626,13 @@ Singleton {
} }
} }
function setTopBarTransparency(transparency) { function setDankBarTransparency(transparency) {
topBarTransparency = transparency dankBarTransparency = transparency
saveSettings() saveSettings()
} }
function setTopBarWidgetTransparency(transparency) { function setDankBarWidgetTransparency(transparency) {
topBarWidgetTransparency = transparency dankBarWidgetTransparency = transparency
saveSettings() saveSettings()
} }
@@ -727,25 +767,25 @@ Singleton {
saveSettings() saveSettings()
} }
function setTopBarWidgetOrder(order) { function setDankBarWidgetOrder(order) {
topBarWidgetOrder = order dankBarWidgetOrder = order
saveSettings() saveSettings()
} }
function setTopBarLeftWidgets(order) { function setDankBarLeftWidgets(order) {
topBarLeftWidgets = order dankBarLeftWidgets = order
updateListModel(leftWidgetsModel, order) updateListModel(leftWidgetsModel, order)
saveSettings() saveSettings()
} }
function setTopBarCenterWidgets(order) { function setDankBarCenterWidgets(order) {
topBarCenterWidgets = order dankBarCenterWidgets = order
updateListModel(centerWidgetsModel, order) updateListModel(centerWidgetsModel, order)
saveSettings() saveSettings()
} }
function setTopBarRightWidgets(order) { function setDankBarRightWidgets(order) {
topBarRightWidgets = order dankBarRightWidgets = order
updateListModel(rightWidgetsModel, order) updateListModel(rightWidgetsModel, order)
saveSettings() saveSettings()
} }
@@ -778,13 +818,13 @@ Singleton {
widgetDataChanged() widgetDataChanged()
} }
function resetTopBarWidgetsToDefault() { function resetDankBarWidgetsToDefault() {
var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"] var defaultLeft = ["launcherButton", "workspaceSwitcher", "focusedWindow"]
var defaultCenter = ["music", "clock", "weather"] var defaultCenter = ["music", "clock", "weather"]
var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"] var defaultRight = ["systemTray", "clipboard", "notificationButton", "battery", "controlCenterButton"]
topBarLeftWidgets = defaultLeft dankBarLeftWidgets = defaultLeft
topBarCenterWidgets = defaultCenter dankBarCenterWidgets = defaultCenter
topBarRightWidgets = defaultRight dankBarRightWidgets = defaultRight
updateListModel(leftWidgetsModel, defaultLeft) updateListModel(leftWidgetsModel, defaultLeft)
updateListModel(centerWidgetsModel, defaultCenter) updateListModel(centerWidgetsModel, defaultCenter)
updateListModel(rightWidgetsModel, defaultRight) updateListModel(rightWidgetsModel, defaultRight)
@@ -951,6 +991,14 @@ Singleton {
function setShowDock(enabled) { function setShowDock(enabled) {
showDock = 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() saveSettings()
} }
@@ -964,6 +1012,11 @@ Singleton {
saveSettings() saveSettings()
} }
function setdockOpenOnOverview(enabled) {
dockOpenOnOverview = enabled
saveSettings()
}
function setCornerRadius(radius) { function setCornerRadius(radius) {
cornerRadius = radius cornerRadius = radius
saveSettings() saveSettings()
@@ -974,23 +1027,23 @@ Singleton {
saveSettings() saveSettings()
} }
function setTopBarAutoHide(enabled) { function setDankBarAutoHide(enabled) {
topBarAutoHide = enabled dankBarAutoHide = enabled
saveSettings() saveSettings()
} }
function setTopBarOpenOnOverview(enabled) { function setDankBarOpenOnOverview(enabled) {
topBarOpenOnOverview = enabled dankBarOpenOnOverview = enabled
saveSettings() saveSettings()
} }
function setTopBarVisible(visible) { function setDankBarVisible(visible) {
topBarVisible = visible dankBarVisible = visible
saveSettings() saveSettings()
} }
function toggleTopBarVisible() { function toggleDankBarVisible() {
topBarVisible = !topBarVisible dankBarVisible = !dankBarVisible
saveSettings() saveSettings()
} }
@@ -1009,36 +1062,148 @@ Singleton {
saveSettings() saveSettings()
} }
function setTopBarSpacing(spacing) { function setNotificationPopupPosition(position) {
topBarSpacing = spacing notificationPopupPosition = position
saveSettings() saveSettings()
} }
function setTopBarBottomGap(gap) { function sendTestNotifications() {
topBarBottomGap = gap 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() saveSettings()
} }
function setTopBarInnerPadding(padding) { function setDankBarBottomGap(gap) {
topBarInnerPadding = padding dankBarBottomGap = gap
saveSettings() saveSettings()
} }
function setTopBarSquareCorners(enabled) { function setDankBarInnerPadding(padding) {
topBarSquareCorners = enabled dankBarInnerPadding = padding
saveSettings() saveSettings()
} }
function setTopBarNoBackground(enabled) { function setDankBarSquareCorners(enabled) {
topBarNoBackground = enabled dankBarSquareCorners = enabled
saveSettings() saveSettings()
} }
function setTopBarGothCornersEnabled(enabled) { function setDankBarNoBackground(enabled) {
topBarGothCornersEnabled = enabled dankBarNoBackground = enabled
saveSettings() 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) { function setLockScreenShowPowerActions(enabled) {
lockScreenShowPowerActions = enabled lockScreenShowPowerActions = enabled
saveSettings() saveSettings()
@@ -1075,6 +1240,11 @@ Singleton {
return Quickshell.screens.filter(screen => prefs.includes(screen.name)) return Quickshell.screens.filter(screen => prefs.includes(screen.name))
} }
function setAnimationSpeed(speed) {
animationSpeed = speed
saveSettings()
}
function _shq(s) { function _shq(s) {
return "'" + String(s).replace(/'/g, "'\\''") + "'" return "'" + String(s).replace(/'/g, "'\\''") + "'"
} }
@@ -1221,22 +1391,22 @@ Singleton {
IpcHandler { IpcHandler {
function reveal(): string { function reveal(): string {
root.setTopBarVisible(true) root.setDankBarVisible(true)
return "BAR_SHOW_SUCCESS" return "BAR_SHOW_SUCCESS"
} }
function hide(): string { function hide(): string {
root.setTopBarVisible(false) root.setDankBarVisible(false)
return "BAR_HIDE_SUCCESS" return "BAR_HIDE_SUCCESS"
} }
function toggle(): string { function toggle(): string {
root.toggleTopBarVisible() root.toggleDankBarVisible()
return topBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS" return root.dankBarVisible ? "BAR_SHOW_SUCCESS" : "BAR_HIDE_SUCCESS"
} }
function status(): string { function status(): string {
return topBarVisible ? "visible" : "hidden" return root.dankBarVisible ? "visible" : "hidden"
} }
target: "bar" target: "bar"

View File

@@ -2,101 +2,101 @@
// Separated from Theme.qml to keep that file clean // Separated from Theme.qml to keep that file clean
const CatppuccinMocha = { const CatppuccinMocha = {
surface: "#45475a", surface: "#313244",
surfaceText: "#cdd6f4", surfaceText: "#cdd6f4",
surfaceVariant: "#45475a", surfaceVariant: "#313244",
surfaceVariantText: "#a6adc8", surfaceVariantText: "#a6adc8",
background: "#1e1e2e", background: "#1e1e2e",
backgroundText: "#cdd6f4", backgroundText: "#cdd6f4",
outline: "#6c7086", outline: "#6c7086",
surfaceContainer: "#313244", surfaceContainer: "#45475a",
surfaceContainerHigh: "#585b70", surfaceContainerHigh: "#585b70",
surfaceContainerHighest: "#7f849c" surfaceContainerHighest: "#6c7086"
} }
const CatppuccinLatte = { const CatppuccinLatte = {
surface: "#bcc0cc", surface: "#e6e9ef",
surfaceText: "#4c4f69", surfaceText: "#4c4f69",
surfaceVariant: "#bcc0cc", surfaceVariant: "#e6e9ef",
surfaceVariantText: "#6c6f85", surfaceVariantText: "#6c6f85",
background: "#eff1f5", background: "#eff1f5",
backgroundText: "#4c4f69", backgroundText: "#4c4f69",
outline: "#9ca0b0", outline: "#9ca0b0",
surfaceContainer: "#ccd0da", surfaceContainer: "#dce0e8",
surfaceContainerHigh: "#acb0be", surfaceContainerHigh: "#ccd0da",
surfaceContainerHighest: "#8c8fa1" surfaceContainerHighest: "#bcc0cc"
} }
const CatppuccinVariants = { const CatppuccinVariants = {
"cat-rosewater": { "cat-rosewater": {
name: "Rosewater", name: "Rosewater",
dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#8b6b5e", surfaceTint: "#f5e0dc" }, dark: { primary: "#f5e0dc", secondary: "#f2cdcd", primaryText: "#1e1e2e", primaryContainer: "#7d5d56", surfaceTint: "#f5e0dc" },
light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f4d2ca", surfaceTint: "#dc8a78" } light: { primary: "#dc8a78", secondary: "#dd7878", primaryText: "#ffffff", primaryContainer: "#f6e7e3", surfaceTint: "#dc8a78" }
}, },
"cat-flamingo": { "cat-flamingo": {
name: "Flamingo", name: "Flamingo",
dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#885d62", surfaceTint: "#f2cdcd" }, dark: { primary: "#f2cdcd", secondary: "#f5e0dc", primaryText: "#1e1e2e", primaryContainer: "#7a555a", surfaceTint: "#f2cdcd" },
light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f4caca", surfaceTint: "#dd7878" } light: { primary: "#dd7878", secondary: "#dc8a78", primaryText: "#ffffff", primaryContainer: "#f6e5e5", surfaceTint: "#dd7878" }
}, },
"cat-pink": { "cat-pink": {
name: "Pink", name: "Pink",
dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#8b537a", surfaceTint: "#f5c2e7" }, dark: { primary: "#f5c2e7", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#7a3f69", surfaceTint: "#f5c2e7" },
light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7c9e7", surfaceTint: "#ea76cb" } light: { primary: "#ea76cb", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#f7d7ee", surfaceTint: "#ea76cb" }
}, },
"cat-mauve": { "cat-mauve": {
name: "Mauve", name: "Mauve",
dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#61378a", surfaceTint: "#cba6f7" }, dark: { primary: "#cba6f7", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#55307f", surfaceTint: "#cba6f7" },
light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e4d3ff", surfaceTint: "#8839ef" } light: { primary: "#8839ef", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#eadcff", surfaceTint: "#8839ef" }
}, },
"cat-red": { "cat-red": {
name: "Red", name: "Red",
dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#891c3b", surfaceTint: "#f38ba8" }, dark: { primary: "#f38ba8", secondary: "#eba0ac", primaryText: "#1e1e2e", primaryContainer: "#6f2438", surfaceTint: "#f38ba8" },
light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f1b8c4", surfaceTint: "#d20f39" } light: { primary: "#d20f39", secondary: "#e64553", primaryText: "#ffffff", primaryContainer: "#f6d0d6", surfaceTint: "#d20f39" }
}, },
"cat-maroon": { "cat-maroon": {
name: "Maroon", name: "Maroon",
dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#81313f", surfaceTint: "#eba0ac" }, dark: { primary: "#eba0ac", secondary: "#f38ba8", primaryText: "#1e1e2e", primaryContainer: "#6d3641", surfaceTint: "#eba0ac" },
light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f4c3c8", surfaceTint: "#e64553" } light: { primary: "#e64553", secondary: "#d20f39", primaryText: "#ffffff", primaryContainer: "#f7d8dc", surfaceTint: "#e64553" }
}, },
"cat-peach": { "cat-peach": {
name: "Peach", name: "Peach",
dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#90441a", surfaceTint: "#fab387" }, dark: { primary: "#fab387", secondary: "#f9e2af", primaryText: "#1e1e2e", primaryContainer: "#734226", surfaceTint: "#fab387" },
light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffddcc", surfaceTint: "#fe640b" } light: { primary: "#fe640b", secondary: "#df8e1d", primaryText: "#ffffff", primaryContainer: "#ffe4d5", surfaceTint: "#fe640b" }
}, },
"cat-yellow": { "cat-yellow": {
name: "Yellow", name: "Yellow",
dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#8f7342", surfaceTint: "#f9e2af" }, dark: { primary: "#f9e2af", secondary: "#a6e3a1", primaryText: "#1e1e2e", primaryContainer: "#6e5a2f", surfaceTint: "#f9e2af" },
light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff3cc", surfaceTint: "#df8e1d" } light: { primary: "#df8e1d", secondary: "#40a02b", primaryText: "#ffffff", primaryContainer: "#fff6d6", surfaceTint: "#df8e1d" }
}, },
"cat-green": { "cat-green": {
name: "Green", name: "Green",
dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#3c7534", surfaceTint: "#a6e3a1" }, dark: { primary: "#a6e3a1", secondary: "#94e2d5", primaryText: "#1e1e2e", primaryContainer: "#2f5f36", surfaceTint: "#a6e3a1" },
light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#d4f5d4", surfaceTint: "#40a02b" } light: { primary: "#40a02b", secondary: "#179299", primaryText: "#ffffff", primaryContainer: "#dff4e0", surfaceTint: "#40a02b" }
}, },
"cat-teal": { "cat-teal": {
name: "Teal", name: "Teal",
dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2a7468", surfaceTint: "#94e2d5" }, dark: { primary: "#94e2d5", secondary: "#89dceb", primaryText: "#1e1e2e", primaryContainer: "#2e5e59", surfaceTint: "#94e2d5" },
light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#ccf2f2", surfaceTint: "#179299" } light: { primary: "#179299", secondary: "#04a5e5", primaryText: "#ffffff", primaryContainer: "#daf3f1", surfaceTint: "#179299" }
}, },
"cat-sky": { "cat-sky": {
name: "Sky", name: "Sky",
dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#196e7e", surfaceTint: "#89dceb" }, dark: { primary: "#89dceb", secondary: "#74c7ec", primaryText: "#1e1e2e", primaryContainer: "#24586a", surfaceTint: "#89dceb" },
light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#ccebff", surfaceTint: "#04a5e5" } light: { primary: "#04a5e5", secondary: "#209fb5", primaryText: "#ffffff", primaryContainer: "#dbf1fb", surfaceTint: "#04a5e5" }
}, },
"cat-sapphire": { "cat-sapphire": {
name: "Sapphire", name: "Sapphire",
dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#0a597f", surfaceTint: "#74c7ec" }, dark: { primary: "#74c7ec", secondary: "#89b4fa", primaryText: "#1e1e2e", primaryContainer: "#1f4d6f", surfaceTint: "#74c7ec" },
light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#d0f0f5", surfaceTint: "#209fb5" } light: { primary: "#209fb5", secondary: "#1e66f5", primaryText: "#ffffff", primaryContainer: "#def3f8", surfaceTint: "#209fb5" }
}, },
"cat-blue": { "cat-blue": {
name: "Blue", name: "Blue",
dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#19468d", surfaceTint: "#89b4fa" }, dark: { primary: "#89b4fa", secondary: "#b4befe", primaryText: "#1e1e2e", primaryContainer: "#243f75", surfaceTint: "#89b4fa" },
light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#ccd9ff", surfaceTint: "#1e66f5" } light: { primary: "#1e66f5", secondary: "#7287fd", primaryText: "#ffffff", primaryContainer: "#e0e9ff", surfaceTint: "#1e66f5" }
}, },
"cat-lavender": { "cat-lavender": {
name: "Lavender", name: "Lavender",
dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#4a5091", surfaceTint: "#b4befe" }, dark: { primary: "#b4befe", secondary: "#cba6f7", primaryText: "#1e1e2e", primaryContainer: "#3f4481", surfaceTint: "#b4befe" },
light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#dde1ff", surfaceTint: "#7287fd" } light: { primary: "#7287fd", secondary: "#8839ef", primaryText: "#ffffff", primaryContainer: "#e5e8ff", surfaceTint: "#7287fd" }
} }
} }
@@ -290,9 +290,9 @@ const StockThemes = {
background: "#131315", background: "#131315",
backgroundText: "#e4e2e3", backgroundText: "#e4e2e3",
outline: "#929092", outline: "#929092",
surfaceContainer: "#2a2a2a", surfaceContainer: "#353535",
surfaceContainerHigh: "#2a2a2b", surfaceContainerHigh: "#424242",
surfaceContainerHighest: "#353535", surfaceContainerHighest: "#505050",
error: "#ffb4ab", error: "#ffb4ab",
warning: "#3f4759", warning: "#3f4759",
info: "#595e6c", info: "#595e6c",
@@ -476,8 +476,9 @@ const StockThemes = {
background: "#ffffff", background: "#ffffff",
backgroundText: "#1a1a1a", backgroundText: "#1a1a1a",
outline: "#757577", outline: "#757577",
surfaceContainer: "#f5f5f6", surfaceContainer: "#e8e8ea",
surfaceContainerHigh: "#eaeaeb", surfaceContainerHigh: "#dcdcde",
surfaceContainerHighest: "#d0d0d2",
error: "#ba1a1a", error: "#ba1a1a",
warning: "#f9e79f", warning: "#f9e79f",
info: "#5d6475", info: "#5d6475",

View File

@@ -16,11 +16,12 @@ Singleton {
readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true" readonly property bool envDisableMatugen: Quickshell.env("DMS_DISABLE_MATUGEN") === "1" || Quickshell.env("DMS_DISABLE_MATUGEN") === "true"
readonly property real popupDistance: 4 // ! TODO - Synchronize with niri/hyprland gaps?
readonly property real popupDistance: 2
property string currentTheme: "blue" property string currentTheme: "blue"
property string currentThemeCategory: "generic" property string currentThemeCategory: "generic"
property bool isLightMode: false property bool isLightMode: typeof SessionData !== "undefined" ? SessionData.isLightMode : false
readonly property string dynamic: "dynamic" readonly property string dynamic: "dynamic"
readonly property string custom : "custom" readonly property string custom : "custom"
@@ -82,11 +83,13 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", stateDir]) Quickshell.execDetached(["mkdir", "-p", stateDir])
matugenCheck.running = true matugenCheck.running = true
if (typeof SessionData !== "undefined") if (typeof SessionData !== "undefined") {
SessionData.isLightModeChanged.connect(root.onLightModeChanged) SessionData.isLightModeChanged.connect(root.onLightModeChanged)
isLightMode = SessionData.isLightMode
}
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) { if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
switchTheme(SettingsData.currentThemeName, false) switchTheme(SettingsData.currentThemeName, false, false)
} }
} }
@@ -232,11 +235,22 @@ Singleton {
property color shadowMedium: Qt.rgba(0, 0, 0, 0.08) property color shadowMedium: Qt.rgba(0, 0, 0, 0.08)
property color shadowStrong: Qt.rgba(0, 0, 0, 0.3) property color shadowStrong: Qt.rgba(0, 0, 0, 0.3)
property int shorterDuration: 100 readonly property var animationDurations: [
property int shortDuration: 150 { shorter: 0, short: 0, medium: 0, long: 0, extraLong: 0 },
property int mediumDuration: 300 { shorter: 50, short: 75, medium: 150, long: 250, extraLong: 500 },
property int longDuration: 500 { shorter: 100, short: 150, medium: 300, long: 500, extraLong: 1000 },
property int extraLongDuration: 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 standardEasing: Easing.OutCubic
property int emphasizedEasing: Easing.OutQuart property int emphasizedEasing: Easing.OutQuart
@@ -256,8 +270,8 @@ Singleton {
property real iconSizeLarge: 32 property real iconSizeLarge: 32
property real panelTransparency: 0.85 property real panelTransparency: 0.85
property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.topBarWidgetTransparency !== undefined ? SettingsData.topBarWidgetTransparency : 0.85 property real widgetTransparency: typeof SettingsData !== "undefined" && SettingsData.dankBarWidgetTransparency !== undefined ? SettingsData.dankBarWidgetTransparency : 1.0
property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 0.92 property real popupTransparency: typeof SettingsData !== "undefined" && SettingsData.popupTransparency !== undefined ? SettingsData.popupTransparency : 1.0
function screenTransition() { function screenTransition() {
CompositorService.isNiri && NiriService.doScreenTransition() CompositorService.isNiri && NiriService.doScreenTransition()
@@ -266,7 +280,12 @@ Singleton {
function switchTheme(themeName, savePrefs = true, enableTransition = true) { function switchTheme(themeName, savePrefs = true, enableTransition = true) {
if (enableTransition) { if (enableTransition) {
screenTransition() screenTransition()
themeTransitionTimer.themeName = themeName
themeTransitionTimer.savePrefs = savePrefs
themeTransitionTimer.restart()
return
} }
if (themeName === dynamic) { if (themeName === dynamic) {
currentTheme = dynamic currentTheme = dynamic
currentThemeCategory = dynamic currentThemeCategory = dynamic
@@ -278,7 +297,6 @@ Singleton {
} }
} else { } else {
currentTheme = themeName currentTheme = themeName
// Determine category based on theme name
if (StockThemes.isCatppuccinVariant(themeName)) { if (StockThemes.isCatppuccinVariant(themeName)) {
currentThemeCategory = "catppuccin" currentThemeCategory = "catppuccin"
} else { } else {
@@ -291,8 +309,15 @@ Singleton {
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
function setLightMode(light, savePrefs = true) { function setLightMode(light, savePrefs = true, enableTransition = false) {
if (enableTransition) {
screenTransition() screenTransition()
lightModeTransitionTimer.lightMode = light
lightModeTransitionTimer.savePrefs = savePrefs
lightModeTransitionTimer.restart()
return
}
isLightMode = light isLightMode = light
if (savePrefs && typeof SessionData !== "undefined") if (savePrefs && typeof SessionData !== "undefined")
SessionData.setLightMode(isLightMode) SessionData.setLightMode(isLightMode)
@@ -301,11 +326,10 @@ Singleton {
} }
function toggleLightMode(savePrefs = true) { function toggleLightMode(savePrefs = true) {
setLightMode(!isLightMode, savePrefs) setLightMode(!isLightMode, savePrefs, true)
} }
function forceGenerateSystemThemes() { function forceGenerateSystemThemes() {
screenTransition()
if (!matugenAvailable) { if (!matugenAvailable) {
return return
} }
@@ -329,8 +353,10 @@ Singleton {
} }
function switchThemeCategory(category, defaultTheme) { function switchThemeCategory(category, defaultTheme) {
currentThemeCategory = category screenTransition()
switchTheme(defaultTheme, true, false) themeCategoryTransitionTimer.category = category
themeCategoryTransitionTimer.defaultTheme = defaultTheme
themeCategoryTransitionTimer.restart()
} }
function getCatppuccinColor(variantName) { function getCatppuccinColor(variantName) {
@@ -354,7 +380,6 @@ Singleton {
} }
function loadCustomTheme(themeData) { function loadCustomTheme(themeData) {
screenTransition()
if (themeData.dark || themeData.light) { if (themeData.dark || themeData.light) {
const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark" const colorMode = (typeof SessionData !== "undefined" && SessionData.isLightMode) ? "light" : "dark"
const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light const selectedTheme = themeData[colorMode] || themeData.dark || themeData.light
@@ -638,6 +663,7 @@ Singleton {
qtApplier.running = true qtApplier.running = true
} }
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a); }
Process { Process {
id: matugenCheck id: matugenCheck
@@ -837,12 +863,12 @@ Singleton {
} }
function light(): string { function light(): string {
root.setLightMode(true) root.setLightMode(true, true, true)
return "light" return "light"
} }
function dark(): string { function dark(): string {
root.setLightMode(false) root.setLightMode(false, true, true)
return "dark" return "dark"
} }
@@ -850,4 +876,35 @@ Singleton {
return root.isLightMode ? "light" : "dark" 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)
}
}
} }

View File

@@ -53,7 +53,7 @@ QtObject {
modal.hide() modal.hide()
event.accepted = true 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) { if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true modal.keyboardNavigationActive = true
modal.selectedIndex = 0 modal.selectedIndex = 0
@@ -62,7 +62,7 @@ QtObject {
selectNext() selectNext()
event.accepted = true 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) { if (!modal.keyboardNavigationActive) {
modal.keyboardNavigationActive = true modal.keyboardNavigationActive = true
modal.selectedIndex = 0 modal.selectedIndex = 0
@@ -74,6 +74,42 @@ QtObject {
selectPrevious() selectPrevious()
event.accepted = true 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)) { } else if (event.key === Qt.Key_Delete && (event.modifiers & Qt.ShiftModifier)) {
modal.clearAll() modal.clearAll()
modal.hide() modal.hide()

View File

@@ -93,6 +93,48 @@ DankModal {
selectedButton = 1 selectedButton = 1
event.accepted = true event.accepted = true
break 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: case Qt.Key_Tab:
keyboardNavigation = true keyboardNavigation = true
selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2 selectedButton = selectedButton === -1 ? 0 : (selectedButton + 1) % 2

View File

@@ -22,7 +22,7 @@ PanelWindow {
property bool closeOnEscapeKey: true property bool closeOnEscapeKey: true
property bool closeOnBackgroundClick: true property bool closeOnBackgroundClick: true
property string animationType: "scale" property string animationType: "scale"
property int animationDuration: Theme.shorterDuration property int animationDuration: Theme.shortDuration
property var animationEasing: Theme.emphasizedEasing property var animationEasing: Theme.emphasizedEasing
property color backgroundColor: Theme.surfaceContainer property color backgroundColor: Theme.surfaceContainer
property color borderColor: Theme.outlineMedium property color borderColor: Theme.outlineMedium
@@ -34,6 +34,7 @@ PanelWindow {
property bool shouldHaveFocus: shouldBeVisible property bool shouldHaveFocus: shouldBeVisible
property bool allowFocusOverride: false property bool allowFocusOverride: false
property bool allowStacking: false property bool allowStacking: false
property bool keepContentLoaded: false
signal opened signal opened
signal dialogClosed signal dialogClosed
@@ -90,7 +91,7 @@ PanelWindow {
Timer { Timer {
id: closeTimer id: closeTimer
interval: animationDuration + 50 interval: animationDuration + 100
onTriggered: { onTriggered: {
visible = false visible = false
} }
@@ -158,7 +159,6 @@ PanelWindow {
border.width: root.borderWidth border.width: root.borderWidth
layer.enabled: root.enableShadow layer.enabled: root.enableShadow
opacity: root.shouldBeVisible ? 1 : 0 opacity: root.shouldBeVisible ? 1 : 0
scale: root.animationType === "scale" ? (root.shouldBeVisible ? 1 : 0.9) : 1
transform: root.animationType === "slide" ? slideTransform : null transform: root.animationType === "slide" ? slideTransform : null
Translate { Translate {
@@ -172,7 +172,7 @@ PanelWindow {
id: contentLoader id: contentLoader
anchors.fill: parent anchors.fill: parent
active: root.visible active: root.keepContentLoaded || root.shouldBeVisible || root.visible
asynchronous: false 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 { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
@@ -207,8 +198,8 @@ PanelWindow {
objectName: "modalFocusScope" objectName: "modalFocusScope"
anchors.fill: parent anchors.fill: parent
visible: root.visible // Only active when the modal is visible visible: root.shouldBeVisible || root.visible
focus: root.visible focus: root.shouldBeVisible
Keys.onEscapePressed: event => { Keys.onEscapePressed: event => {
if (root.closeOnEscapeKey && shouldHaveFocus) { if (root.closeOnEscapeKey && shouldHaveFocus) {
root.close() root.close()
@@ -223,7 +214,7 @@ PanelWindow {
Connections { Connections {
function onShouldHaveFocusChanged() { function onShouldHaveFocusChanged() {
if (shouldHaveFocus && visible) { if (shouldHaveFocus && shouldBeVisible) {
Qt.callLater(() => focusScope.forceActiveFocus()) Qt.callLater(() => focusScope.forceActiveFocus())
} }
} }

View File

@@ -239,7 +239,12 @@ DankModal {
return return
} }
if (!keyboardNavigationActive) { 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 keyboardNavigationActive = true
if (currentPath !== homeDir) { if (currentPath !== homeDir) {
backButtonFocused = true backButtonFocused = true
@@ -281,6 +286,69 @@ DankModal {
} }
event.accepted = true event.accepted = true
break 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: case Qt.Key_Left:
if (backButtonFocused) if (backButtonFocused)
return return

View File

@@ -57,13 +57,11 @@ DankModal {
modalFocusScope.Keys.onPressed: (event) => { modalFocusScope.Keys.onPressed: (event) => {
switch (event.key) { switch (event.key) {
case Qt.Key_Up: case Qt.Key_Up:
case Qt.Key_Backtab:
selectedIndex = (selectedIndex - 1 + optionCount) % optionCount; selectedIndex = (selectedIndex - 1 + optionCount) % optionCount;
event.accepted = true; event.accepted = true;
break; break;
case Qt.Key_Down: case Qt.Key_Down:
selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true;
break;
case Qt.Key_Tab: case Qt.Key_Tab:
selectedIndex = (selectedIndex + 1) % optionCount; selectedIndex = (selectedIndex + 1) % optionCount;
event.accepted = true; event.accepted = true;
@@ -78,6 +76,30 @@ DankModal {
} }
event.accepted = true; event.accepted = true;
break; 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;
} }
} }

View File

@@ -67,7 +67,7 @@ Item {
visible: active visible: active
asynchronous: true asynchronous: true
sourceComponent: TopBarTab { sourceComponent: DankBarTab {
} }
} }

View File

@@ -18,7 +18,7 @@ Rectangle {
"text": "Weather", "text": "Weather",
"icon": "cloud" "icon": "cloud"
}, { }, {
"text": "Top Bar", "text": "Dank Bar",
"icon": "toolbar" "icon": "toolbar"
}, { }, {
"text": "Widgets", "text": "Widgets",
@@ -83,7 +83,7 @@ Rectangle {
width: parent.width - Theme.spacingS * 2 width: parent.width - Theme.spacingS * 2
height: 44 height: 44
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: isActive ? Theme.primaryContainer : tabMouseArea.containsMouse ? Theme.surfaceHover : "transparent" color: isActive ? Theme.primaryContainer : tabMouseArea.containsMouse ? Theme.surfaceHover : Theme.withAlpha(Theme.primaryContainer, 0)
Row { Row {
anchors.left: parent.left anchors.left: parent.left

View File

@@ -33,6 +33,46 @@ Item {
} else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") { } else if (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") {
appLauncher.selectPreviousInRow() appLauncher.selectPreviousInRow()
event.accepted = true 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) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
appLauncher.launchSelected() appLauncher.launchSelected()
event.accepted = true event.accepted = true
@@ -124,7 +164,7 @@ Item {
else if (appLauncher.model.count > 0) else if (appLauncher.model.count > 0)
appLauncher.launchApp(appLauncher.model.get(0)) appLauncher.launchApp(appLauncher.model.get(0))
event.accepted = true 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 event.accepted = false
} }
} }

View File

@@ -32,11 +32,7 @@ DankModal {
function hide() { function hide() {
spotlightOpen = false spotlightOpen = false
close() close()
if (contentLoader.item && contentLoader.item.appLauncher) { cleanupTimer.restart()
contentLoader.item.appLauncher.searchQuery = ""
contentLoader.item.appLauncher.selectedIndex = 0
contentLoader.item.appLauncher.setCategory("All")
}
} }
function toggle() { function toggle() {
@@ -55,6 +51,7 @@ DankModal {
borderColor: Theme.outlineMedium borderColor: Theme.outlineMedium
borderWidth: 1 borderWidth: 1
enableShadow: true enableShadow: true
keepContentLoaded: true
onVisibleChanged: () => { onVisibleChanged: () => {
if (visible && !spotlightOpen) { if (visible && !spotlightOpen) {
show() show()
@@ -72,6 +69,19 @@ DankModal {
} }
content: spotlightContent 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 { Connections {
function onCloseAllModalsExcept(excludedModal) { function onCloseAllModalsExcept(excludedModal) {
if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) { if (excludedModal !== spotlightModal && !allowStacking && spotlightOpen) {

View File

@@ -12,7 +12,6 @@ import qs.Widgets
DankPopout { DankPopout {
id: appDrawerPopout id: appDrawerPopout
property string triggerSection: "left"
property var triggerScreen: null property var triggerScreen: null
// Setting to Exclusive, so virtual keyboards can send input to app drawer // Setting to Exclusive, so virtual keyboards can send input to app drawer
@@ -33,9 +32,9 @@ DankPopout {
popupWidth: 520 popupWidth: 520
popupHeight: 600 popupHeight: 600
triggerX: Theme.spacingL triggerX: Theme.spacingL
triggerY: Math.max(26 + SettingsData.topBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.topBarInnerPadding)) + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
triggerWidth: 40 triggerWidth: 40
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
onShouldBeVisibleChanged: { onShouldBeVisibleChanged: {
@@ -128,6 +127,44 @@ DankPopout {
return 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)) { if (!searchField.activeFocus && event.text && /[a-zA-Z0-9\s]/.test(event.text)) {
searchField.forceActiveFocus() searchField.forceActiveFocus()
searchField.insertText(event.text) searchField.insertText(event.text)

View File

@@ -7,6 +7,7 @@ Item {
property string expandedSection: "" property string expandedSection: ""
property var expandedWidgetData: null property var expandedWidgetData: null
property var bluetoothCodecSelector: null
Loader { Loader {
width: parent.width width: parent.width
@@ -42,7 +43,17 @@ Item {
Component { Component {
id: bluetoothDetailComponent id: bluetoothDetailComponent
BluetoothDetail {} 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 { Component {

View File

@@ -13,6 +13,8 @@ Column {
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var model: null property var model: null
property var expandedWidgetData: null property var expandedWidgetData: null
property var bluetoothCodecSelector: null
property bool darkModeTransitionPending: false
signal expandClicked(var widgetData, int globalIndex) signal expandClicked(var widgetData, int globalIndex)
signal removeWidget(int index) signal removeWidget(int index)
@@ -24,6 +26,7 @@ Column {
property var currentRowWidgets: [] property var currentRowWidgets: []
property real currentRowWidth: 0 property real currentRowWidth: 0
property int expandedRowIndex: -1 property int expandedRowIndex: -1
property var colorPickerModal: null
function calculateRowsAndWidgets() { function calculateRowsAndWidgets() {
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex) return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
@@ -130,6 +133,8 @@ Column {
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
} else if (id === "diskUsage") { } else if (id === "diskUsage") {
return diskUsagePillComponent return diskUsagePillComponent
} else if (id === "colorPicker") {
return colorPickerPillComponent
} else { } else {
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
} }
@@ -158,6 +163,7 @@ Column {
visible: active visible: active
expandedSection: root.expandedSection expandedSection: root.expandedSection
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
bluetoothCodecSelector: root.bluetoothCodecSelector
} }
} }
} }
@@ -530,7 +536,13 @@ Column {
} }
} }
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0 iconRotation: {
if (widgetData.id !== "darkMode") return 0
if (darkModeTransitionPending) {
return SessionData.isLightMode ? 0 : 180
}
return SessionData.isLightMode ? 180 : 0
}
isActive: { isActive: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
@@ -549,6 +561,14 @@ Column {
enabled: !root.editMode enabled: !root.editMode
onIconRotationCompleted: {
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
root.darkModeTransitionPending = false
Theme.screenTransition()
Theme.toggleLightMode()
}
}
onClicked: { onClicked: {
if (root.editMode) if (root.editMode)
return return
@@ -561,7 +581,7 @@ enabled: !root.editMode
} }
case "darkMode": case "darkMode":
{ {
Theme.toggleLightMode() root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb":
@@ -602,7 +622,13 @@ enabled: !root.editMode
} }
} }
iconRotation: widgetData.id === "darkMode" && SessionData.isLightMode ? 180 : 0 iconRotation: {
if (widgetData.id !== "darkMode") return 0
if (darkModeTransitionPending) {
return SessionData.isLightMode ? 0 : 180
}
return SessionData.isLightMode ? 180 : 0
}
isActive: { isActive: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
@@ -621,6 +647,14 @@ enabled: !root.editMode
enabled: !root.editMode enabled: !root.editMode
onIconRotationCompleted: {
if (root.darkModeTransitionPending && widgetData.id === "darkMode") {
root.darkModeTransitionPending = false
Theme.screenTransition()
Theme.toggleLightMode()
}
}
onClicked: { onClicked: {
if (root.editMode) if (root.editMode)
return return
@@ -633,7 +667,7 @@ enabled: !root.editMode
} }
case "darkMode": case "darkMode":
{ {
Theme.toggleLightMode() root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb":
@@ -669,4 +703,16 @@ enabled: !root.editMode
} }
} }
} }
Component {
id: colorPickerPillComponent
ColorPickerPill {
property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0
width: parent.width
height: 60
colorPickerModal: root.colorPickerModal
}
}
} }

View File

@@ -10,7 +10,7 @@ import qs.Common
import qs.Modules.ControlCenter import qs.Modules.ControlCenter
import qs.Modules.ControlCenter.Widgets import qs.Modules.ControlCenter.Widgets
import qs.Modules.ControlCenter.Details import qs.Modules.ControlCenter.Details
import qs.Modules.TopBar import qs.Modules.DankBar
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.ControlCenter.Components import qs.Modules.ControlCenter.Components
@@ -22,7 +22,6 @@ DankPopout {
property string expandedSection: "" property string expandedSection: ""
property bool powerOptionsExpanded: false property bool powerOptionsExpanded: false
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
property bool editMode: false property bool editMode: false
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
@@ -66,9 +65,9 @@ DankPopout {
popupWidth: 550 popupWidth: 550
popupHeight: Math.min((triggerScreen?.height ?? 1080) - 100, contentLoader.item && contentLoader.item.implicitHeight > 0 ? contentLoader.item.implicitHeight + 20 : 400) 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 triggerX: (triggerScreen?.width ?? 1920) - 600 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.popupDistance triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
triggerWidth: 80 triggerWidth: 80
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
shouldBeVisible: false shouldBeVisible: false
visible: shouldBeVisible visible: shouldBeVisible
@@ -102,7 +101,7 @@ DankPopout {
property alias bluetoothCodecSelector: bluetoothCodecSelector property alias bluetoothCodecSelector: bluetoothCodecSelector
color: { color: {
const transparency = Theme.popupTransparency || 0.92 const transparency = Theme.popupTransparency
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
return Qt.rgba(surface.r, surface.g, surface.b, transparency) return Qt.rgba(surface.r, surface.g, surface.b, transparency)
} }
@@ -153,6 +152,8 @@ DankPopout {
expandedWidgetIndex: root.expandedWidgetIndex expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
model: widgetModel model: widgetModel
bluetoothCodecSelector: bluetoothCodecSelector
colorPickerModal: root.colorPickerModal
onExpandClicked: (widgetData, globalIndex) => { onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex root.expandedWidgetIndex = globalIndex
root.expandedWidgetData = widgetData root.expandedWidgetData = widgetData
@@ -222,4 +223,6 @@ DankPopout {
id: batteryDetailComponent id: batteryDetailComponent
BatteryDetail {} BatteryDetail {}
} }
property var colorPickerModal: null
} }

View File

@@ -116,6 +116,14 @@ QtObject {
"enabled": DgopService.dgopAvailable, "enabled": DgopService.dgopAvailable,
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined, "warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
"allowMultiple": true "allowMultiple": true
},
{
"id": "colorPicker",
"text": "Color Picker",
"description": "Choose colors from palette",
"icon": "palette",
"type": "action",
"enabled": true
} }
] ]

View File

@@ -20,11 +20,7 @@ Row {
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
@@ -34,7 +30,9 @@ Row {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (defaultSink) { if (defaultSink) {
AudioService.suppressOSD = true
defaultSink.audio.muted = !defaultSink.audio.muted defaultSink.audio.muted = !defaultSink.audio.muted
AudioService.suppressOSD = false
} }
} }
} }
@@ -71,6 +69,9 @@ Row {
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
onIsDraggingChanged: {
AudioService.suppressOSD = isDragging
}
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (defaultSink) { if (defaultSink) {
defaultSink.audio.volume = newValue / 100.0 defaultSink.audio.volume = newValue / 100.0

View File

@@ -18,17 +18,13 @@ Row {
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse color: iconArea.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.12)
: "transparent" : Theme.withAlpha(Theme.primary, 0)
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: DisplayService.devices.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: function(event) { onClicked: function(event) {
if (DisplayService.devices.length > 1) { if (DisplayService.devices.length > 1) {
@@ -41,6 +37,22 @@ Row {
} }
} }
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 { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: { name: {
@@ -141,4 +153,10 @@ Row {
onObjectRemoved: (index, object) => deviceMenu.removeItem(object) onObjectRemoved: (index, object) => deviceMenu.removeItem(object)
} }
} }
Loader {
id: tooltipLoader
active: false
sourceComponent: DankTooltip {}
}
} }

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

View File

@@ -41,7 +41,7 @@ Rectangle {
readonly property color _labelSecondary: Theme.surfaceVariantText readonly property color _labelSecondary: Theme.surfaceVariantText
readonly property color _tileBgActive: Theme.primary readonly property color _tileBgActive: Theme.primary
readonly property color _tileBgInactive: { readonly property color _tileBgInactive: {
const transparency = Theme.popupTransparency || 0.92 const transparency = Theme.popupTransparency
const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1) const surface = Theme.surfaceContainer || Qt.rgba(0.1, 0.1, 0.1, 1)
return Qt.rgba(surface.r, surface.g, surface.b, transparency) return Qt.rgba(surface.r, surface.g, surface.b, transparency)
} }

View File

@@ -20,11 +20,7 @@ Row {
height: Theme.iconSize + Theme.spacingS * 2 height: Theme.iconSize + Theme.spacingS * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
radius: (Theme.iconSize + Theme.spacingS * 2) / 2 radius: (Theme.iconSize + Theme.spacingS * 2) / 2
color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: iconArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
MouseArea { MouseArea {
id: iconArea id: iconArea
@@ -34,7 +30,9 @@ Row {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (defaultSource) { if (defaultSource) {
AudioService.suppressOSD = true
defaultSource.audio.muted = !defaultSource.audio.muted defaultSource.audio.muted = !defaultSource.audio.muted
AudioService.suppressOSD = false
} }
} }
} }
@@ -69,6 +67,9 @@ Row {
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
thumbOutlineColor: Theme.surfaceContainer thumbOutlineColor: Theme.surfaceContainer
trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh trackColor: root.sliderTrackColor.a > 0 ? root.sliderTrackColor : Theme.surfaceContainerHigh
onIsDraggingChanged: {
AudioService.suppressOSD = isDragging
}
onSliderValueChanged: function(newValue) { onSliderValueChanged: function(newValue) {
if (defaultSource) { if (defaultSource) {
defaultSource.audio.volume = newValue / 100.0 defaultSource.audio.volume = newValue / 100.0

View File

@@ -91,13 +91,6 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -12,6 +12,7 @@ Rectangle {
property real iconRotation: 0 property real iconRotation: 0
signal clicked() signal clicked()
signal iconRotationCompleted()
width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48 width: parent ? ((parent.width - parent.spacing * 3) / 4) : 48
height: 48 height: 48
@@ -58,6 +59,7 @@ Rectangle {
size: Theme.iconSize size: Theme.iconSize
color: isActive ? _tileIconActive : _tileIconInactive color: isActive ? _tileIconActive : _tileIconInactive
rotation: iconRotation rotation: iconRotation
onRotationCompleted: root.iconRotationCompleted()
} }
MouseArea { MouseArea {
@@ -69,13 +71,6 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View File

@@ -14,6 +14,7 @@ Rectangle {
property real iconRotation: 0 property real iconRotation: 0
signal clicked() signal clicked()
signal iconRotationCompleted()
width: parent ? parent.width : 200 width: parent ? parent.width : 200
height: 60 height: 60
@@ -46,7 +47,7 @@ Rectangle {
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(_containerBg) : "transparent" color: mouseArea.containsMouse ? hoverTint(_containerBg) : Theme.withAlpha(_containerBg, 0)
opacity: mouseArea.containsMouse ? 0.08 : 0.0 opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity { Behavior on opacity {
@@ -66,6 +67,7 @@ Rectangle {
color: isActive ? Theme.primaryContainer : Theme.primary color: isActive ? Theme.primaryContainer : Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
rotation: root.iconRotation rotation: root.iconRotation
onRotationCompleted: root.iconRotationCompleted()
} }
Item { Item {
@@ -110,13 +112,6 @@ Rectangle {
onClicked: root.clicked() onClicked: root.clicked()
} }
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: Theme.shortDuration duration: Theme.shortDuration

View 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
}
}

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

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

View 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

File diff suppressed because it is too large Load Diff

View 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
}
}
}
}
}
}

View File

@@ -11,7 +11,6 @@ import qs.Widgets
DankPopout { DankPopout {
id: root id: root
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -45,9 +44,9 @@ DankPopout {
popupWidth: 400 popupWidth: 400
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
triggerX: Screen.width - 380 - Theme.spacingL triggerX: Screen.width - 380 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.popupDistance triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
triggerWidth: 70 triggerWidth: 70
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
shouldBeVisible: false shouldBeVisible: false
visible: shouldBeVisible visible: shouldBeVisible

View File

@@ -13,7 +13,6 @@ import qs.Widgets
DankPopout { DankPopout {
id: root id: root
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -27,9 +26,9 @@ DankPopout {
popupWidth: 360 popupWidth: 360
popupHeight: Math.min(Screen.height - 100, contentLoader.item ? contentLoader.item.implicitHeight : 260) popupHeight: Math.min(Screen.height - 100, contentLoader.item ? contentLoader.item.implicitHeight : 260)
triggerX: Screen.width - 380 - Theme.spacingL triggerX: Screen.width - 380 - Theme.spacingL
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.popupDistance triggerY: Theme.barHeight - 4 + SettingsData.dankBarSpacing
triggerWidth: 70 triggerWidth: 70
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
shouldBeVisible: false shouldBeVisible: false
visible: shouldBeVisible visible: shouldBeVisible

View 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
}
}
}
}
}
}

View 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
}
}

View 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();
}
}
}

View 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
}
}
}

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

View File

@@ -5,21 +5,23 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false property bool isActive: false
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real widgetHeight: 30 property real widgetThickness: 30
property real barHeight: 48 property real barThickness: 48
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))
signal clicked() signal clicked()
width: colorPickerIcon.width + horizontalPadding * 2 width: isVertical ? widgetThickness : (colorPickerIcon.width + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (colorPickerIcon.height + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -43,12 +45,10 @@ Rectangle {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
console.log("Color picker button clicked!")
root.colorPickerRequested(); root.colorPickerRequested();
} }
} }
// Signal to notify TopBar to open color picker
signal colorPickerRequested() signal colorPickerRequested()
} }

View File

@@ -6,6 +6,8 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false property bool isActive: false
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
@@ -14,17 +16,17 @@ Rectangle {
property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon
property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon
property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon
property real widgetHeight: 30 property real widgetThickness: 30
property real barHeight: 48 property real barThickness: 48
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))
signal clicked() signal clicked()
width: controlIndicators.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (controlIndicators.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (controlColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -32,9 +34,106 @@ Rectangle {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); 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 { Row {
id: controlIndicators id: controlIndicators
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
@@ -156,11 +255,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
root.clicked(); root.clicked();
} }

View File

@@ -7,21 +7,23 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
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))
width: cpuContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (cpuColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -43,11 +45,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { if (root.toggleProcessList) {
@@ -57,9 +58,47 @@ Rectangle {
} }
} }
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 { Row {
id: cpuContent id: cpuContent
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: 3 spacing: 3

View File

@@ -7,21 +7,23 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
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))
width: cpuTempContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (cpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -43,11 +45,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { if (root.toggleProcessList) {
@@ -57,9 +58,47 @@ Rectangle {
} }
} }
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 { Row {
id: cpuTempContent id: cpuTempContent
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: 3 spacing: 3

View File

@@ -7,10 +7,13 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var widgetData: null property var widgetData: null
property real widgetHeight: 30 property var parentScreen: null
property real widgetThickness: 30
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/" property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
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 var selectedMount: { property var selectedMount: {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) { if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
@@ -46,11 +49,11 @@ Rectangle {
return parseFloat(percentStr) || 0 return parseFloat(percentStr) || 0
} }
width: diskContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (diskContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (diskColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent" return "transparent"
} }
@@ -100,10 +103,77 @@ Rectangle {
target: SettingsData 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 { Row {
id: diskContent id: diskContent
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: 3 spacing: 3

View File

@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Services import qs.Services
@@ -9,14 +10,45 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var parentScreen
property bool compactMode: SettingsData.focusedWindowCompactMode property bool compactMode: SettingsData.focusedWindowCompactMode
property int availableWidth: 400 property int availableWidth: 400
property real widgetHeight: 30 property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property int baseWidth: contentRow.implicitWidth + horizontalPadding * 2 readonly property int baseWidth: contentRow.implicitWidth + horizontalPadding * 2
readonly property int maxNormalWidth: 456 readonly property int maxNormalWidth: 456
readonly property int maxCompactWidth: 288 readonly property int maxCompactWidth: 288
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel 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: { readonly property bool hasWindowsOnCurrentWorkspace: {
if (CompositorService.isNiri) { if (CompositorService.isNiri) {
let currentWorkspaceId = null let currentWorkspaceId = null
@@ -54,15 +86,15 @@ Rectangle {
return activeWindow && activeWindow.title return activeWindow && activeWindow.title
} }
width: !hasWindowsOnCurrentWorkspace ? 0 : (compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)) width: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : (compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth)))
height: widgetHeight height: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : widgetThickness)
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (!activeWindow || !activeWindow.title) { if (!activeWindow || !activeWindow.title) {
return "transparent"; return "transparent";
} }
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -72,11 +104,61 @@ Rectangle {
clip: true clip: true
visible: hasWindowsOnCurrentWorkspace 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 { Row {
id: contentRow id: contentRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingS spacing: Theme.spacingS
visible: !root.isVertical
StyledText { StyledText {
id: appText id: appText
@@ -117,7 +199,6 @@ Rectangle {
return title; return title;
} }
// Remove app name from end of title if it exists there
if (title.endsWith(" - " + appName)) { if (title.endsWith(" - " + appName)) {
return title.substring(0, title.length - (" - " + appName).length); return title.substring(0, title.length - (" - " + appName).length);
} }
@@ -144,7 +225,39 @@ Rectangle {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true 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 {}
} }

View File

@@ -7,6 +7,8 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
@@ -14,10 +16,10 @@ Rectangle {
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property var widgetData: null property var widgetData: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0 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: { property real displayTemp: {
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) { if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
return 0; return 0;
@@ -37,11 +39,11 @@ Rectangle {
const sectionId = sections[s]; const sectionId = sections[s];
let widgets = []; let widgets = [];
if (sectionId === "left") { if (sectionId === "left") {
widgets = SettingsData.topBarLeftWidgets.slice(); widgets = SettingsData.dankBarLeftWidgets.slice();
} else if (sectionId === "center") { } else if (sectionId === "center") {
widgets = SettingsData.topBarCenterWidgets.slice(); widgets = SettingsData.dankBarCenterWidgets.slice();
} else if (sectionId === "right") { } else if (sectionId === "right") {
widgets = SettingsData.topBarRightWidgets.slice(); widgets = SettingsData.dankBarRightWidgets.slice();
} }
for (let i = 0; i < widgets.length; i++) { for (let i = 0; i < widgets.length; i++) {
const widget = widgets[i]; const widget = widgets[i];
@@ -53,11 +55,11 @@ Rectangle {
"pciId": pciId "pciId": pciId
}; };
if (sectionId === "left") { if (sectionId === "left") {
SettingsData.setTopBarLeftWidgets(widgets); SettingsData.setDankBarLeftWidgets(widgets);
} else if (sectionId === "center") { } else if (sectionId === "center") {
SettingsData.setTopBarCenterWidgets(widgets); SettingsData.setDankBarCenterWidgets(widgets);
} else if (sectionId === "right") { } else if (sectionId === "right") {
SettingsData.setTopBarRightWidgets(widgets); SettingsData.setDankBarRightWidgets(widgets);
} }
return ; return ;
} }
@@ -65,11 +67,11 @@ Rectangle {
} }
} }
width: gpuTempContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (gpuTempContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (gpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -78,20 +80,14 @@ Rectangle {
} }
Component.onCompleted: { Component.onCompleted: {
DgopService.addRef(["gpu"]); 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) { if (widgetData && widgetData.pciId) {
console.log("Adding GPU PCI ID to service:", widgetData.pciId);
DgopService.addGpuPciId(widgetData.pciId); DgopService.addGpuPciId(widgetData.pciId);
} else { } 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; autoSaveTimer.running = true;
} }
} }
Component.onDestruction: { Component.onDestruction: {
DgopService.removeRef(["gpu"]); DgopService.removeRef(["gpu"]);
// Remove this widget's PCI ID from the service
if (widgetData && widgetData.pciId) { if (widgetData && widgetData.pciId) {
DgopService.removeGpuPciId(widgetData.pciId); DgopService.removeGpuPciId(widgetData.pciId);
} }
@@ -117,11 +113,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { 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 { Row {
id: gpuTempContent id: gpuTempContent
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: 3 spacing: 3

View File

@@ -8,17 +8,19 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real widgetHeight: 30 property real widgetThickness: 30
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))
width: idleIcon.width + horizontalPadding * 2 width: isVertical ? widgetThickness : (idleIcon.width + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (idleIcon.height + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }

View File

@@ -10,15 +10,18 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) 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 currentLayout: ""
property string hyprlandKeyboard: "" property string hyprlandKeyboard: ""
width: contentRow.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -47,11 +50,42 @@ Rectangle {
} }
} }
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 { Row {
id: contentRow id: contentRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingS spacing: Theme.spacingS
visible: !root.isVertical
StyledText { StyledText {
text: currentLayout text: currentLayout

View File

@@ -7,17 +7,19 @@ Item {
id: root id: root
property bool isActive: false property bool isActive: false
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "left" property string section: "left"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real widgetHeight: 30 property real widgetThickness: 30
property real barHeight: 48 property real barThickness: 48
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))
signal clicked() signal clicked()
width: Theme.iconSize + horizontalPadding * 2 width: widgetThickness
height: widgetHeight height: widgetThickness
MouseArea { MouseArea {
id: launcherArea id: launcherArea
@@ -27,14 +29,13 @@ Item {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
root.clicked();
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0);
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen;
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width);
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen);
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
root.clicked();
} }
} }
@@ -42,9 +43,9 @@ Item {
id: launcherContent id: launcherContent
anchors.fill: parent anchors.fill: parent
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -55,8 +56,8 @@ Item {
SystemLogo { SystemLogo {
visible: SettingsData.useOSLogo visible: SettingsData.useOSLogo
anchors.centerIn: parent anchors.centerIn: parent
width: Theme.iconSize - 3 width: widgetThickness - 8
height: Theme.iconSize - 3 height: widgetThickness - 8
colorOverride: SettingsData.osLogoColorOverride colorOverride: SettingsData.osLogoColorOverride
brightnessOverride: SettingsData.osLogoBrightness brightnessOverride: SettingsData.osLogoBrightness
contrastOverride: SettingsData.osLogoContrast contrastOverride: SettingsData.osLogoContrast
@@ -64,9 +65,11 @@ Item {
DankIcon { DankIcon {
visible: !SettingsData.useOSLogo visible: !SettingsData.useOSLogo
anchors.centerIn: parent anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
name: "apps" name: "apps"
size: Theme.iconSize - 6 size: widgetThickness - 8
color: Theme.surfaceText color: Theme.surfaceText
} }
} }

View File

@@ -7,6 +7,8 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property bool playerAvailable: activePlayer !== null readonly property bool playerAvailable: activePlayer !== null
property bool compactMode: false property bool compactMode: false
@@ -21,27 +23,36 @@ Rectangle {
} }
} }
readonly property int currentContentWidth: { readonly property int currentContentWidth: {
// Calculate actual content width: if (isVertical) {
// AudioViz (20) + spacing + [text + spacing] + controls (prev:20 + spacing + play:24 + spacing + next:20) + padding return widgetThickness;
}
const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20; const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20;
// ~72px total
const audioVizWidth = 20; const audioVizWidth = 20;
const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth; const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth;
return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0) + horizontalPadding * 2; 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 string section: "center"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
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))
signal clicked() signal clicked()
height: widgetHeight width: currentContentWidth
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius height: currentContentHeight
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -57,6 +68,7 @@ Rectangle {
target: root target: root
opacity: 1 opacity: 1
width: currentContentWidth width: currentContentWidth
height: currentContentHeight
} }
}, },
@@ -67,7 +79,8 @@ Rectangle {
PropertyChanges { PropertyChanges {
target: root target: root
opacity: 0 opacity: 0
width: 0 width: isVertical ? widgetThickness : 0
height: isVertical ? 0 : widgetThickness
} }
} }
@@ -83,7 +96,7 @@ Rectangle {
} }
NumberAnimation { NumberAnimation {
properties: "opacity,width" properties: isVertical ? "opacity,height" : "opacity,width"
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
@@ -96,7 +109,7 @@ Rectangle {
to: "shown" to: "shown"
NumberAnimation { NumberAnimation {
properties: "opacity,width" properties: isVertical ? "opacity,height" : "opacity,width"
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing 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 { Row {
id: mediaRow id: mediaRow
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
@@ -208,13 +319,12 @@ Rectangle {
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
onPressed: { onPressed: {
if (root.popupTarget && root.popupTarget.setTriggerPosition) { if (root.popupTarget && root.popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = root.parentScreen || Screen; const currentScreen = root.parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.width)
const relativeX = globalPos.x - screenX; root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
root.popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, root.width, root.section, currentScreen);
} }
root.clicked(); root.clicked()
} }
} }
@@ -330,7 +440,13 @@ Rectangle {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
}
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
} }
} }

View File

@@ -8,10 +8,13 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property int availableWidth: 400 property int availableWidth: 400
readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2 readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2
readonly property int maxNormalWidth: 456 readonly property int maxNormalWidth: 456
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))
function formatNetworkSpeed(bytesPerSec) { function formatNetworkSpeed(bytesPerSec) {
if (bytesPerSec < 1024) { if (bytesPerSec < 1024) {
@@ -25,11 +28,11 @@ Rectangle {
} }
} }
width: contentRow.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -51,11 +54,53 @@ Rectangle {
cursorShape: Qt.PointingHandCursor 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 { Row {
id: contentRow id: contentRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingS spacing: Theme.spacingS
visible: !root.isVertical
DankIcon { DankIcon {
name: "network_check" name: "network_check"

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

View 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
}
}
}

View File

@@ -7,23 +7,26 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real widgetHeight: 30 property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property bool hasActivePrivacy: PrivacyService.anyPrivacyActive readonly property bool hasActivePrivacy: PrivacyService.anyPrivacyActive
readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive 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 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 width: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0)
height: hasActivePrivacy ? widgetHeight : 0 height: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : (hasActivePrivacy ? widgetThickness : 0)
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: hasActivePrivacy visible: hasActivePrivacy
opacity: hasActivePrivacy ? 1 : 0 opacity: hasActivePrivacy ? 1 : 0
enabled: hasActivePrivacy enabled: hasActivePrivacy
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; 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 { Row {
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
visible: hasActivePrivacy visible: !root.isVertical && hasActivePrivacy
Item { Item {
width: 18 width: 18
@@ -158,7 +227,17 @@ Rectangle {
} }
Behavior on width { 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 { NumberAnimation {
duration: Theme.mediumDuration duration: Theme.mediumDuration

View File

@@ -7,21 +7,23 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property string section: "right"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
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))
width: ramContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (ramColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -43,11 +45,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
DgopService.setSortBy("memory"); DgopService.setSortBy("memory");
if (root.toggleProcessList) { if (root.toggleProcessList) {
@@ -57,9 +58,47 @@ Rectangle {
} }
} }
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 { Row {
id: ramContent id: ramContent
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: 3 spacing: 3

View File

@@ -10,13 +10,14 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "left" property string section: "left"
property var parentScreen property var parentScreen
property var hoveredItem: null property var hoveredItem: null
property var topBar: null property var topBar: null
property real widgetHeight: 30 property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
// The visual root for this window
property Item windowRoot: (Window.window ? Window.window.contentItem : null) property Item windowRoot: (Window.window ? Window.window.contentItem : null)
readonly property var sortedToplevels: { readonly property var sortedToplevels: {
if (SettingsData.runningAppsCurrentWorkspace) { if (SettingsData.runningAppsCurrentWorkspace) {
@@ -25,7 +26,7 @@ Rectangle {
return CompositorService.sortedToplevels; return CompositorService.sortedToplevels;
} }
readonly property int windowCount: sortedToplevels.length readonly property int windowCount: sortedToplevels.length
readonly property int calculatedWidth: { readonly property int calculatedSize: {
if (windowCount === 0) { if (windowCount === 0) {
return 0; return 0;
} }
@@ -37,9 +38,9 @@ Rectangle {
} }
} }
width: calculatedWidth width: isVertical ? widgetThickness : calculatedSize
height: widgetHeight height: isVertical ? calculatedSize : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: windowCount > 0 visible: windowCount > 0
clip: false clip: false
color: { color: {
@@ -47,7 +48,7 @@ Rectangle {
return "transparent"; return "transparent";
} }
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -143,15 +144,19 @@ Rectangle {
} }
} }
Row { Loader {
id: windowRow id: layoutLoader
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: root.isVertical ? columnLayout : rowLayout
}
Component {
id: rowLayout
Row {
spacing: Theme.spacingXS spacing: Theme.spacingXS
Repeater { Repeater {
id: windowRepeater id: windowRepeater
model: sortedToplevels model: sortedToplevels
delegate: Item { delegate: Item {
@@ -288,7 +293,7 @@ Rectangle {
} }
} else if (mouse.button === Qt.RightButton) { } else if (mouse.button === Qt.RightButton) {
if (tooltipLoader.item) { if (tooltipLoader.item) {
tooltipLoader.item.hideTooltip(); tooltipLoader.item.hide();
} }
tooltipLoader.active = false; tooltipLoader.active = false;
@@ -299,29 +304,35 @@ Rectangle {
const screenX = root.parentScreen ? root.parentScreen.x : 0; const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0; const screenY = root.parentScreen ? root.parentScreen.y : 0;
const relativeX = globalPos.x - screenX; const relativeX = globalPos.x - screenX;
const yPos = Theme.barHeight + SettingsData.topBarSpacing - 7; const yPos = root.isVertical ? delegateItem.height / 2 : (Theme.barHeight + SettingsData.dankBarSpacing - 7);
windowContextMenuLoader.item.showAt(relativeX, yPos); windowContextMenuLoader.item.showAt(relativeX, yPos);
} }
} }
} }
onEntered: { onEntered: {
root.hoveredItem = delegateItem; root.hoveredItem = delegateItem;
const globalPos = delegateItem.mapToGlobal(
delegateItem.width / 2, delegateItem.height);
tooltipLoader.active = true; tooltipLoader.active = true;
if (tooltipLoader.item) { if (tooltipLoader.item) {
const tooltipY = Theme.barHeight if (root.isVertical) {
+ SettingsData.topBarSpacing + Theme.spacingXS; const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height / 2);
tooltipLoader.item.showTooltip( const screenX = root.parentScreen ? root.parentScreen.x : 0;
delegateItem.tooltipText, globalPos.x, const screenY = root.parentScreen ? root.parentScreen.y : 0;
tooltipY, root.parentScreen); 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);
const isLeft = root.axis?.edge === "left";
tooltipLoader.item.show(delegateItem.tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft);
} else {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height);
const tooltipY = Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS;
tooltipLoader.item.show(delegateItem.tooltipText, globalPos.x, tooltipY, root.parentScreen, false, false);
}
} }
} }
onExited: { onExited: {
if (root.hoveredItem === delegateItem) { if (root.hoveredItem === delegateItem) {
root.hoveredItem = null; root.hoveredItem = null;
if (tooltipLoader.item) { if (tooltipLoader.item) {
tooltipLoader.item.hideTooltip(); tooltipLoader.item.hide();
} }
tooltipLoader.active = false; tooltipLoader.active = false;
@@ -331,13 +342,205 @@ Rectangle {
} }
} }
} }
}
Component {
id: columnLayout
Column {
spacing: Theme.spacingXS
Repeater {
id: windowRepeater
model: sortedToplevels
delegate: Item {
id: delegateItem
property bool isFocused: modelData.activated
property string appId: modelData.appId || ""
property string windowTitle: modelData.title || "(Unnamed)"
property var toplevelObject: modelData
property string tooltipText: {
let appName = "Unknown";
if (appId) {
const desktopEntry = DesktopEntries.heuristicLookup(appId);
appName = desktopEntry
&& desktopEntry.name ? desktopEntry.name : appId;
}
return appName + (windowTitle ? " • " + windowTitle : "")
}
width: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120)
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: {
if (isFocused) {
return mouseArea.containsMouse ? Qt.rgba(
Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.3) : Qt.rgba(
Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.2);
} else {
return mouseArea.containsMouse ? Qt.rgba(
Theme.primaryHover.r,
Theme.primaryHover.g,
Theme.primaryHover.b,
0.1) : "transparent";
}
}
}
IconImage {
id: iconImg
anchors.left: parent.left
anchors.leftMargin: SettingsData.runningAppsCompactMode ? (parent.width - 18) / 2 : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
width: 18
height: 18
source: {
const moddedId = Paths.moddedAppId(appId)
if (moddedId.toLowerCase().includes("steam_app")) {
return ""
}
return Quickshell.iconPath(DesktopEntries.heuristicLookup(moddedId)?.icon, true)
}
smooth: true
mipmap: true
asynchronous: true
visible: status === Image.Ready
}
DankIcon {
anchors.left: parent.left
anchors.leftMargin: SettingsData.runningAppsCompactMode ? (parent.width - 18) / 2 : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
size: 18
name: "sports_esports"
color: Theme.surfaceText
visible: {
const moddedId = Paths.moddedAppId(appId)
return moddedId.toLowerCase().includes("steam_app")
}
}
Text {
anchors.centerIn: parent
visible: {
const moddedId = Paths.moddedAppId(appId)
const isSteamApp = moddedId.toLowerCase().includes("steam_app")
return !iconImg.visible && !isSteamApp
}
text: {
if (!appId) {
return "?";
}
const desktopEntry = DesktopEntries.heuristicLookup(appId);
if (desktopEntry && desktopEntry.name) {
return desktopEntry.name.charAt(0).toUpperCase();
}
return appId.charAt(0).toUpperCase();
}
font.pixelSize: 10
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledText {
anchors.left: iconImg.right
anchors.leftMargin: Theme.spacingXS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
visible: !SettingsData.runningAppsCompactMode
text: windowTitle
font.pixelSize: Theme.fontSizeMedium - 1
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
maximumLineCount: 1
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton) {
if (toplevelObject) {
toplevelObject.activate();
}
} else if (mouse.button === Qt.RightButton) {
if (tooltipLoader.item) {
tooltipLoader.item.hide();
}
tooltipLoader.active = false;
windowContextMenuLoader.active = true;
if (windowContextMenuLoader.item) {
windowContextMenuLoader.item.currentWindow = toplevelObject;
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, 0);
const screenX = root.parentScreen ? root.parentScreen.x : 0;
const screenY = root.parentScreen ? root.parentScreen.y : 0;
const relativeX = globalPos.x - screenX;
const yPos = root.isVertical ? delegateItem.height / 2 : (Theme.barHeight + SettingsData.dankBarSpacing - 7);
windowContextMenuLoader.item.showAt(relativeX, yPos);
}
}
}
onEntered: {
root.hoveredItem = delegateItem;
tooltipLoader.active = true;
if (tooltipLoader.item) {
if (root.isVertical) {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.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);
const isLeft = root.axis?.edge === "left";
tooltipLoader.item.show(delegateItem.tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft);
} else {
const globalPos = delegateItem.mapToGlobal(delegateItem.width / 2, delegateItem.height);
const tooltipY = Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS;
tooltipLoader.item.show(delegateItem.tooltipText, globalPos.x, tooltipY, root.parentScreen, false, false);
}
}
}
onExited: {
if (root.hoveredItem === delegateItem) {
root.hoveredItem = null;
if (tooltipLoader.item) {
tooltipLoader.item.hide();
}
tooltipLoader.active = false;
}
}
}
}
}
}
}
Loader { Loader {
id: tooltipLoader id: tooltipLoader
active: false active: false
sourceComponent: RunningAppsTooltip {} sourceComponent: DankTooltip {}
} }
Loader { Loader {

View File

@@ -0,0 +1,630 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Services.SystemTray
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var parentWindow: null
property var parentScreen: null
property real widgetThickness: 30
property bool isAtBottom: false
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property int calculatedSize: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0
width: isVertical ? widgetThickness : calculatedSize
height: isVertical ? calculatedSize : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SystemTray.items.values.length === 0) {
return "transparent";
}
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
visible: SystemTray.items.values.length > 0
Loader {
id: layoutLoader
anchors.centerIn: parent
sourceComponent: root.isVertical ? columnComp : rowComp
}
Component {
id: rowComp
Row {
spacing: 0
Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0];
const path = split[1];
const fileName = name.substring(name.lastIndexOf("/") + 1);
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
return `file://${icon}`;
}
return icon;
}
return "";
}
width: 24
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
}
IconImage {
anchors.centerIn: parent
width: 16
height: 16
source: parent.iconSource
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
trayItem.activate();
return ;
}
if (trayItem.hasMenu) {
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}
}
}
}
Component {
id: columnComp
Column {
spacing: 0
Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon === "") {
return "";
}
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0];
const path = split[1];
const fileName = name.substring(name.lastIndexOf("/") + 1);
return `file://${path}/${fileName}`;
}
if (icon.startsWith("/") && !icon.startsWith("file://")) {
return `file://${icon}`;
}
return icon;
}
return "";
}
width: 24
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
}
IconImage {
anchors.centerIn: parent
width: 16
height: 16
source: parent.iconSource
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
trayItem.activate();
return ;
}
if (trayItem.hasMenu) {
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
}
}
}
}
}
}
}
Component {
id: trayMenuComponent
Rectangle {
id: menuRoot
property var trayItem: null
property var anchorItem: null
property var parentScreen: null
property bool isAtBottom: false
property bool isVertical: false
property var axis: null
property bool showMenu: false
property var menuHandle: null
ListModel { id: entryStack }
function topEntry() {
return entryStack.count ? entryStack.get(entryStack.count - 1).handle : null
}
function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
trayItem = item
anchorItem = anchor
parentScreen = screen
isAtBottom = atBottom
isVertical = vertical
axis = axisObj
menuHandle = item?.menu
if (parentScreen) {
for (var i = 0; i < Quickshell.screens.length; i++) {
const s = Quickshell.screens[i]
if (s === parentScreen) {
menuWindow.screen = s
break
}
}
}
showMenu = true
}
function close() {
showMenu = false
}
function showSubMenu(entry) {
if (!entry || !entry.hasChildren) return;
entryStack.append({ handle: entry });
const h = entry.menu || entry;
if (h && typeof h.updateLayout === "function") h.updateLayout();
submenuHydrator.menu = h;
submenuHydrator.open();
Qt.callLater(() => submenuHydrator.close());
}
function goBack() {
if (!entryStack.count) return;
entryStack.remove(entryStack.count - 1);
}
width: 0
height: 0
color: "transparent"
PanelWindow {
id: menuWindow
visible: menuRoot.showMenu && (menuRoot.trayItem?.hasMenu ?? false)
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
property point anchorPos: Qt.point(screen.width / 2, screen.height / 2)
onVisibleChanged: {
if (visible) {
updatePosition()
}
}
function updatePosition() {
if (!menuRoot.anchorItem || !menuRoot.trayItem) {
anchorPos = Qt.point(screen.width / 2, screen.height / 2)
return
}
const globalPos = menuRoot.anchorItem.mapToGlobal(0, 0)
const screenX = screen.x || 0
const screenY = screen.y || 0
const relativeX = globalPos.x - screenX
const relativeY = globalPos.y - screenY
const widgetThickness = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
const effectiveBarThickness = Math.max(widgetThickness + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
if (menuRoot.isVertical) {
const edge = menuRoot.axis?.edge
let targetX
if (edge === "left") {
targetX = effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance
} else {
const popupX = effectiveBarThickness + SettingsData.dankBarSpacing + Theme.popupDistance
targetX = screen.width - popupX
}
anchorPos = Qt.point(targetX, relativeY + menuRoot.anchorItem.height / 2)
} else {
let targetY
if (menuRoot.isAtBottom) {
const popupY = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance
targetY = screen.height - popupY
} else {
targetY = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap + Theme.popupDistance
}
anchorPos = Qt.point(relativeX + menuRoot.anchorItem.width / 2, targetY)
}
}
Rectangle {
id: menuContainer
width: Math.min(500, Math.max(250, menuColumn.implicitWidth + Theme.spacingS * 2))
height: Math.max(40, menuColumn.implicitHeight + Theme.spacingS * 2)
x: {
if (menuRoot.isVertical) {
const edge = menuRoot.axis?.edge
if (edge === "left") {
const targetX = menuWindow.anchorPos.x
return Math.min(menuWindow.screen.width - width - 10, targetX)
} else {
const targetX = menuWindow.anchorPos.x - width
return Math.max(10, targetX)
}
} else {
const left = 10
const right = menuWindow.width - width - 10
const want = menuWindow.anchorPos.x - width / 2
return Math.max(left, Math.min(right, want))
}
}
y: {
if (menuRoot.isVertical) {
const top = 10
const bottom = menuWindow.height - height - 10
const want = menuWindow.anchorPos.y - height / 2
return Math.max(top, Math.min(bottom, want))
} else {
if (menuRoot.isAtBottom) {
const targetY = menuWindow.anchorPos.y - height
return Math.max(10, targetY)
} else {
const targetY = menuWindow.anchorPos.y
return Math.min(menuWindow.screen.height - height - 10, targetY)
}
}
}
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
opacity: menuRoot.showMenu ? 1 : 0
scale: menuRoot.showMenu ? 1 : 0.85
Rectangle {
anchors.fill: parent
anchors.topMargin: 4
anchors.leftMargin: 2
anchors.rightMargin: -2
anchors.bottomMargin: -4
radius: parent.radius
color: Qt.rgba(0, 0, 0, 0.15)
z: parent.z - 1
}
QsMenuAnchor {
id: submenuHydrator
anchor.window: menuWindow
}
QsMenuOpener {
id: rootOpener
menu: menuRoot.menuHandle
}
QsMenuOpener {
id: subOpener
menu: {
const e = menuRoot.topEntry();
return e ? (e.menu || e) : null;
}
}
Column {
id: menuColumn
width: parent.width - Theme.spacingS * 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Theme.spacingS
spacing: 1
Rectangle {
visible: entryStack.count > 0
width: parent.width
height: 28
radius: Theme.cornerRadius
color: backArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
DankIcon {
name: "arrow_back"
size: 16
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Back"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: backArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: menuRoot.goBack()
}
}
Rectangle {
visible: entryStack.count > 0
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
Repeater {
model: entryStack.count
? (subOpener.children ? subOpener.children
: (menuRoot.topEntry()?.children || []))
: rootOpener.children
Rectangle {
property var menuEntry: modelData
width: menuColumn.width
height: menuEntry?.isSeparator ? 1 : 28
radius: menuEntry?.isSeparator ? 0 : Theme.cornerRadius
color: {
if (menuEntry?.isSeparator) {
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
}
return itemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
}
MouseArea {
id: itemArea
anchors.fill: parent
enabled: !menuEntry?.isSeparator && (menuEntry?.enabled !== false)
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (!menuEntry || menuEntry.isSeparator) return;
if (menuEntry.hasChildren) {
menuRoot.showSubMenu(menuEntry);
} else {
if (typeof menuEntry.activate === "function") {
menuEntry.activate();
} else if (typeof menuEntry.triggered === "function") {
menuEntry.triggered();
}
Qt.createQmlObject('import QtQuick; Timer { interval: 80; running: true; repeat: false; onTriggered: menuRoot.close() }', menuRoot);
}
}
}
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
visible: !menuEntry?.isSeparator
Rectangle {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: menuEntry?.buttonType !== undefined && menuEntry.buttonType !== 0
radius: menuEntry?.buttonType === 2 ? 8 : 2
border.width: 1
border.color: Theme.outline
color: "transparent"
Rectangle {
anchors.centerIn: parent
width: parent.width - 6
height: parent.height - 6
radius: parent.radius - 3
color: Theme.primary
visible: menuEntry?.checkState === 2
}
DankIcon {
anchors.centerIn: parent
name: "check"
size: 10
color: Theme.primaryText
visible: menuEntry?.buttonType === 1 && menuEntry?.checkState === 2
}
}
Item {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: menuEntry?.icon && menuEntry.icon !== ""
Image {
anchors.fill: parent
source: menuEntry?.icon || ""
sourceSize.width: 16
sourceSize.height: 16
fillMode: Image.PreserveAspectFit
smooth: true
}
}
StyledText {
text: menuEntry?.text || ""
font.pixelSize: Theme.fontSizeSmall
color: (menuEntry?.enabled !== false) ? Theme.surfaceText : Theme.surfaceTextMedium
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
width: Math.max(150, parent.width - 64)
wrapMode: Text.NoWrap
}
Item {
width: 16
height: 16
anchors.verticalCenter: parent.verticalCenter
DankIcon {
anchors.centerIn: parent
name: "chevron_right"
size: 14
color: Theme.surfaceText
visible: menuEntry?.hasChildren ?? false
}
}
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
MouseArea {
anchors.fill: parent
z: -1
onClicked: menuRoot.close()
}
}
}
}
property var currentTrayMenu: null
function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
if (currentTrayMenu) {
currentTrayMenu.destroy()
}
currentTrayMenu = trayMenuComponent.createObject(null)
if (currentTrayMenu) {
currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom, vertical ?? false, axisObj)
}
}
}

View File

@@ -0,0 +1,154 @@
import QtQuick
import qs.Common
import qs.Services
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))
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking
signal clicked()
width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2)
height: isVertical ? widgetThickness : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: statusIcon
anchors.centerIn: parent
visible: root.isVertical
name: {
if (isChecking) return "refresh";
if (SystemUpdateService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (SystemUpdateService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimation
target: statusIcon
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIcon.rotation = 0
}
}
}
}
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.isVertical && root.hasUpdates && !root.isChecking
}
Row {
id: updaterIcon
anchors.centerIn: parent
spacing: Theme.spacingXS
visible: !root.isVertical
DankIcon {
id: statusIconHorizontal
anchors.verticalCenter: parent.verticalCenter
name: {
if (isChecking) return "refresh";
if (SystemUpdateService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (SystemUpdateService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimationHorizontal
target: statusIconHorizontal
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIconHorizontal.rotation = 0
}
}
}
}
StyledText {
id: countText
anchors.verticalCenter: parent.verticalCenter
text: SystemUpdateService.updateCount.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
visible: hasUpdates && !isChecking
}
}
MouseArea {
id: updaterArea
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.clicked();
}
}
}

View File

@@ -0,0 +1,103 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property int widgetThickness: 28
property int barThickness: 32
property string section: "right"
property var popupTarget: null
property var parentScreen: null
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal toggleVpnPopup()
width: isVertical ? widgetThickness : (Theme.iconSize + horizontalPadding * 2)
height: isVertical ? (Theme.iconSize + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = clickArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: icon
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
size: Theme.iconSize - 6
color: VpnService.connected ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
}
Loader {
id: tooltipLoader
active: false
sourceComponent: DankTooltip {}
}
MouseArea {
id: clickArea
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.toggleVpnPopup();
}
onEntered: {
if (root.parentScreen && !(popupTarget && popupTarget.shouldBeVisible)) {
tooltipLoader.active = true
if (tooltipLoader.item) {
let tooltipText = ""
if (!VpnService.connected) {
tooltipText = "VPN Disconnected"
} else {
const names = VpnService.activeNames || []
if (names.length <= 1) {
tooltipText = "VPN Connected • " + (names[0] || "")
} else {
tooltipText = "VPN Connected • " + names[0] + " +" + (names.length - 1)
}
}
if (root.isVertical) {
const globalPos = mapToGlobal(width / 2, 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)
const isLeft = root.axis?.edge === "left"
tooltipLoader.item.show(tooltipText, screenX + tooltipX, relativeY, root.parentScreen, isLeft, !isLeft)
} else {
const globalPos = mapToGlobal(width / 2, height)
const tooltipY = Theme.barHeight + SettingsData.dankBarSpacing + Theme.spacingXS
tooltipLoader.item.show(tooltipText, globalPos.x, tooltipY, root.parentScreen, false, false)
}
}
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
}
tooltipLoader.active = false
}
}
}

View File

@@ -6,21 +6,23 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "center" property string section: "center"
property var popupTarget: null property var popupTarget: null
property var parentScreen: null property var parentScreen: null
property real barHeight: 48 property real barThickness: 48
property real widgetHeight: 30 property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
signal clicked() signal clicked()
visible: SettingsData.weatherEnabled visible: SettingsData.weatherEnabled
width: visible ? Math.min(100, weatherRow.implicitWidth + horizontalPadding * 2) : 0 width: isVertical ? widgetThickness : (visible ? Math.min(100, weatherRow.implicitWidth + horizontalPadding * 2) : 0)
height: widgetHeight height: isVertical ? (weatherColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) { if (SettingsData.dankBarNoBackground) {
return "transparent"; return "transparent";
} }
@@ -32,9 +34,37 @@ Rectangle {
service: WeatherService service: WeatherService
} }
Column {
id: weatherColumn
visible: root.isVertical
anchors.centerIn: parent
spacing: 1
DankIcon {
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
size: Theme.iconSize - 4
color: Theme.primary
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: {
const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
if (temp === undefined || temp === null || temp === 0) {
return "--";
}
return temp;
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
Row { Row {
id: weatherRow id: weatherRow
visible: !root.isVertical
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
@@ -69,11 +99,10 @@ Rectangle {
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0); const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen; const currentScreen = parentScreen || Screen
const screenX = currentScreen.x || 0; const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
const relativeX = globalPos.x - screenX; popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
} }
root.clicked(); root.clicked();
} }

View File

@@ -10,6 +10,8 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string screenName: "" property string screenName: ""
property real widgetHeight: 30 property real widgetHeight: 30
property int currentWorkspace: { property int currentWorkspace: {
@@ -192,7 +194,9 @@ Rectangle {
return currentMonitor.activeWorkspace?.id ?? 1 return currentMonitor.activeWorkspace?.id ?? 1
} }
readonly property real padding: (widgetHeight - workspaceRow.implicitHeight) / 2 readonly property real padding: isVertical
? Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
: (widgetHeight - workspaceRow.implicitHeight) / 2
function getRealWorkspaces() { function getRealWorkspaces() {
return root.workspaceList.filter(ws => { return root.workspaceList.filter(ws => {
@@ -221,11 +225,11 @@ Rectangle {
} }
} }
width: workspaceRow.implicitWidth + padding * 2 width: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
height: widgetHeight height: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.topBarNoBackground) if (SettingsData.dankBarNoBackground)
return "transparent" return "transparent"
const baseColor = Theme.widgetBaseBackgroundColor const baseColor = Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
@@ -261,11 +265,12 @@ Rectangle {
} }
} }
Row { Flow {
id: workspaceRow id: workspaceRow
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingS spacing: Theme.spacingS
flow: isVertical ? Flow.TopToBottom : Flow.LeftToRight
Repeater { Repeater {
model: root.workspaceList model: root.workspaceList
@@ -332,16 +337,32 @@ Rectangle {
} }
width: { width: {
if (root.isVertical) {
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5;
} else {
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) { if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons); const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0); const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
const baseWidth = isActive ? root.widgetHeight * 1.0 + Theme.spacingXS : root.widgetHeight * 0.8; const baseWidth = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7;
return baseWidth + iconsWidth; return baseWidth + iconsWidth;
} }
return isActive ? root.widgetHeight * 1.2 : root.widgetHeight * 0.8; return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7;
} }
height: SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6 }
radius: height / 2 height: {
if (root.isVertical) {
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
const iconsHeight = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
const baseHeight = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7;
return baseHeight + iconsHeight;
}
return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7;
} else {
return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5;
}
}
radius: Math.min(width, height) / 2
color: isActive ? Theme.primary : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha color: isActive ? Theme.primary : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha
Behavior on width { Behavior on width {
@@ -352,10 +373,20 @@ Rectangle {
} }
} }
Behavior on height {
enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.centerIn: parent
width: root.isVertical ? parent.width + Theme.spacingXL : parent.width
height: root.isVerical ? parent.height : parent.height + Theme.spacingXL
hoverEnabled: !isPlaceholder hoverEnabled: !isPlaceholder
cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor
enabled: !isPlaceholder enabled: !isPlaceholder
@@ -372,15 +403,20 @@ Rectangle {
} }
} }
// Loader for App Icons
Loader { Loader {
id: appIconsLoader id: appIconsLoader
anchors.fill: parent anchors.fill: parent
active: SettingsData.showWorkspaceApps active: SettingsData.showWorkspaceApps
sourceComponent: Item { sourceComponent: Item {
Row { Loader {
id: contentRow id: contentRow
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: root.isVertical ? columnLayout : rowLayout
}
Component {
id: rowLayout
Row {
spacing: 4 spacing: 4
visible: loadedIcons.length > 0 visible: loadedIcons.length > 0
@@ -446,6 +482,76 @@ Rectangle {
} }
} }
} }
Component {
id: columnLayout
Column {
spacing: 4
visible: loadedIcons.length > 0
Repeater {
model: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
delegate: Item {
width: 18
height: 18
IconImage {
id: appIcon
property var windowId: modelData.windowId
anchors.fill: parent
source: modelData.icon
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isSteamApp
}
DankIcon {
anchors.centerIn: parent
size: 18
name: "sports_esports"
color: Theme.surfaceText
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp
}
MouseArea {
id: appMouseArea
hoverEnabled: true
anchors.fill: parent
enabled: isActive
cursorShape: Qt.PointingHandCursor
onClicked: {
if (CompositorService.isHyprland) {
Hyprland.dispatch(`focuswindow address:${appIcon.windowId}`)
} else if (CompositorService.isNiri) {
NiriService.focusWindow(appIcon.windowId)
}
}
}
Rectangle {
visible: modelData.count > 1 && !isActive
width: 12
height: 12
radius: 6
color: "black"
border.color: "white"
border.width: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
z: 2
Text {
anchors.centerIn: parent
text: modelData.count
font.pixelSize: 8
color: "white"
}
}
}
}
}
}
}
} }
// Loader for Custom Name Icon // Loader for Custom Name Icon

View File

@@ -13,30 +13,33 @@ DankPopout {
id: root id: root
property bool dashVisible: false property bool dashVisible: false
property string triggerSection: "center"
property var triggerScreen: null property var triggerScreen: null
property int currentTabIndex: 0 property int currentTabIndex: 0
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
if (section === "center") { triggerSection = section
triggerScreen = screen
triggerY = y
if (section === "center" && (SettingsData.dankBarPosition === SettingsData.Position.Top || SettingsData.dankBarPosition === SettingsData.Position.Bottom)) {
const screenWidth = screen ? screen.width : Screen.width const screenWidth = screen ? screen.width : Screen.width
triggerX = (screenWidth - popupWidth) / 2 triggerX = (screenWidth - popupWidth) / 2
triggerWidth = popupWidth triggerWidth = popupWidth
} else if (section === "center" && (SettingsData.dankBarPosition === SettingsData.Position.Left || SettingsData.dankBarPosition === SettingsData.Position.Right)) {
const screenHeight = screen ? screen.height : Screen.height
triggerX = (screenHeight - popupHeight) / 2
triggerWidth = popupHeight
} else { } else {
triggerX = x triggerX = x
triggerWidth = width triggerWidth = width
} }
triggerY = y
triggerSection = section
triggerScreen = screen
} }
popupWidth: 700 popupWidth: 700
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500
triggerX: Screen.width - 620 - Theme.spacingL triggerX: Screen.width - 620 - Theme.spacingL
triggerY: Math.max(26 + SettingsData.topBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.topBarInnerPadding)) + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
triggerWidth: 80 triggerWidth: 80
positioning: "center"
shouldBeVisible: dashVisible shouldBeVisible: dashVisible
visible: shouldBeVisible visible: shouldBeVisible

View File

@@ -338,28 +338,28 @@ Item {
} }
} }
Rectangle { Popup {
id: audioDevicesDropdown id: audioDevicesDropdown
width: 280 width: 280
height: audioDevicesButton.devicesExpanded ? Math.max(200, Math.min(280, audioDevicesDropdown.availableDevices.length * 50 + 100)) : 0 height: audioDevicesButton.devicesExpanded ? Math.max(200, Math.min(280, audioDevicesDropdown.availableDevices.length * 50 + 100)) : 0
x: parent.width + Theme.spacingS x: root.width + Theme.spacingS
y: audioDevicesButton.y - 50 y: audioDevicesButton.y - 50
visible: audioDevicesButton.devicesExpanded visible: audioDevicesButton.devicesExpanded
clip: true closePolicy: Popup.NoAutoClose
z: 150 modal: false
dim: false
padding: 0
property var availableDevices: Pipewire.nodes.values.filter(node => { property var availableDevices: Pipewire.nodes.values.filter(node => {
return node.audio && node.isSink && !node.isStream return node.audio && node.isSink && !node.isStream
}) })
background: Rectangle {
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98) color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
border.width: 2 border.width: 2
radius: Theme.cornerRadius * 2 radius: Theme.cornerRadius * 2
opacity: audioDevicesButton.devicesExpanded ? 1 : 0
// Drop shadow effect
layer.enabled: true layer.enabled: true
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
@@ -369,6 +369,7 @@ Item {
shadowColor: Qt.rgba(0, 0, 0, 0.4) shadowColor: Qt.rgba(0, 0, 0, 0.4)
shadowOpacity: 0.7 shadowOpacity: 0.7
} }
}
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
@@ -378,8 +379,22 @@ Item {
} }
} }
Behavior on opacity { enter: Transition {
NumberAnimation { NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: Anims.durShort
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: Anims.durShort duration: Anims.durShort
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard easing.bezierCurve: Anims.standard
@@ -471,7 +486,6 @@ Item {
onClicked: { onClicked: {
if (modelData) { if (modelData) {
Pipewire.preferredDefaultAudioSink = modelData Pipewire.preferredDefaultAudioSink = modelData
console.log("Current default sink after change:", AudioService.sink ? AudioService.sink.name : "null")
} }
audioDevicesButton.devicesExpanded = false audioDevicesButton.devicesExpanded = false
} }
@@ -486,23 +500,24 @@ Item {
} }
} }
Rectangle { Popup {
id: playerSelectorDropdown id: playerSelectorDropdown
width: 240 width: 240
height: playerSelectorButton.playersExpanded ? Math.max(180, Math.min(240, (root.allPlayers?.length || 0) * 50 + 80)) : 0 height: playerSelectorButton.playersExpanded ? Math.max(180, Math.min(240, (root.allPlayers?.length || 0) * 50 + 80)) : 0
x: parent.width + Theme.spacingS x: root.width + Theme.spacingS
y: playerSelectorButton.y - 50 y: playerSelectorButton.y - 50
visible: playerSelectorButton.playersExpanded visible: playerSelectorButton.playersExpanded
clip: true closePolicy: Popup.NoAutoClose
z: 150 modal: false
dim: false
padding: 0
background: Rectangle {
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98) color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.98)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
border.width: 2 border.width: 2
radius: Theme.cornerRadius * 2 radius: Theme.cornerRadius * 2
opacity: playerSelectorButton.playersExpanded ? 1 : 0
layer.enabled: true layer.enabled: true
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
@@ -512,6 +527,7 @@ Item {
shadowColor: Qt.rgba(0, 0, 0, 0.4) shadowColor: Qt.rgba(0, 0, 0, 0.4)
shadowOpacity: 0.7 shadowOpacity: 0.7
} }
}
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
@@ -521,8 +537,22 @@ Item {
} }
} }
Behavior on opacity { enter: Transition {
NumberAnimation { NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: Anims.durShort
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: Anims.durShort duration: Anims.durShort
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard easing.bezierCurve: Anims.standard
@@ -797,13 +827,18 @@ Item {
spacing: Theme.spacingM spacing: Theme.spacingM
height: parent.height height: parent.height
Item {
width: 50
height: 50
anchors.verticalCenter: parent.verticalCenter
visible: activePlayer && activePlayer.shuffleSupported
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: 20 radius: 20
anchors.centerIn: parent
color: shuffleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: shuffleArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
anchors.verticalCenter: parent.verticalCenter
visible: activePlayer && activePlayer.shuffleSupported
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -832,13 +867,19 @@ Item {
} }
} }
} }
}
Item {
width: 50
height: 50
anchors.verticalCenter: parent.verticalCenter
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: 20 radius: 20
anchors.centerIn: parent
color: prevBtnArea.containsMouse ? Theme.surfaceContainerHigh : "transparent" color: prevBtnArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
anchors.verticalCenter: parent.verticalCenter
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -865,13 +906,19 @@ Item {
} }
} }
} }
}
Item {
width: 50
height: 50
anchors.verticalCenter: parent.verticalCenter
Rectangle { Rectangle {
width: 50 width: 50
height: 50 height: 50
radius: 25 radius: 25
anchors.centerIn: parent
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -892,19 +939,25 @@ Item {
layer.effect: MultiEffect { layer.effect: MultiEffect {
shadowEnabled: true shadowEnabled: true
shadowHorizontalOffset: 0 shadowHorizontalOffset: 0
shadowVerticalOffset: 6 shadowVerticalOffset: 0
shadowBlur: 1.0 shadowBlur: 1.0
shadowColor: Qt.rgba(0, 0, 0, 0.3) shadowColor: Qt.rgba(0, 0, 0, 0.3)
shadowOpacity: 0.3 shadowOpacity: 0.3
} }
} }
}
Item {
width: 50
height: 50
anchors.verticalCenter: parent.verticalCenter
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: 20 radius: 20
anchors.centerIn: parent
color: nextBtnArea.containsMouse ? Theme.surfaceContainerHigh : "transparent" color: nextBtnArea.containsMouse ? Theme.surfaceContainerHigh : "transparent"
anchors.verticalCenter: parent.verticalCenter
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -921,14 +974,20 @@ Item {
onClicked: activePlayer && activePlayer.next() onClicked: activePlayer && activePlayer.next()
} }
} }
}
Item {
width: 50
height: 50
anchors.verticalCenter: parent.verticalCenter
visible: activePlayer && activePlayer.loopSupported
Rectangle { Rectangle {
width: 40 width: 40
height: 40 height: 40
radius: 20 radius: 20
anchors.centerIn: parent
color: repeatArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" color: repeatArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
anchors.verticalCenter: parent.verticalCenter
visible: activePlayer && activePlayer.loopSupported
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
@@ -979,6 +1038,7 @@ Item {
} }
} }
} }
}
Rectangle { Rectangle {
id: playerSelectorButton id: playerSelectorButton
@@ -1010,10 +1070,29 @@ Item {
onClicked: { onClicked: {
parent.playersExpanded = !parent.playersExpanded parent.playersExpanded = !parent.playersExpanded
} }
onEntered: {
playerTooltipLoader.active = true
if (playerTooltipLoader.item) {
const p = playerSelectorButton.mapToItem(null, playerSelectorButton.width / 2, 0)
playerTooltipLoader.item.show("Media Player", p.x, p.y - 40, null)
}
}
onExited: {
if (playerTooltipLoader.item) {
playerTooltipLoader.item.hide()
}
playerTooltipLoader.active = false
}
} }
} }
Loader {
id: playerTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
Rectangle { Rectangle {
id: volumeButton id: volumeButton
width: 40 width: 40
@@ -1024,7 +1103,7 @@ Item {
color: volumeButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent" color: volumeButtonArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent"
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1 border.width: 1
z: 100 z: 101
property bool volumeExpanded: false property bool volumeExpanded: false
@@ -1068,24 +1147,106 @@ Item {
} }
Rectangle { Rectangle {
id: audioDevicesButton
width: 40
height: 40
radius: 20
x: parent.width - 40 - Theme.spacingM
y: 240
color: audioDevicesArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent"
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1
z: 100
property bool devicesExpanded: false
DankIcon {
anchors.centerIn: parent
name: parent.devicesExpanded ? "expand_less" : "speaker"
size: 18
color: Theme.surfaceText
}
MouseArea {
id: audioDevicesArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
parent.devicesExpanded = !parent.devicesExpanded
}
onEntered: {
audioDevicesTooltipLoader.active = true
if (audioDevicesTooltipLoader.item) {
const p = audioDevicesButton.mapToItem(null, audioDevicesButton.width / 2, 0)
audioDevicesTooltipLoader.item.show("Output Device", p.x, p.y - 40, null)
}
}
onExited: {
if (audioDevicesTooltipLoader.item) {
audioDevicesTooltipLoader.item.hide()
}
audioDevicesTooltipLoader.active = false
}
}
}
Loader {
id: audioDevicesTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
}
Popup {
id: volumeSliderPanel id: volumeSliderPanel
width: 60 width: 60
height: 180 height: 180
radius: Theme.cornerRadius * 2 x: root.width + Theme.spacingS
x: parent.width + Theme.spacingS
y: volumeButton.y - 50 y: volumeButton.y - 50
visible: volumeButton.volumeExpanded
closePolicy: Popup.NoAutoClose
modal: false
dim: false
padding: 0
background: Rectangle {
radius: Theme.cornerRadius * 2
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1 border.width: 1
visible: volumeButton.volumeExpanded
clip: true
z: 110
opacity: volumeButton.volumeExpanded ? 1 : 0 layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
shadowHorizontalOffset: 0
shadowVerticalOffset: 8
shadowBlur: 1.0
shadowColor: Qt.rgba(0, 0, 0, 0.4)
shadowOpacity: 0.7
}
}
Behavior on opacity { enter: Transition {
NumberAnimation { NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: Anims.durShort
easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: Anims.durShort duration: Anims.durShort
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.standard easing.bezierCurve: Anims.standard
@@ -1108,8 +1269,6 @@ Item {
property bool containsMouse: volumeSliderArea.containsMouse property bool containsMouse: volumeSliderArea.containsMouse
property bool active: volumeSliderArea.containsMouse || volumeSliderArea.pressed || dragging property bool active: volumeSliderArea.containsMouse || volumeSliderArea.pressed || dragging
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a) }
Rectangle { Rectangle {
id: sliderTrack id: sliderTrack
width: parent.width width: parent.width
@@ -1197,7 +1356,7 @@ Item {
} }
} }
scale: active ? 1.05 : 1.0 scale: volumeSlider.active ? 1.05 : 1.0
Behavior on scale { Behavior on scale {
NumberAnimation { NumberAnimation {
@@ -1274,39 +1433,4 @@ Item {
} }
} }
} }
Rectangle {
id: audioDevicesButton
width: 40
height: 40
radius: 20
x: parent.width - 40 - Theme.spacingM
y: 240
color: audioDevicesArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2) : "transparent"
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1
z: 100
property bool devicesExpanded: false
DankIcon {
anchors.centerIn: parent
name: parent.devicesExpanded ? "expand_less" : "speaker"
size: 18
color: Theme.surfaceText
}
MouseArea {
id: audioDevicesArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
parent.devicesExpanded = !parent.devicesExpanded
}
}
}
}
} }

View File

@@ -7,22 +7,49 @@ import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
PanelWindow { pragma ComponentBehavior: Bound
Variants {
id: dockVariants
model: SettingsData.getFilteredScreens("dock")
property var contextMenu
delegate: PanelWindow {
id: dock id: dock
WlrLayershell.namespace: "quickshell:dock" WlrLayershell.namespace: "quickshell:dock"
WlrLayershell.layer: WlrLayershell.Top anchors {
WlrLayershell.exclusiveZone: -1 top: SettingsData.dockPosition === SettingsData.Position.Top
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None bottom: SettingsData.dockPosition === SettingsData.Position.Bottom
left: true
right: true
}
property var modelData property var modelData: item
property var contextMenu
property bool autoHide: SettingsData.dockAutoHide property bool autoHide: SettingsData.dockAutoHide
property real backgroundTransparency: SettingsData.dockTransparency property real backgroundTransparency: SettingsData.dockTransparency
property bool groupByApp: SettingsData.dockGroupByApp property bool groupByApp: SettingsData.dockGroupByApp
property bool contextMenuOpen: (contextMenu && contextMenu.visible && contextMenu.screen === modelData) readonly property real widgetHeight: Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
readonly property real effectiveBarHeight: Math.max(widgetHeight + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
readonly property real barSpacing: {
// Only add spacing if bar is visible, horizontal (Top/Bottom), and at same position as dock
const barIsHorizontal = (SettingsData.dankBarPosition === SettingsData.Position.Top || SettingsData.dankBarPosition === SettingsData.Position.Bottom)
const samePosition = (SettingsData.dockPosition === SettingsData.dankBarPosition)
return (SettingsData.dankBarVisible && barIsHorizontal && samePosition)
? (SettingsData.dankBarSpacing + effectiveBarHeight + SettingsData.dankBarBottomGap)
: 0
}
readonly property real dockMargin: SettingsData.dockSpacing
readonly property real positionSpacing: barSpacing + SettingsData.dockBottomGap
readonly property real _dpr: (dock.screen && dock.screen.devicePixelRatio) ? dock.screen.devicePixelRatio : 1
function px(v) { return Math.round(v * _dpr) / _dpr }
property bool contextMenuOpen: (dockVariants.contextMenu && dockVariants.contextMenu.visible && dockVariants.contextMenu.screen === modelData)
property bool windowIsFullscreen: { property bool windowIsFullscreen: {
if (!ToplevelManager.activeToplevel) { if (!ToplevelManager.activeToplevel) {
return false return false
@@ -31,7 +58,28 @@ PanelWindow {
const fullscreenApps = ["vlc", "mpv", "kodi", "steam", "lutris", "wine", "dosbox"] const fullscreenApps = ["vlc", "mpv", "kodi", "steam", "lutris", "wine", "dosbox"]
return fullscreenApps.some(app => activeWindow.appId && activeWindow.appId.toLowerCase().includes(app)) return fullscreenApps.some(app => activeWindow.appId && activeWindow.appId.toLowerCase().includes(app))
} }
property bool reveal: (!autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen) && !windowIsFullscreen property bool revealSticky: false
Timer {
id: revealHold
interval: 250
repeat: false
onTriggered: dock.revealSticky = false
}
property bool reveal: {
if (CompositorService.isNiri && NiriService.inOverview) {
return SettingsData.dockOpenOnOverview
}
return (!autoHide || dockMouseArea.containsMouse || dockApps.requestDockShow || contextMenuOpen || revealSticky) && !windowIsFullscreen
}
onContextMenuOpenChanged: {
if (!contextMenuOpen && autoHide && !dockMouseArea.containsMouse) {
revealSticky = true
revealHold.restart()
}
}
Connections { Connections {
target: SettingsData target: SettingsData
@@ -44,37 +92,50 @@ PanelWindow {
visible: SettingsData.showDock visible: SettingsData.showDock
color: "transparent" color: "transparent"
anchors {
bottom: true
left: true
right: true
}
margins { exclusiveZone: {
left: 0 if (!SettingsData.showDock || autoHide) return -1
right: 0 if (barSpacing > 0) return -1
return px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap)
} }
implicitHeight: 100
exclusiveZone: autoHide ? -1 : 65 - 16
mask: Region { mask: Region {
item: dockMouseArea item: dockMouseArea
} }
Item {
id: dockCore
anchors.fill: parent
Connections {
target: dockMouseArea
function onContainsMouseChanged() {
if (dockMouseArea.containsMouse) {
dock.revealSticky = true
revealHold.stop()
} else {
if (dock.autoHide && !dock.contextMenuOpen) {
revealHold.restart()
}
}
}
}
MouseArea { MouseArea {
id: dockMouseArea id: dockMouseArea
property real currentScreen: modelData ? modelData : dock.screen property real currentScreen: modelData ? modelData : dock.screen
property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920 property real screenWidth: currentScreen ? currentScreen.geometry.width : 1920
property real maxDockWidth: Math.min(screenWidth * 0.8, 1200) property real maxDockWidth: Math.min(screenWidth * 0.8, 1200)
height: dock.reveal ? 65 : 20 height: dock.reveal ? px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap) : 1
width: dock.reveal ? Math.min(dockBackground.width + 32, maxDockWidth) : Math.min(Math.max(dockBackground.width + 64, 200), screenWidth * 0.5) width: dock.reveal ? Math.min(dockBackground.implicitWidth + 32, maxDockWidth) : Math.min(Math.max(dockBackground.implicitWidth + 64, 200), screenWidth * 0.5)
anchors { anchors {
bottom: parent.bottom top: SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top
bottom: SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
} }
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.NoButton
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
@@ -83,13 +144,21 @@ PanelWindow {
} }
} }
Item { Item {
id: dockContainer id: dockContainer
anchors.fill: parent anchors.fill: parent
transform: Translate { transform: Translate {
id: dockSlide id: dockSlide
y: dock.reveal ? 0 : 60 y: {
if (dock.reveal) return 0
if (SettingsData.dockPosition === SettingsData.Position.Bottom) {
return 60
} else {
return -60
}
}
Behavior on y { Behavior on y {
NumberAnimation { NumberAnimation {
@@ -103,16 +172,17 @@ PanelWindow {
id: dockBackground id: dockBackground
objectName: "dockBackground" objectName: "dockBackground"
anchors { anchors {
top: parent.top top: SettingsData.dockPosition === SettingsData.Position.Bottom ? undefined : parent.top
bottom: parent.bottom bottom: SettingsData.dockPosition === SettingsData.Position.Bottom ? parent.bottom : undefined
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
} }
anchors.topMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? 0 : barSpacing + 4
anchors.bottomMargin: SettingsData.dockPosition === SettingsData.Position.Bottom ? barSpacing + 1 : 0
width: dockApps.implicitWidth + 12 implicitWidth: dockApps.implicitWidth + SettingsData.dockSpacing * 2
height: parent.height - 8 implicitHeight: dockApps.implicitHeight + SettingsData.dockSpacing * 2
width: implicitWidth
anchors.topMargin: 4 height: implicitHeight
anchors.bottomMargin: 1
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency) color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, backgroundTransparency)
radius: Theme.cornerRadius radius: Theme.cornerRadius
@@ -132,10 +202,10 @@ PanelWindow {
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 4 anchors.topMargin: SettingsData.dockSpacing
anchors.bottomMargin: 4 anchors.bottomMargin: SettingsData.dockSpacing
contextMenu: dock.contextMenu contextMenu: dockVariants.contextMenu
groupByApp: dock.groupByApp groupByApp: dock.groupByApp
} }
} }
@@ -171,15 +241,15 @@ PanelWindow {
property string tooltipText: hoveredButton ? hoveredButton.tooltipText : "" property string tooltipText: hoveredButton ? hoveredButton.tooltipText : ""
visible: hoveredButton !== null && tooltipText !== "" visible: hoveredButton !== null && tooltipText !== ""
width: tooltipLabel.implicitWidth + 24 width: px(tooltipLabel.implicitWidth + 24)
height: tooltipLabel.implicitHeight + 12 height: px(tooltipLabel.implicitHeight + 12)
color: Theme.surfaceContainer color: Theme.surfaceContainer
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.width: 1 border.width: 1
border.color: Theme.outlineMedium border.color: Theme.outlineMedium
y: -height - 8 y: SettingsData.dockPosition === SettingsData.Position.Bottom ? -height - Theme.spacingS : parent.height + Theme.spacingS
x: hoveredButton ? hoveredButton.mapToItem(dockContainer, hoveredButton.width / 2, 0).x - width / 2 : 0 x: hoveredButton ? hoveredButton.mapToItem(dockContainer, hoveredButton.width / 2, 0).x - width / 2 : 0
StyledText { StyledText {
@@ -193,3 +263,5 @@ PanelWindow {
} }
} }
} }
}
}

View File

@@ -25,6 +25,27 @@ Item {
property string windowTitle: "" property string windowTitle: ""
property bool isHovered: mouseArea.containsMouse && !dragging property bool isHovered: mouseArea.containsMouse && !dragging
property bool showTooltip: mouseArea.containsMouse && !dragging property bool showTooltip: mouseArea.containsMouse && !dragging
property var cachedDesktopEntry: null
function updateDesktopEntry() {
if (!appData || appData.appId === "__SEPARATOR__") {
cachedDesktopEntry = null
return
}
const moddedId = Paths.moddedAppId(appData.appId)
cachedDesktopEntry = DesktopEntries.heuristicLookup(moddedId)
}
Component.onCompleted: updateDesktopEntry()
onAppDataChanged: updateDesktopEntry()
Connections {
target: DesktopEntries
function onApplicationsChanged() {
updateDesktopEntry()
}
}
property bool isWindowFocused: { property bool isWindowFocused: {
if (!appData) { if (!appData) {
return false return false
@@ -55,8 +76,7 @@ Item {
} }
if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) { if ((appData.type === "window" && showWindowTitle) || (appData.type === "grouped" && appData.windowTitle)) {
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) const appName = cachedDesktopEntry && cachedDesktopEntry.name ? cachedDesktopEntry.name : appData.appId
const appName = desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
const title = appData.type === "window" ? windowTitle : appData.windowTitle const title = appData.type === "window" ? windowTitle : appData.windowTitle
return appName + (title ? " • " + title : "") return appName + (title ? " • " + title : "")
} }
@@ -65,8 +85,7 @@ Item {
return "" return ""
} }
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) return cachedDesktopEntry && cachedDesktopEntry.name ? cachedDesktopEntry.name : appData.appId
return desktopEntry && desktopEntry.name ? desktopEntry.name : appData.appId
} }
width: 40 width: 40
@@ -255,7 +274,7 @@ Item {
if (mouse.button === Qt.LeftButton) { if (mouse.button === Qt.LeftButton) {
if (appData.type === "pinned") { if (appData.type === "pinned") {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) const desktopEntry = cachedDesktopEntry
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -275,7 +294,7 @@ Item {
} else if (appData.type === "grouped") { } else if (appData.type === "grouped") {
if (appData.windowCount === 0) { if (appData.windowCount === 0) {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) const desktopEntry = cachedDesktopEntry
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -305,7 +324,7 @@ Item {
} }
} else if (mouse.button === Qt.MiddleButton) { } else if (mouse.button === Qt.MiddleButton) {
if (appData && appData.appId) { if (appData && appData.appId) {
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) const desktopEntry = cachedDesktopEntry
if (desktopEntry) { if (desktopEntry) {
AppUsageHistoryData.addAppUsage({ AppUsageHistoryData.addAppUsage({
"id": appData.appId, "id": appData.appId,
@@ -341,8 +360,7 @@ Item {
if (moddedId.toLowerCase().includes("steam_app")) { if (moddedId.toLowerCase().includes("steam_app")) {
return "" return ""
} }
const desktopEntry = DesktopEntries.heuristicLookup(moddedId) return cachedDesktopEntry && cachedDesktopEntry.icon ? Quickshell.iconPath(cachedDesktopEntry.icon, true) : ""
return desktopEntry && desktopEntry.icon ? Quickshell.iconPath(desktopEntry.icon, true) : ""
} }
mipmap: true mipmap: true
smooth: true smooth: true
@@ -381,7 +399,7 @@ Item {
return "?" return "?"
} }
const desktopEntry = DesktopEntries.heuristicLookup(appData.appId) const desktopEntry = cachedDesktopEntry
if (desktopEntry && desktopEntry.name) { if (desktopEntry && desktopEntry.name) {
return desktopEntry.name.charAt(0).toUpperCase() return desktopEntry.name.charAt(0).toUpperCase()
} }

View File

@@ -35,7 +35,7 @@ Item {
Row { Row {
id: row id: row
spacing: 2 spacing: 8
anchors.centerIn: parent anchors.centerIn: parent
height: 40 height: 40

View File

@@ -96,8 +96,15 @@ PanelWindow {
actualDockHeight = dockBackground.height actualDockHeight = dockBackground.height
} }
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
const dockBottomMargin = 16 const dockBottomMargin = 16
const buttonScreenY = root.screen.height - actualDockHeight - dockBottomMargin - 20 let buttonScreenY
if (isDockAtBottom) {
buttonScreenY = root.screen.height - actualDockHeight - dockBottomMargin - 20
} else {
buttonScreenY = actualDockHeight + dockBottomMargin + 20
}
const dockContentWidth = dockWindow.width const dockContentWidth = dockWindow.width
const screenWidth = root.screen.width const screenWidth = root.screen.width
@@ -119,7 +126,14 @@ PanelWindow {
const want = root.anchorPos.x - width / 2 const want = root.anchorPos.x - width / 2
return Math.max(left, Math.min(right, want)) return Math.max(left, Math.min(right, want))
} }
y: Math.max(10, root.anchorPos.y - height + 30) y: {
const isDockAtBottom = SettingsData.dockPosition === SettingsData.Position.Bottom
if (isDockAtBottom) {
return Math.max(10, root.anchorPos.y - height + 30)
} else {
return Math.min(root.height - height - 10, root.anchorPos.y - 30)
}
}
color: Theme.popupBackground() color: Theme.popupBackground()
radius: Theme.cornerRadius radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)

View File

@@ -63,7 +63,7 @@ Column {
width: calculatedWidth width: calculatedWidth
height: 32 height: 32
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : "transparent" color: isActive ? Theme.primaryPressed : isHovered ? Theme.primaryHoverLight : Theme.withAlpha(Theme.primaryPressed, 0)
border.width: isActive ? 0 : 1 border.width: isActive ? 0 : 1
border.color: Theme.outlineMedium border.color: Theme.outlineMedium
@@ -104,7 +104,7 @@ Column {
width: 20 width: 20
height: 20 height: 20
radius: 10 radius: 10
color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : "transparent" color: closeMouseArea.containsMouse ? Theme.surfaceTextHover : Theme.withAlpha(Theme.surfaceTextHover, 0)
visible: NotepadStorageService.tabs.length > 1 visible: NotepadStorageService.tabs.length > 1
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View File

@@ -13,7 +13,6 @@ DankPopout {
id: root id: root
property bool notificationHistoryVisible: false property bool notificationHistoryVisible: false
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
NotificationKeyboardController { NotificationKeyboardController {
@@ -35,10 +34,10 @@ DankPopout {
popupWidth: 400 popupWidth: 400
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 400
triggerX: Screen.width - 400 - Theme.spacingL triggerX: 0
triggerY: Theme.barHeight - 4 + SettingsData.topBarSpacing + Theme.popupDistance triggerY: 0
triggerWidth: 40 triggerWidth: 40
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
shouldBeVisible: notificationHistoryVisible shouldBeVisible: notificationHistoryVisible
visible: shouldBeVisible visible: shouldBeVisible

View File

@@ -411,6 +411,78 @@ QtObject {
selectPrevious() selectPrevious()
event.accepted = true event.accepted = true
} }
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else if (selectedFlatIndex === 0) {
keyboardNavigationActive = false
if (listView) {
listView.keyboardActive = false
}
selectionVersion++
} else {
selectPrevious()
}
event.accepted = true
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else {
selectNext()
}
event.accepted = true
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
if (!keyboardNavigationActive) {
keyboardNavigationActive = true
rebuildFlatNavigation()
selectedFlatIndex = 0
updateSelectedIdFromIndex()
if (listView) {
listView.keyboardActive = true
}
selectionVersion++
ensureVisible()
} else if (selectedFlatIndex === 0) {
keyboardNavigationActive = false
if (listView) {
listView.keyboardActive = false
}
selectionVersion++
} else {
selectPrevious()
}
event.accepted = true
} else if (keyboardNavigationActive) { } else if (keyboardNavigationActive) {
if (event.key === Qt.Key_Space) { if (event.key === Qt.Key_Space) {
toggleGroupExpanded() toggleGroupExpanded()
@@ -425,7 +497,10 @@ QtObject {
clearSelected() clearSelected()
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Tab) { } else if (event.key === Qt.Key_Tab) {
selectNextWrapping() selectNext()
event.accepted = true
} else if (event.key === Qt.Key_Backtab) {
selectPrevious()
event.accepted = true event.accepted = true
} else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) { } else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
const actionIndex = event.key - Qt.Key_1 const actionIndex = event.key - Qt.Key_1

View File

@@ -76,7 +76,6 @@ PanelWindow {
color: "transparent" color: "transparent"
implicitWidth: 400 implicitWidth: 400
implicitHeight: 122 implicitHeight: 122
onScreenYChanged: margins.top = Theme.barHeight - 4 + SettingsData.topBarSpacing + 4 + screenY
onHasValidDataChanged: { onHasValidDataChanged: {
if (!hasValidData && !exiting && !_isDestroying) { if (!hasValidData && !exiting && !_isDestroying) {
forceExit() forceExit()
@@ -108,14 +107,94 @@ PanelWindow {
} }
} }
anchors { property bool isTopCenter: SettingsData.notificationPopupPosition === -1
top: true
right: true anchors.top: isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left
} anchors.bottom: SettingsData.notificationPopupPosition === SettingsData.Position.Bottom || SettingsData.notificationPopupPosition === SettingsData.Position.Right
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
margins { margins {
top: Theme.barHeight - 4 + SettingsData.topBarSpacing + 4 top: getTopMargin()
right: 12 bottom: getBottomMargin()
left: getLeftMargin()
right: getRightMargin()
}
function getTopMargin() {
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left
if (!isTop) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
let base = Theme.popupDistance
if (barPos === SettingsData.Position.Top) {
base = exclusiveZone
}
return base + screenY
}
function getBottomMargin() {
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isBottom = popupPos === SettingsData.Position.Bottom || popupPos === SettingsData.Position.Right
if (!isBottom) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
let base = Theme.popupDistance
if (barPos === SettingsData.Position.Bottom) {
base = exclusiveZone
}
return base + screenY
}
function getLeftMargin() {
if (isTopCenter) {
return (screen.width - implicitWidth) / 2
}
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom
if (!isLeft) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
if (barPos === SettingsData.Position.Left) {
return exclusiveZone
}
return Theme.popupDistance
}
function getRightMargin() {
if (isTopCenter) return 0
const popupPos = SettingsData.notificationPopupPosition
const barPos = SettingsData.dankBarPosition
const isRight = popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Right
if (!isRight) return 0
const effectiveBarThickness = Math.max(26 + SettingsData.dankBarInnerPadding * 0.6 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
const exclusiveZone = effectiveBarThickness + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap
if (barPos === SettingsData.Position.Right) {
return exclusiveZone
}
return Theme.popupDistance
} }
Item { Item {
@@ -123,7 +202,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
visible: win.hasValidData visible: win.hasValidData
layer.enabled: (enterX.running || exitAnim.running) layer.enabled: true
layer.smooth: true layer.smooth: true
Rectangle { Rectangle {
@@ -455,7 +534,12 @@ PanelWindow {
transform: Translate { transform: Translate {
id: tx id: tx
x: Anims.slidePx x: {
if (isTopCenter) return 0
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
y: isTopCenter ? -Anims.slidePx : 0
} }
} }
@@ -463,15 +547,23 @@ PanelWindow {
id: enterX id: enterX
target: tx target: tx
property: "x" property: isTopCenter ? "y" : "x"
from: Anims.slidePx from: {
if (isTopCenter) return -Anims.slidePx
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
to: 0 to: 0
duration: Anims.durMed duration: Anims.durMed
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasizedDecel easing.bezierCurve: isTopCenter ? Anims.standardDecel : Anims.emphasizedDecel
onStopped: { onStopped: {
if (!win.exiting && !win._isDestroying && Math.abs(tx.x) < 0.5) { if (!win.exiting && !win._isDestroying) {
win.entered() if (isTopCenter) {
if (Math.abs(tx.y) < 0.5) win.entered()
} else {
if (Math.abs(tx.x) < 0.5) win.entered()
}
} }
} }
} }
@@ -483,9 +575,13 @@ PanelWindow {
PropertyAnimation { PropertyAnimation {
target: tx target: tx
property: "x" property: isTopCenter ? "y" : "x"
from: 0 from: 0
to: Anims.slidePx to: {
if (isTopCenter) return -Anims.slidePx
const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
return isLeft ? -Anims.slidePx : Anims.slidePx
}
duration: Anims.durShort duration: Anims.durShort
easing.type: Easing.BezierSpline easing.type: Easing.BezierSpline
easing.bezierCurve: Anims.emphasizedAccel easing.bezierCurve: Anims.emphasizedAccel

View File

@@ -12,11 +12,23 @@ DankOSD {
enableMouseInteraction: true enableMouseInteraction: true
Connections { Connections {
target: AudioService target: AudioService.sink && AudioService.sink.audio ? AudioService.sink.audio : null
function onVolumeChanged() { function onVolumeChanged() {
if (!AudioService.suppressOSD) {
root.show() root.show()
} }
}
function onMutedChanged() {
if (!AudioService.suppressOSD) {
root.show()
}
}
}
Connections {
target: AudioService
function onSinkChanged() { function onSinkChanged() {
if (root.shouldBeVisible) { if (root.shouldBeVisible) {

View File

@@ -12,8 +12,8 @@ Rectangle {
width: parent ? parent.width : 0 width: parent ? parent.width : 0
height: 40 height: 40
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent" color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.withAlpha(Theme.primary, 0)
border.color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" border.color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
border.width: 1 border.width: 1
MouseArea { MouseArea {
@@ -158,7 +158,7 @@ Rectangle {
width: 28 width: 28
height: 28 height: 28
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: menuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent" color: menuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0)
anchors.right: parent.right anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View File

@@ -15,7 +15,6 @@ DankPopout {
id: processListPopout id: processListPopout
property var parentWidget: null property var parentWidget: null
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -40,9 +39,9 @@ DankPopout {
popupWidth: 600 popupWidth: 600
popupHeight: 600 popupHeight: 600
triggerX: Screen.width - 600 - Theme.spacingL triggerX: Screen.width - 600 - Theme.spacingL
triggerY: Math.max(26 + SettingsData.topBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.topBarInnerPadding)) + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
triggerWidth: 55 triggerWidth: 55
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
visible: shouldBeVisible visible: shouldBeVisible
shouldBeVisible: false shouldBeVisible: false

View File

@@ -30,7 +30,7 @@ Column {
if (DgopService.currentSort === "name") { if (DgopService.currentSort === "name") {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12); return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} }
return processHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"; return processHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
anchors.left: parent.left anchors.left: parent.left
@@ -74,7 +74,7 @@ Column {
if (DgopService.currentSort === "cpu") { if (DgopService.currentSort === "cpu") {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12); return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} }
return cpuHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"; return cpuHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
anchors.right: parent.right anchors.right: parent.right
@@ -118,7 +118,7 @@ Column {
if (DgopService.currentSort === "memory") { if (DgopService.currentSort === "memory") {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12); return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} }
return memoryHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"; return memoryHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
anchors.right: parent.right anchors.right: parent.right
@@ -162,7 +162,7 @@ Column {
if (DgopService.currentSort === "pid") { if (DgopService.currentSort === "pid") {
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12); return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
} }
return pidHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"; return pidHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
} }
radius: Theme.cornerRadius radius: Theme.cornerRadius
anchors.right: parent.right anchors.right: parent.right
@@ -204,7 +204,7 @@ Column {
width: 28 width: 28
height: 28 height: 28
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: sortOrderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent" color: sortOrderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0)
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 8 anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter

View File

@@ -6,7 +6,7 @@ import qs.Services
import qs.Widgets import qs.Widgets
Item { Item {
id: topBarTab id: dankBarTab
property var baseWidgetDefinitions: [{ property var baseWidgetDefinitions: [{
"id": "launcherButton", "id": "launcherButton",
@@ -236,51 +236,51 @@ Item {
var widgets = [] var widgets = []
if (targetSection === "left") { if (targetSection === "left") {
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
widgets.push(widgetObj) widgets.push(widgetObj)
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
} else if (targetSection === "center") { } else if (targetSection === "center") {
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
widgets.push(widgetObj) widgets.push(widgetObj)
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
} else if (targetSection === "right") { } else if (targetSection === "right") {
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
widgets.push(widgetObj) widgets.push(widgetObj)
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
} }
function removeWidgetFromSection(sectionId, widgetIndex) { function removeWidgetFromSection(sectionId, widgetIndex) {
var widgets = [] var widgets = []
if (sectionId === "left") { if (sectionId === "left") {
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets.splice(widgetIndex, 1) widgets.splice(widgetIndex, 1)
} }
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
} else if (sectionId === "center") { } else if (sectionId === "center") {
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets.splice(widgetIndex, 1) widgets.splice(widgetIndex, 1)
} }
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
} else if (sectionId === "right") { } else if (sectionId === "right") {
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets.splice(widgetIndex, 1) widgets.splice(widgetIndex, 1)
} }
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
} }
function handleItemEnabledChanged(sectionId, itemId, enabled) { function handleItemEnabledChanged(sectionId, itemId, enabled) {
var widgets = [] var widgets = []
if (sectionId === "left") if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center") else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right") else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
for (var i = 0; i < widgets.length; i++) { for (var i = 0; i < widgets.length; i++) {
var widget = widgets[i] var widget = widgets[i]
var widgetId = typeof widget === "string" ? widget : widget.id var widgetId = typeof widget === "string" ? widget : widget.id
@@ -316,30 +316,30 @@ Item {
} }
} }
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
function handleItemOrderChanged(sectionId, newOrder) { function handleItemOrderChanged(sectionId, newOrder) {
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(newOrder) SettingsData.setDankBarLeftWidgets(newOrder)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(newOrder) SettingsData.setDankBarCenterWidgets(newOrder)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(newOrder) SettingsData.setDankBarRightWidgets(newOrder)
} }
function handleSpacerSizeChanged(sectionId, widgetIndex, newSize) { function handleSpacerSizeChanged(sectionId, widgetIndex, newSize) {
var widgets = [] var widgets = []
if (sectionId === "left") if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center") else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right") else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
var widget = widgets[widgetIndex] var widget = widgets[widgetIndex]
@@ -372,21 +372,21 @@ Item {
} }
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
function handleGpuSelectionChanged(sectionId, widgetIndex, selectedGpuIndex) { function handleGpuSelectionChanged(sectionId, widgetIndex, selectedGpuIndex) {
var widgets = [] var widgets = []
if (sectionId === "left") if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center") else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right") else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
var widget = widgets[widgetIndex] var widget = widgets[widgetIndex]
@@ -415,21 +415,21 @@ Item {
} }
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
function handleDiskMountSelectionChanged(sectionId, widgetIndex, mountPath) { function handleDiskMountSelectionChanged(sectionId, widgetIndex, mountPath) {
var widgets = [] var widgets = []
if (sectionId === "left") if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center") else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right") else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) { if (widgetIndex >= 0 && widgetIndex < widgets.length) {
var widget = widgets[widgetIndex] var widget = widgets[widgetIndex]
@@ -461,11 +461,11 @@ Item {
} }
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
function handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) { function handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) {
@@ -483,11 +483,11 @@ Item {
var widgets = [] var widgets = []
var widgetData = [] var widgetData = []
if (sectionId === "left") if (sectionId === "left")
widgetData = SettingsData.topBarLeftWidgets || [] widgetData = SettingsData.dankBarLeftWidgets || []
else if (sectionId === "center") else if (sectionId === "center")
widgetData = SettingsData.topBarCenterWidgets || [] widgetData = SettingsData.dankBarCenterWidgets || []
else if (sectionId === "right") else if (sectionId === "right")
widgetData = SettingsData.topBarRightWidgets || [] widgetData = SettingsData.dankBarRightWidgets || []
widgetData.forEach(widget => { widgetData.forEach(widget => {
var widgetId = typeof widget === "string" ? widget : widget.id var widgetId = typeof widget === "string" ? widget : widget.id
var widgetEnabled = typeof widget var widgetEnabled = typeof widget
@@ -531,23 +531,23 @@ Item {
Component.onCompleted: { Component.onCompleted: {
// Only set defaults if widgets have never been configured (null/undefined, not empty array) // Only set defaults if widgets have never been configured (null/undefined, not empty array)
if (!SettingsData.topBarLeftWidgets) if (!SettingsData.dankBarLeftWidgets)
SettingsData.setTopBarLeftWidgets(defaultLeftWidgets) SettingsData.setDankBarLeftWidgets(defaultLeftWidgets)
if (!SettingsData.topBarCenterWidgets) if (!SettingsData.dankBarCenterWidgets)
SettingsData.setTopBarCenterWidgets(defaultCenterWidgets) SettingsData.setDankBarCenterWidgets(defaultCenterWidgets)
if (!SettingsData.topBarRightWidgets) if (!SettingsData.dankBarRightWidgets)
SettingsData.setTopBarRightWidgets(defaultRightWidgets) SettingsData.setDankBarRightWidgets(defaultRightWidgets)
const sections = ["left", "center", "right"] const sections = ["left", "center", "right"]
sections.forEach(sectionId => { sections.forEach(sectionId => {
var widgets = [] var widgets = []
if (sectionId === "left") if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice() widgets = SettingsData.dankBarLeftWidgets.slice()
else if (sectionId === "center") else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice() widgets = SettingsData.dankBarCenterWidgets.slice()
else if (sectionId === "right") else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice() widgets = SettingsData.dankBarRightWidgets.slice()
var updated = false var updated = false
for (var i = 0; i < widgets.length; i++) { for (var i = 0; i < widgets.length; i++) {
var widget = widgets[i] var widget = widgets[i]
@@ -562,11 +562,11 @@ Item {
} }
if (updated) { if (updated) {
if (sectionId === "left") if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets) SettingsData.setDankBarLeftWidgets(widgets)
else if (sectionId === "center") else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets) SettingsData.setDankBarCenterWidgets(widgets)
else if (sectionId === "right") else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setDankBarRightWidgets(widgets)
} }
}) })
} }
@@ -584,10 +584,10 @@ Item {
width: parent.width width: parent.width
spacing: Theme.spacingXL spacing: Theme.spacingXL
// TopBar Auto-hide Section // Position Section
StyledRect { StyledRect {
width: parent.width width: parent.width
height: topBarAutoHideSection.implicitHeight + Theme.spacingL * 2 height: positionSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
@@ -595,7 +595,71 @@ Item {
border.width: 0 border.width: 0
Column { Column {
id: topBarAutoHideSection id: positionSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "vertical_align_center"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Position"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
DankButtonGroup {
id: positionButtonGroup
anchors.verticalCenter: parent.verticalCenter
model: ["Top", "Bottom", "Left", "Right"]
currentIndex: {
switch (SettingsData.dankBarPosition) {
case SettingsData.Position.Top: return 0
case SettingsData.Position.Bottom: return 1
case SettingsData.Position.Left: return 2
case SettingsData.Position.Right: return 3
default: return 0
}
}
onSelectionChanged: (index, selected) => {
if (selected) {
switch (index) {
case 0: SettingsData.setDankBarPosition(SettingsData.Position.Top); break
case 1: SettingsData.setDankBarPosition(SettingsData.Position.Bottom); break
case 2: SettingsData.setDankBarPosition(SettingsData.Position.Left); break
case 3: SettingsData.setDankBarPosition(SettingsData.Position.Right); break
}
}
}
}
}
}
}
// DankBar Auto-hide Section
StyledRect {
width: parent.width
height: dankBarAutoHideSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 0
Column {
id: dankBarAutoHideSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
@@ -638,9 +702,9 @@ Item {
id: autoHideToggle id: autoHideToggle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.topBarAutoHide checked: SettingsData.dankBarAutoHide
onToggled: toggled => { onToggled: toggled => {
return SettingsData.setTopBarAutoHide( return SettingsData.setDankBarAutoHide(
toggled) toggled)
} }
} }
@@ -690,9 +754,9 @@ Item {
id: visibilityToggle id: visibilityToggle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.topBarVisible checked: SettingsData.dankBarVisible
onToggled: toggled => { onToggled: toggled => {
return SettingsData.setTopBarVisible( return SettingsData.setDankBarVisible(
toggled) toggled)
} }
} }
@@ -744,9 +808,9 @@ Item {
id: overviewToggle id: overviewToggle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.topBarOpenOnOverview checked: SettingsData.dankBarOpenOnOverview
onToggled: toggled => { onToggled: toggled => {
return SettingsData.setTopBarOpenOnOverview( return SettingsData.setDankBarOpenOnOverview(
toggled) toggled)
} }
} }
@@ -758,7 +822,7 @@ Item {
// Spacing // Spacing
StyledRect { StyledRect {
width: parent.width width: parent.width
height: topBarSpacingSection.implicitHeight + Theme.spacingL * 2 height: dankBarSpacingSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
@@ -766,7 +830,7 @@ Item {
border.width: 0 border.width: 0
Column { Column {
id: topBarSpacingSection id: dankBarSpacingSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
@@ -797,7 +861,7 @@ Item {
spacing: Theme.spacingS spacing: Theme.spacingS
StyledText { StyledText {
text: "Top/Left/Right Gaps (0 = edge-to-edge)" text: "Edge Spacing (0 = edge-to-edge)"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
@@ -806,7 +870,7 @@ Item {
DankSlider { DankSlider {
width: parent.width width: parent.width
height: 24 height: 24
value: SettingsData.topBarSpacing value: SettingsData.dankBarSpacing
minimum: 0 minimum: 0
maximum: 32 maximum: 32
unit: "" unit: ""
@@ -814,7 +878,7 @@ Item {
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarSpacing( SettingsData.setDankBarSpacing(
newValue) newValue)
} }
} }
@@ -825,7 +889,7 @@ Item {
spacing: Theme.spacingS spacing: Theme.spacingS
StyledText { StyledText {
text: "Bottom Gap (Exclusive Zone)" text: "Exclusive Zone Offset"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
@@ -834,7 +898,7 @@ Item {
DankSlider { DankSlider {
width: parent.width width: parent.width
height: 24 height: 24
value: SettingsData.topBarBottomGap value: SettingsData.dankBarBottomGap
minimum: -100 minimum: -100
maximum: 100 maximum: 100
unit: "" unit: ""
@@ -842,7 +906,7 @@ Item {
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarBottomGap( SettingsData.setDankBarBottomGap(
newValue) newValue)
} }
} }
@@ -862,7 +926,7 @@ Item {
DankSlider { DankSlider {
width: parent.width width: parent.width
height: 24 height: 24
value: SettingsData.topBarInnerPadding value: SettingsData.dankBarInnerPadding
minimum: 0 minimum: 0
maximum: 24 maximum: 24
unit: "" unit: ""
@@ -870,7 +934,7 @@ Item {
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarInnerPadding( SettingsData.setDankBarInnerPadding(
newValue) newValue)
} }
} }
@@ -881,9 +945,9 @@ Item {
width: parent.width width: parent.width
text: "Square Corners" text: "Square Corners"
description: "Removes rounded corners from bar container." description: "Removes rounded corners from bar container."
checked: SettingsData.topBarSquareCorners checked: SettingsData.dankBarSquareCorners
onToggled: checked => { onToggled: checked => {
SettingsData.setTopBarSquareCorners( SettingsData.setDankBarSquareCorners(
checked) checked)
} }
} }
@@ -892,9 +956,9 @@ Item {
width: parent.width width: parent.width
text: "No Background" text: "No Background"
description: "Remove widget backgrounds for a minimal look with tighter spacing." description: "Remove widget backgrounds for a minimal look with tighter spacing."
checked: SettingsData.topBarNoBackground checked: SettingsData.dankBarNoBackground
onToggled: checked => { onToggled: checked => {
SettingsData.setTopBarNoBackground( SettingsData.setDankBarNoBackground(
checked) checked)
} }
} }
@@ -903,9 +967,9 @@ Item {
width: parent.width width: parent.width
text: "Goth Corners" text: "Goth Corners"
description: "Add curved swooping tips at the bottom of the bar." description: "Add curved swooping tips at the bottom of the bar."
checked: SettingsData.topBarGothCornersEnabled checked: SettingsData.dankBarGothCornersEnabled
onToggled: checked => { onToggled: checked => {
SettingsData.setTopBarGothCornersEnabled( SettingsData.setDankBarGothCornersEnabled(
checked) checked)
} }
} }
@@ -996,11 +1060,11 @@ Item {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
SettingsData.setTopBarLeftWidgets( SettingsData.setDankBarLeftWidgets(
defaultLeftWidgets) defaultLeftWidgets)
SettingsData.setTopBarCenterWidgets( SettingsData.setDankBarCenterWidgets(
defaultCenterWidgets) defaultCenterWidgets)
SettingsData.setTopBarRightWidgets( SettingsData.setDankBarRightWidgets(
defaultRightWidgets) defaultRightWidgets)
} }
} }
@@ -1035,7 +1099,7 @@ Item {
width: parent.width width: parent.width
spacing: Theme.spacingL spacing: Theme.spacingL
// Left Section // Left/Top Section
StyledRect { StyledRect {
width: parent.width width: parent.width
height: leftSection.implicitHeight + Theme.spacingL * 2 height: leftSection.implicitHeight + Theme.spacingL * 2
@@ -1049,32 +1113,32 @@ Item {
id: leftSection id: leftSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
title: "Left Section" title: SettingsData.dankBarIsVertical ? "Top Section" : "Left Section"
titleIcon: "format_align_left" titleIcon: "format_align_left"
sectionId: "left" sectionId: "left"
allWidgets: topBarTab.baseWidgetDefinitions allWidgets: dankBarTab.baseWidgetDefinitions
items: topBarTab.getItemsForSection("left") items: dankBarTab.getItemsForSection("left")
onItemEnabledChanged: (sectionId, itemId, enabled) => { onItemEnabledChanged: (sectionId, itemId, enabled) => {
topBarTab.handleItemEnabledChanged( dankBarTab.handleItemEnabledChanged(
sectionId, sectionId,
itemId, enabled) itemId, enabled)
} }
onItemOrderChanged: newOrder => { onItemOrderChanged: newOrder => {
topBarTab.handleItemOrderChanged( dankBarTab.handleItemOrderChanged(
"left", newOrder) "left", newOrder)
} }
onAddWidget: sectionId => { onAddWidget: sectionId => {
widgetSelectionPopup.allWidgets widgetSelectionPopup.allWidgets
= topBarTab.baseWidgetDefinitions = dankBarTab.baseWidgetDefinitions
widgetSelectionPopup.targetSection = sectionId widgetSelectionPopup.targetSection = sectionId
widgetSelectionPopup.safeOpen() widgetSelectionPopup.safeOpen()
} }
onRemoveWidget: (sectionId, widgetIndex) => { onRemoveWidget: (sectionId, widgetIndex) => {
topBarTab.removeWidgetFromSection( dankBarTab.removeWidgetFromSection(
sectionId, widgetIndex) sectionId, widgetIndex)
} }
onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => { onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => {
topBarTab.handleSpacerSizeChanged( dankBarTab.handleSpacerSizeChanged(
sectionId, widgetIndex, newSize) sectionId, widgetIndex, newSize)
} }
onCompactModeChanged: (widgetId, value) => { onCompactModeChanged: (widgetId, value) => {
@@ -1096,12 +1160,12 @@ Item {
handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value)
} }
onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => { onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => {
topBarTab.handleGpuSelectionChanged( dankBarTab.handleGpuSelectionChanged(
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => { onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
} }
@@ -1124,29 +1188,29 @@ Item {
title: "Center Section" title: "Center Section"
titleIcon: "format_align_center" titleIcon: "format_align_center"
sectionId: "center" sectionId: "center"
allWidgets: topBarTab.baseWidgetDefinitions allWidgets: dankBarTab.baseWidgetDefinitions
items: topBarTab.getItemsForSection("center") items: dankBarTab.getItemsForSection("center")
onItemEnabledChanged: (sectionId, itemId, enabled) => { onItemEnabledChanged: (sectionId, itemId, enabled) => {
topBarTab.handleItemEnabledChanged( dankBarTab.handleItemEnabledChanged(
sectionId, sectionId,
itemId, enabled) itemId, enabled)
} }
onItemOrderChanged: newOrder => { onItemOrderChanged: newOrder => {
topBarTab.handleItemOrderChanged( dankBarTab.handleItemOrderChanged(
"center", newOrder) "center", newOrder)
} }
onAddWidget: sectionId => { onAddWidget: sectionId => {
widgetSelectionPopup.allWidgets widgetSelectionPopup.allWidgets
= topBarTab.baseWidgetDefinitions = dankBarTab.baseWidgetDefinitions
widgetSelectionPopup.targetSection = sectionId widgetSelectionPopup.targetSection = sectionId
widgetSelectionPopup.safeOpen() widgetSelectionPopup.safeOpen()
} }
onRemoveWidget: (sectionId, widgetIndex) => { onRemoveWidget: (sectionId, widgetIndex) => {
topBarTab.removeWidgetFromSection( dankBarTab.removeWidgetFromSection(
sectionId, widgetIndex) sectionId, widgetIndex)
} }
onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => { onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => {
topBarTab.handleSpacerSizeChanged( dankBarTab.handleSpacerSizeChanged(
sectionId, widgetIndex, newSize) sectionId, widgetIndex, newSize)
} }
onCompactModeChanged: (widgetId, value) => { onCompactModeChanged: (widgetId, value) => {
@@ -1168,18 +1232,18 @@ Item {
handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value)
} }
onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => { onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => {
topBarTab.handleGpuSelectionChanged( dankBarTab.handleGpuSelectionChanged(
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => { onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
} }
} }
// Right Section // Right/Bottom Section
StyledRect { StyledRect {
width: parent.width width: parent.width
height: rightSection.implicitHeight + Theme.spacingL * 2 height: rightSection.implicitHeight + Theme.spacingL * 2
@@ -1193,32 +1257,32 @@ Item {
id: rightSection id: rightSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
title: "Right Section" title: SettingsData.dankBarIsVertical ? "Bottom Section" : "Right Section"
titleIcon: "format_align_right" titleIcon: "format_align_right"
sectionId: "right" sectionId: "right"
allWidgets: topBarTab.baseWidgetDefinitions allWidgets: dankBarTab.baseWidgetDefinitions
items: topBarTab.getItemsForSection("right") items: dankBarTab.getItemsForSection("right")
onItemEnabledChanged: (sectionId, itemId, enabled) => { onItemEnabledChanged: (sectionId, itemId, enabled) => {
topBarTab.handleItemEnabledChanged( dankBarTab.handleItemEnabledChanged(
sectionId, sectionId,
itemId, enabled) itemId, enabled)
} }
onItemOrderChanged: newOrder => { onItemOrderChanged: newOrder => {
topBarTab.handleItemOrderChanged( dankBarTab.handleItemOrderChanged(
"right", newOrder) "right", newOrder)
} }
onAddWidget: sectionId => { onAddWidget: sectionId => {
widgetSelectionPopup.allWidgets widgetSelectionPopup.allWidgets
= topBarTab.baseWidgetDefinitions = dankBarTab.baseWidgetDefinitions
widgetSelectionPopup.targetSection = sectionId widgetSelectionPopup.targetSection = sectionId
widgetSelectionPopup.safeOpen() widgetSelectionPopup.safeOpen()
} }
onRemoveWidget: (sectionId, widgetIndex) => { onRemoveWidget: (sectionId, widgetIndex) => {
topBarTab.removeWidgetFromSection( dankBarTab.removeWidgetFromSection(
sectionId, widgetIndex) sectionId, widgetIndex)
} }
onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => { onSpacerSizeChanged: (sectionId, widgetIndex, newSize) => {
topBarTab.handleSpacerSizeChanged( dankBarTab.handleSpacerSizeChanged(
sectionId, widgetIndex, newSize) sectionId, widgetIndex, newSize)
} }
onCompactModeChanged: (widgetId, value) => { onCompactModeChanged: (widgetId, value) => {
@@ -1240,12 +1304,12 @@ Item {
handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value)
} }
onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => { onGpuSelectionChanged: (sectionId, widgetIndex, selectedIndex) => {
topBarTab.handleGpuSelectionChanged( dankBarTab.handleGpuSelectionChanged(
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => { onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged( dankBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath) sectionId, widgetIndex, mountPath)
} }
} }
@@ -1259,7 +1323,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
onWidgetSelected: (widgetId, targetSection) => { onWidgetSelected: (widgetId, targetSection) => {
topBarTab.addWidgetToSection(widgetId, dankBarTab.addWidgetToSection(widgetId,
targetSection) targetSection)
} }
} }

View File

@@ -10,8 +10,8 @@ Item {
id: displaysTab id: displaysTab
property var variantComponents: [{ property var variantComponents: [{
"id": "topBar", "id": "dankBar",
"name": "Top Bar", "name": "Dank Bar",
"description": "System bar with widgets and system information", "description": "System bar with widgets and system information",
"icon": "toolbar" "icon": "toolbar"
}, { }, {

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell.Widgets import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Services
import qs.Widgets import qs.Widgets
Item { Item {
@@ -19,10 +20,10 @@ Item {
width: parent.width width: parent.width
spacing: Theme.spacingXL spacing: Theme.spacingXL
// Enable Dock // Dock Position
StyledRect { StyledRect {
width: parent.width width: parent.width
height: enableDockSection.implicitHeight + Theme.spacingL * 2 height: dockPositionSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
@@ -30,7 +31,7 @@ Item {
border.width: 0 border.width: 0
Column { Column {
id: enableDockSection id: dockPositionSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
@@ -41,61 +42,53 @@ Item {
spacing: Theme.spacingM spacing: Theme.spacingM
DankIcon { DankIcon {
name: "dock_to_bottom" name: "swap_vert"
size: Theme.iconSize size: Theme.iconSize
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- enableToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
StyledText { StyledText {
text: "Show Dock" id: positionText
text: "Dock Position"
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium font.weight: Font.Medium
color: Theme.surfaceText color: Theme.surfaceText
}
StyledText {
text: "Display a dock at the bottom of the screen with pinned and running applications"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
DankToggle {
id: enableToggle
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.showDock }
onToggled: checked => {
SettingsData.setShowDock(checked) Item {
width: parent.width - Theme.iconSize - Theme.spacingM - positionText.width - positionButtonGroup.width - Theme.spacingM * 2
anchors.verticalCenter: parent.verticalCenter
}
DankButtonGroup {
id: positionButtonGroup
anchors.verticalCenter: parent.verticalCenter
model: ["Top", "Bottom"]
currentIndex: SettingsData.dockPosition === SettingsData.Position.Bottom ? 1 : 0
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.setDockPosition(index === 1 ? SettingsData.Position.Bottom : SettingsData.Position.Top)
}
} }
} }
} }
} }
} }
// Auto-hide Dock // Dock Visibility Section
StyledRect { StyledRect {
width: parent.width width: parent.width
height: autoHideSection.implicitHeight + Theme.spacingL * 2 height: dockVisibilitySection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2) Theme.outline.b, 0.2)
border.width: 0 border.width: 0
visible: SettingsData.showDock
opacity: visible ? 1 : 0
Column { Column {
id: autoHideSection id: dockVisibilitySection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
@@ -126,7 +119,7 @@ Item {
} }
StyledText { StyledText {
text: "Hide the dock when not in use and reveal it when hovering near the bottom of the screen" text: "Hide the dock when not in use and reveal it when hovering near the dock area"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
@@ -144,12 +137,109 @@ Item {
} }
} }
} }
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
} }
Behavior on opacity { Row {
NumberAnimation { width: parent.width
duration: Theme.mediumDuration spacing: Theme.spacingM
easing.type: Theme.emphasizedEasing
DankIcon {
name: "dock_to_bottom"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- enableToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: "Show Dock"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
}
StyledText {
text: "Display a dock with pinned and running applications that can be positioned at the top or bottom of the screen"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
DankToggle {
id: enableToggle
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.showDock
onToggled: checked => {
SettingsData.setShowDock(checked)
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: CompositorService.isNiri
}
Row {
width: parent.width
spacing: Theme.spacingM
visible: CompositorService.isNiri
DankIcon {
name: "fullscreen"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM
- overviewToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: "Show on Overview"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
}
StyledText {
text: "Always show the dock when niri's overview is open"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
}
DankToggle {
id: overviewToggle
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.dockOpenOnOverview
onToggled: checked => {
SettingsData.setDockOpenOnOverview(checked)
}
}
} }
} }
} }
@@ -226,6 +316,110 @@ Item {
} }
} }
// Dock Spacing Section
StyledRect {
width: parent.width
height: dockSpacingSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 0
visible: SettingsData.showDock
opacity: visible ? 1 : 0
Column {
id: dockSpacingSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "space_bar"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Spacing"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Padding"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: SettingsData.dockSpacing
minimum: 0
maximum: 32
unit: ""
showValue: true
wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => {
SettingsData.setDockSpacing(
newValue)
}
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Height to Edge Gap (Exclusive Zone)"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
}
DankSlider {
width: parent.width
height: 24
value: SettingsData.dockBottomGap
minimum: -100
maximum: 100
unit: ""
showValue: true
wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => {
SettingsData.setDockBottomGap(
newValue)
}
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
// Dock Transparency Section // Dock Transparency Section
StyledRect { StyledRect {
width: parent.width width: parent.width

View File

@@ -15,7 +15,6 @@ Item {
property alias wallpaperBrowser: wallpaperBrowser property alias wallpaperBrowser: wallpaperBrowser
property var parentModal: null property var parentModal: null
property var cachedFontFamilies: [] property var cachedFontFamilies: []
property var cachedMonoFamilies: []
property bool fontsEnumerated: false property bool fontsEnumerated: false
property string selectedMonitorName: { property string selectedMonitorName: {
var screens = Quickshell.screens var screens = Quickshell.screens
@@ -45,28 +44,6 @@ Item {
} }
} }
cachedFontFamilies = fonts.concat(rootFamilies.sort()) cachedFontFamilies = fonts.concat(rootFamilies.sort())
var monoFonts = ["Default"]
var monoFamilies = []
var seenMonoFamilies = new Set()
for (var j = 0; j < availableFonts.length; j++) {
var fontName2 = availableFonts[j]
if (fontName2.startsWith("."))
continue
if (fontName2 === SettingsData.defaultMonoFontFamily)
continue
var lowerName = fontName2.toLowerCase()
if (lowerName.includes("mono") || lowerName.includes("code") || lowerName.includes("console") || lowerName.includes("terminal") || lowerName.includes("courier") || lowerName.includes("dejavu sans mono") || lowerName.includes(
"jetbrains") || lowerName.includes("fira") || lowerName.includes("hack") || lowerName.includes("source code") || lowerName.includes("ubuntu mono") || lowerName.includes("cascadia")) {
var rootName2 = fontName2.replace(/ (Thin|Extra Light|Light|Regular|Medium|Semi Bold|Demi Bold|Bold|Extra Bold|Black|Heavy)$/i, "").replace(/ (Italic|Oblique|Condensed|Extended|Narrow|Wide)$/i, "").trim()
if (!seenMonoFamilies.has(rootName2) && rootName2 !== "") {
seenMonoFamilies.add(rootName2)
monoFamilies.push(rootName2)
}
}
}
cachedMonoFamilies = monoFonts.concat(monoFamilies.sort())
} }
Component.onCompleted: { Component.onCompleted: {
@@ -414,6 +391,63 @@ Item {
} }
} }
// Per-Mode Wallpaper Section - Full Width
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.2
visible: SessionData.wallpaperPath !== ""
}
Column {
width: parent.width
spacing: Theme.spacingM
visible: SessionData.wallpaperPath !== ""
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "brightness_6"
size: Theme.iconSize
color: SessionData.perModeWallpaper ? Theme.primary : Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - Theme.iconSize - Theme.spacingM - perModeToggle.width - Theme.spacingM
spacing: Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
StyledText {
text: "Per-Mode Wallpapers"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
}
StyledText {
text: "Set different wallpapers for light and dark mode"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
}
}
DankToggle {
id: perModeToggle
anchors.verticalCenter: parent.verticalCenter
checked: SessionData.perModeWallpaper
onToggled: toggled => {
return SessionData.setPerModeWallpaper(toggled)
}
}
}
}
// Per-Monitor Wallpaper Section - Full Width // Per-Monitor Wallpaper Section - Full Width
Rectangle { Rectangle {
width: parent.width width: parent.width
@@ -505,19 +539,18 @@ Item {
} }
} }
// Wallpaper Cycling Section - Full Width
Rectangle { Rectangle {
width: parent.width width: parent.width
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.2 opacity: 0.2
visible: SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper visible: (SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper) && !SessionData.perModeWallpaper
} }
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingM spacing: Theme.spacingM
visible: SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper visible: (SessionData.wallpaperPath !== "" || SessionData.perMonitorWallpaper) && !SessionData.perModeWallpaper
Row { Row {
width: parent.width width: parent.width
@@ -574,7 +607,6 @@ Item {
} }
} }
// Cycling mode and settings
Column { Column {
width: parent.width width: parent.width
spacing: Theme.spacingS spacing: Theme.spacingS
@@ -1008,7 +1040,8 @@ Item {
text: "Light Mode" text: "Light Mode"
description: "Use light theme instead of dark theme" description: "Use light theme instead of dark theme"
checked: SessionData.isLightMode checked: SessionData.isLightMode
onToggled: checked => { onToggleCompleted: checked => {
Theme.screenTransition()
Theme.setLightMode(checked) Theme.setLightMode(checked)
} }
} }
@@ -1354,17 +1387,17 @@ Item {
} }
} }
// Lock Screen Settings // Notification Popup Settings
StyledRect { StyledRect {
width: parent.width width: parent.width
height: lockScreenSection.implicitHeight + Theme.spacingL * 2 height: notificationPopupSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0 border.width: 0
Column { Column {
id: lockScreenSection id: notificationPopupSection
anchors.fill: parent anchors.fill: parent
anchors.margins: Theme.spacingL anchors.margins: Theme.spacingL
@@ -1375,14 +1408,14 @@ Item {
spacing: Theme.spacingM spacing: Theme.spacingM
DankIcon { DankIcon {
name: "lock" name: "notifications"
size: Theme.iconSize size: Theme.iconSize
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
StyledText { StyledText {
text: "Lock Screen" text: "Notification Popups"
font.pixelSize: Theme.fontSizeLarge font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium font.weight: Font.Medium
color: Theme.surfaceText color: Theme.surfaceText
@@ -1390,13 +1423,47 @@ Item {
} }
} }
DankToggle { DankDropdown {
width: parent.width width: parent.width
text: "Show Power Actions" text: "Popup Position"
description: "Show power, restart, and logout buttons on the lock screen" description: "Choose where notification popups appear on screen"
checked: SettingsData.lockScreenShowPowerActions currentValue: {
onToggled: checked => { if (SettingsData.notificationPopupPosition === -1) {
SettingsData.setLockScreenShowPowerActions(checked) return "Top Center"
}
switch (SettingsData.notificationPopupPosition) {
case SettingsData.Position.Top:
return "Top Right"
case SettingsData.Position.Bottom:
return "Bottom Left"
case SettingsData.Position.Left:
return "Top Left"
case SettingsData.Position.Right:
return "Bottom Right"
default:
return "Top Right"
}
}
options: ["Top Right", "Top Left", "Top Center", "Bottom Right", "Bottom Left"]
onValueChanged: value => {
switch (value) {
case "Top Right":
SettingsData.setNotificationPopupPosition(SettingsData.Position.Top)
break
case "Top Left":
SettingsData.setNotificationPopupPosition(SettingsData.Position.Left)
break
case "Top Center":
SettingsData.setNotificationPopupPosition(-1)
break
case "Bottom Right":
SettingsData.setNotificationPopupPosition(SettingsData.Position.Right)
break
case "Bottom Left":
SettingsData.setNotificationPopupPosition(SettingsData.Position.Bottom)
break
}
SettingsData.sendTestNotifications()
} }
} }
} }
@@ -1540,7 +1607,7 @@ Item {
enableFuzzySearch: true enableFuzzySearch: true
popupWidthOffset: 100 popupWidthOffset: 100
maxPopupHeight: 400 maxPopupHeight: 400
options: cachedMonoFamilies options: cachedFontFamilies
onValueChanged: value => { onValueChanged: value => {
if (value === "Default") if (value === "Default")
SettingsData.setMonoFontFamily(SettingsData.defaultMonoFontFamily) SettingsData.setMonoFontFamily(SettingsData.defaultMonoFontFamily)
@@ -1634,6 +1701,125 @@ Item {
} }
} }
} }
// Animation Settings
StyledRect {
width: parent.width
height: animationSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: animationSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "animation"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Animations"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Column {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: "Animation Speed"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
StyledText {
text: "Control the speed of animations throughout the interface"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
width: parent.width
}
DankButtonGroup {
id: animationSpeedGroup
width: parent.width
model: ["None", "Shortest", "Short", "Medium", "Long"]
selectionMode: "single"
currentIndex: SettingsData.animationSpeed
onSelectionChanged: (index, selected) => {
if (selected) {
SettingsData.setAnimationSpeed(index)
}
}
}
}
}
}
// Lock Screen Settings
StyledRect {
width: parent.width
height: lockScreenSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Column {
id: lockScreenSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "lock"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Lock Screen"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
width: parent.width
text: "Show Power Actions"
description: "Show power, restart, and logout buttons on the lock screen"
checked: SettingsData.lockScreenShowPowerActions
onToggled: checked => {
SettingsData.setLockScreenShowPowerActions(checked)
}
}
}
}
} }
} }

View File

@@ -226,6 +226,7 @@ Item {
if (Theme.currentThemeCategory === "catppuccin") return 1 if (Theme.currentThemeCategory === "catppuccin") return 1
return 0 return 0
} }
property int pendingThemeIndex: -1
model: ["Generic", "Catppuccin", "Auto", "Custom"] model: ["Generic", "Catppuccin", "Auto", "Custom"]
currentIndex: currentThemeIndex currentIndex: currentThemeIndex
@@ -233,7 +234,11 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
onSelectionChanged: (index, selected) => { onSelectionChanged: (index, selected) => {
if (!selected) return if (!selected) return
switch (index) { pendingThemeIndex = index
}
onAnimationCompleted: {
if (pendingThemeIndex === -1) return
switch (pendingThemeIndex) {
case 0: Theme.switchThemeCategory("generic", "blue"); break case 0: Theme.switchThemeCategory("generic", "blue"); break
case 1: Theme.switchThemeCategory("catppuccin", "cat-mauve"); break case 1: Theme.switchThemeCategory("catppuccin", "cat-mauve"); break
case 2: case 2:
@@ -242,14 +247,15 @@ Item {
else if (ToastService.wallpaperErrorStatus === "error") else if (ToastService.wallpaperErrorStatus === "error")
ToastService.showError("Wallpaper processing failed - check wallpaper path") ToastService.showError("Wallpaper processing failed - check wallpaper path")
else else
Theme.switchTheme(Theme.dynamic, true, false) Theme.switchTheme(Theme.dynamic, true, true)
break break
case 3: case 3:
if (Theme.currentThemeName !== "custom") { if (Theme.currentThemeName !== "custom") {
Theme.switchTheme("custom", true, false) Theme.switchTheme("custom", true, true)
} }
break break
} }
pendingThemeIndex = -1
} }
} }
@@ -764,7 +770,7 @@ Item {
spacing: Theme.spacingS spacing: Theme.spacingS
StyledText { StyledText {
text: "Top Bar Transparency" text: "Dank Bar Transparency"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
@@ -774,7 +780,7 @@ Item {
width: parent.width width: parent.width
height: 24 height: 24
value: Math.round( value: Math.round(
SettingsData.topBarTransparency * 100) SettingsData.dankBarTransparency * 100)
minimum: 0 minimum: 0
maximum: 100 maximum: 100
unit: "" unit: ""
@@ -782,7 +788,7 @@ Item {
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarTransparency( SettingsData.setDankBarTransparency(
newValue / 100) newValue / 100)
} }
} }
@@ -798,7 +804,7 @@ Item {
StyledText { StyledText {
id: transparencyLabel id: transparencyLabel
text: "Top Bar Widget Transparency" text: "Dank Bar Widget Transparency"
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
font.weight: Font.Medium font.weight: Font.Medium
@@ -843,7 +849,7 @@ Item {
width: parent.width width: parent.width
height: 24 height: 24
value: Math.round( value: Math.round(
SettingsData.topBarWidgetTransparency * 100) SettingsData.dankBarWidgetTransparency * 100)
minimum: 0 minimum: 0
maximum: 100 maximum: 100
unit: "" unit: ""
@@ -851,7 +857,7 @@ Item {
wheelEnabled: false wheelEnabled: false
thumbOutlineColor: Theme.surfaceContainerHigh thumbOutlineColor: Theme.surfaceContainerHigh
onSliderValueChanged: newValue => { onSliderValueChanged: newValue => {
SettingsData.setTopBarWidgetTransparency( SettingsData.setDankBarWidgetTransparency(
newValue / 100) newValue / 100)
} }
} }

View File

@@ -85,7 +85,6 @@ Popup {
onOpened: { onOpened: {
isOpening = false isOpening = false
Qt.callLater(() => { Qt.callLater(() => {
contentItem.forceActiveFocus()
searchField.forceActiveFocus() searchField.forceActiveFocus()
}) })
} }
@@ -121,6 +120,18 @@ Popup {
} else if (event.key === Qt.Key_Up) { } else if (event.key === Qt.Key_Up) {
root.selectPrevious() root.selectPrevious()
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
root.selectNext()
event.accepted = true
} else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
root.selectPrevious()
event.accepted = true
} else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
root.selectNext()
event.accepted = true
} else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
root.selectPrevious()
event.accepted = true
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (root.keyboardNavigationActive) { if (root.keyboardNavigationActive) {
root.selectWidget() root.selectWidget()

View File

@@ -305,6 +305,19 @@ Column {
onClicked: { onClicked: {
root.compactModeChanged("music", 0) root.compactModeChanged("music", 0)
} }
onEntered: {
smallTooltipLoader.active = true
if (smallTooltipLoader.item) {
const p = smallSizeButton.mapToItem(null, smallSizeButton.width / 2, 0)
smallTooltipLoader.item.show("Small", p.x, p.y - 40, null)
}
}
onExited: {
if (smallTooltipLoader.item) {
smallTooltipLoader.item.hide()
}
smallTooltipLoader.active = false
}
} }
DankActionButton { DankActionButton {
@@ -318,6 +331,19 @@ Column {
onClicked: { onClicked: {
root.compactModeChanged("music", 1) root.compactModeChanged("music", 1)
} }
onEntered: {
mediumTooltipLoader.active = true
if (mediumTooltipLoader.item) {
const p = mediumSizeButton.mapToItem(null, mediumSizeButton.width / 2, 0)
mediumTooltipLoader.item.show("Medium", p.x, p.y - 40, null)
}
}
onExited: {
if (mediumTooltipLoader.item) {
mediumTooltipLoader.item.hide()
}
mediumTooltipLoader.active = false
}
} }
DankActionButton { DankActionButton {
@@ -331,6 +357,19 @@ Column {
onClicked: { onClicked: {
root.compactModeChanged("music", 2) root.compactModeChanged("music", 2)
} }
onEntered: {
largeTooltipLoader.active = true
if (largeTooltipLoader.item) {
const p = largeSizeButton.mapToItem(null, largeSizeButton.width / 2, 0)
largeTooltipLoader.item.show("Large", p.x, p.y - 40, null)
}
}
onExited: {
if (largeTooltipLoader.item) {
largeTooltipLoader.item.hide()
}
largeTooltipLoader.active = false
}
} }
DankActionButton { DankActionButton {
@@ -373,6 +412,27 @@ Column {
!SettingsData.runningAppsCompactMode) !SettingsData.runningAppsCompactMode)
} }
} }
onEntered: {
compactTooltipLoader.active = true
if (compactTooltipLoader.item) {
let tooltipText = "Toggle Compact Mode"
if (modelData.id === "clock") {
tooltipText = SettingsData.clockCompactMode ? "Full Size" : "Compact"
} else if (modelData.id === "focusedWindow") {
tooltipText = SettingsData.focusedWindowCompactMode ? "Full Size" : "Compact"
} else if (modelData.id === "runningApps") {
tooltipText = SettingsData.runningAppsCompactMode ? "Full Size" : "Compact"
}
const p = compactModeButton.mapToItem(null, compactModeButton.width / 2, 0)
compactTooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
}
}
onExited: {
if (compactTooltipLoader.item) {
compactTooltipLoader.item.hide()
}
compactTooltipLoader.active = false
}
} }
Rectangle { Rectangle {
@@ -426,6 +486,7 @@ Column {
} }
DankActionButton { DankActionButton {
id: visibilityButton
visible: modelData.id !== "spacer" visible: modelData.id !== "spacer"
buttonSize: 32 buttonSize: 32
iconName: modelData.enabled ? "visibility" : "visibility_off" iconName: modelData.enabled ? "visibility" : "visibility_off"
@@ -436,6 +497,20 @@ Column {
modelData.id, modelData.id,
!modelData.enabled) !modelData.enabled)
} }
onEntered: {
visibilityTooltipLoader.active = true
if (visibilityTooltipLoader.item) {
const tooltipText = modelData.enabled ? "Hide" : "Show"
const p = visibilityButton.mapToItem(null, visibilityButton.width / 2, 0)
visibilityTooltipLoader.item.show(tooltipText, p.x, p.y - 40, null)
}
}
onExited: {
if (visibilityTooltipLoader.item) {
visibilityTooltipLoader.item.hide()
}
visibilityTooltipLoader.active = false
}
} }
Row { Row {
@@ -791,4 +866,34 @@ Column {
} }
} }
Loader {
id: smallTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
Loader {
id: mediumTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
Loader {
id: largeTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
Loader {
id: compactTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
Loader {
id: visibilityTooltipLoader
active: false
sourceComponent: DankTooltip {}
}
} }

View File

@@ -12,7 +12,6 @@ DankPopout {
id: systemUpdatePopout id: systemUpdatePopout
property var parentWidget: null property var parentWidget: null
property string triggerSection: "right"
property var triggerScreen: null property var triggerScreen: null
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -26,9 +25,9 @@ DankPopout {
popupWidth: 400 popupWidth: 400
popupHeight: 500 popupHeight: 500
triggerX: Screen.width - 600 - Theme.spacingL triggerX: Screen.width - 600 - Theme.spacingL
triggerY: Math.max(26 + SettingsData.topBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.topBarInnerPadding)) + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance triggerY: Math.max(26 + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2
triggerWidth: 55 triggerWidth: 55
positioning: "center" positioning: ""
screen: triggerScreen screen: triggerScreen
visible: shouldBeVisible visible: shouldBeVisible
shouldBeVisible: false shouldBeVisible: false

View File

@@ -69,7 +69,7 @@ PanelWindow {
width: shouldBeVisible ? (ToastService.hasDetails ? 380 : 350) : frozenWidth width: shouldBeVisible ? (ToastService.hasDetails ? 380 : 350) : frozenWidth
height: toastContent.height + Theme.spacingL * 2 height: toastContent.height + Theme.spacingL * 2
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
y: Theme.barHeight - 4 + SettingsData.topBarSpacing + 2 y: Theme.barHeight - 4 + SettingsData.dankBarSpacing + 2
color: { color: {
switch (ToastService.currentLevel) { switch (ToastService.currentLevel) {
case ToastService.levelError: case ToastService.levelError:

View File

@@ -1,153 +0,0 @@
import QtQuick
import Quickshell.Services.UPower
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: battery
property bool batteryPopupVisible: false
property string section: "right"
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))
signal toggleBatteryPopup()
width: batteryContent.implicitWidth + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
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
Row {
id: batteryContent
anchors.centerIn: parent
spacing: SettingsData.topBarNoBackground ? 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 screenX = currentScreen.x || 0;
const relativeX = globalPos.x - screenX;
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
}
toggleBatteryPopup();
}
}
Rectangle {
id: batteryTooltip
width: Math.max(120, tooltipText.contentWidth + Theme.spacingM * 2)
height: tooltipText.contentHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: Theme.widgetBaseBackgroundColor
border.color: Theme.surfaceVariantAlpha
border.width: 1
visible: batteryArea.containsMouse && !batteryPopupVisible
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
opacity: batteryArea.containsMouse ? 1 : 0
Column {
anchors.centerIn: parent
spacing: 2
StyledText {
id: tooltipText
text: {
if (!BatteryService.batteryAvailable) {
if (typeof PowerProfiles === "undefined") {
return "Power Management";
}
switch (PowerProfiles.profile) {
case PowerProfile.PowerSaver:
return "Power Profile: Power Saver";
case PowerProfile.Performance:
return "Power Profile: Performance";
default:
return "Power Profile: Balanced";
}
}
const status = BatteryService.batteryStatus;
const level = `${BatteryService.batteryLevel}%`;
const time = BatteryService.formatTimeRemaining();
if (time !== "Unknown") {
return `${status} ${level} ${time}`;
} else {
return `${status} ${level}`;
}
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}

View File

@@ -1,93 +0,0 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
property bool compactMode: false
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 ? 2 : Theme.spacingS
signal clockClicked
width: clockRow.implicitWidth + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = clockMouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
Row {
id: clockRow
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 screenX = currentScreen.x || 0
const relativeX = globalPos.x - screenX
popupTarget.setTriggerPosition(relativeX, barHeight + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen)
}
root.clockClicked()
}
}
}

View File

@@ -1,65 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isActive: false
property string section: "right"
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))
signal clicked()
width: notepadIcon.width + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
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.topBarNoBackground ? 0 : 4
anchors.topMargin: SettingsData.topBarNoBackground ? 0 : 4
visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0
opacity: 0.8
}
MouseArea {
id: notepadArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
root.clicked();
}
}
}

View File

@@ -1,71 +0,0 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
property bool hasUnread: false
property bool isActive: false
property string section: "right"
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))
signal clicked()
width: notificationIcon.width + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = notificationArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: notificationIcon
anchors.centerIn: parent
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
size: Theme.iconSize - 6
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.topBarNoBackground ? 0 : 6
anchors.topMargin: SettingsData.topBarNoBackground ? 0 : 6
visible: root.hasUnread
}
MouseArea {
id: notificationArea
anchors.fill: parent
hoverEnabled: true
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 + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
}
root.clicked();
}
}
}

View File

@@ -1,123 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Services.SystemTray
import Quickshell.Widgets
import qs.Common
Rectangle {
id: root
property var parentWindow: null
property var parentScreen: null
property real widgetHeight: 30
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 2 : Theme.spacingS
readonly property int calculatedWidth: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0
width: calculatedWidth
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SystemTray.items.values.length === 0) {
return "transparent";
}
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
visible: SystemTray.items.values.length > 0
Row {
id: systemTrayRow
anchors.centerIn: parent
spacing: 0
Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData
property string iconSource: {
let icon = trayItem && trayItem.icon;
if (typeof icon === 'string' || icon instanceof String) {
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0];
const path = split[1];
const fileName = name.substring(name.lastIndexOf("/") + 1);
return `file://${path}/${fileName}`;
}
return icon;
}
return "";
}
width: 24
height: 24
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
}
IconImage {
anchors.centerIn: parent
width: 16
height: 16
source: parent.iconSource
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) {
trayItem.activate();
return ;
}
if (trayItem.hasMenu) {
const globalPos = mapToGlobal(0, 0);
const currentScreen = parentScreen || Screen;
const screenX = currentScreen.x || 0;
const relativeX = globalPos.x - screenX;
menuAnchor.menu = trayItem.menu;
menuAnchor.anchor.window = parentWindow;
menuAnchor.anchor.rect = Qt.rect(relativeX, parentWindow.effectiveBarHeight + SettingsData.topBarSpacing, parent.width, 1);
menuAnchor.open();
}
}
}
}
}
}
QsMenuAnchor {
id: menuAnchor
}
}

View File

@@ -1,104 +0,0 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool isActive: false
property string section: "right"
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))
readonly property bool hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking
signal clicked()
width: updaterIcon.width + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
Row {
id: updaterIcon
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
id: statusIcon
anchors.verticalCenter: parent.verticalCenter
name: {
if (isChecking) return "refresh";
if (SystemUpdateService.hasError) return "error";
if (hasUpdates) return "system_update_alt";
return "check_circle";
}
size: Theme.iconSize - 6
color: {
if (SystemUpdateService.hasError) return Theme.error;
if (hasUpdates) return Theme.primary;
return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText);
}
RotationAnimation {
id: rotationAnimation
target: statusIcon
property: "rotation"
from: 0
to: 360
duration: 1000
running: isChecking
loops: Animation.Infinite
onRunningChanged: {
if (!running) {
statusIcon.rotation = 0
}
}
}
}
StyledText {
id: countText
anchors.verticalCenter: parent.verticalCenter
text: SystemUpdateService.updateCount.toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
visible: hasUpdates && !isChecking
}
}
MouseArea {
id: updaterArea
anchors.fill: parent
hoverEnabled: true
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 + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
}
root.clicked();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
// Passed in by TopBar
property int widgetHeight: 28
property int barHeight: 32
property string section: "right"
property var popupTarget: null
property var parentScreen: null
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
signal toggleVpnPopup()
width: Theme.iconSize + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent";
}
const baseColor = clickArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
DankIcon {
id: icon
name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off")
size: Theme.iconSize - 6
color: VpnService.connected ? Theme.primary : Theme.surfaceText
anchors.centerIn: parent
RotationAnimation on rotation {
running: VpnService.isBusy
loops: Animation.Infinite
from: 0
to: 360
duration: 900
}
}
MouseArea {
id: clickArea
anchors.fill: parent
hoverEnabled: true
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 + SettingsData.topBarSpacing + SettingsData.topBarBottomGap - 2 + Theme.popupDistance, width, section, currentScreen);
}
root.toggleVpnPopup();
}
}
Rectangle {
id: tooltip
width: Math.max(120, tooltipText.contentWidth + Theme.spacingM * 2)
height: tooltipText.contentHeight + Theme.spacingS * 2
radius: Theme.cornerRadius
color: Theme.widgetBaseBackgroundColor
border.color: Theme.surfaceVariantAlpha
border.width: 1
visible: clickArea.containsMouse && !(popupTarget && popupTarget.shouldBeVisible)
anchors.bottom: parent.top
anchors.bottomMargin: Theme.spacingS
anchors.horizontalCenter: parent.horizontalCenter
opacity: clickArea.containsMouse ? 1 : 0
Text {
id: tooltipText
anchors.centerIn: parent
text: {
if (!VpnService.connected) {
return "VPN Disconnected";
}
const names = VpnService.activeNames || [];
if (names.length <= 1) {
return "VPN Connected • " + (names[0] || "");
}
return "VPN Connected • " + names[0] + " +" + (names.length - 1);
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}

View File

@@ -38,6 +38,18 @@ LazyLoader {
property bool isColorSource: source.startsWith("#") property bool isColorSource: source.startsWith("#")
property string transitionType: SessionData.wallpaperTransition property string transitionType: SessionData.wallpaperTransition
property string actualTransitionType: transitionType property string actualTransitionType: transitionType
Connections {
target: SessionData
function onIsLightModeChanged() {
if (SessionData.perModeWallpaper) {
var newSource = SessionData.getMonitorWallpaper(modelData.name) || ""
if (newSource !== root.source) {
root.source = newSource
}
}
}
}
onTransitionTypeChanged: { onTransitionTypeChanged: {
if (transitionType === "random") { if (transitionType === "random") {
if (SessionData.includedTransitions.length === 0) { if (SessionData.includedTransitions.length === 0) {
@@ -77,7 +89,6 @@ LazyLoader {
monitor: modelData.name monitor: modelData.name
} }
Component.onDestruction: { Component.onDestruction: {
weProc.stop() weProc.stop()
} }
@@ -116,8 +127,10 @@ LazyLoader {
} }
function changeWallpaper(newPath, force) { function changeWallpaper(newPath, force) {
if (!force && newPath === currentWallpaper.source) return if (!force && newPath === currentWallpaper.source)
if (!newPath || newPath.startsWith("#")) return return
if (!newPath || newPath.startsWith("#"))
return
if (root.transitioning) { if (root.transitioning) {
transitionAnimation.stop() transitionAnimation.stop()
@@ -163,7 +176,6 @@ LazyLoader {
} }
} }
Loader { Loader {
anchors.fill: parent anchors.fill: parent
active: !root.source || root.isColorSource active: !root.source || root.isColorSource
@@ -212,15 +224,14 @@ LazyLoader {
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
onStatusChanged: { onStatusChanged: {
if (status !== Image.Ready) return if (status !== Image.Ready)
return
if (root.actualTransitionType === "none") { if (root.actualTransitionType === "none") {
currentWallpaper.source = source currentWallpaper.source = source
nextWallpaper.source = "" nextWallpaper.source = ""
root.transitionProgress = 0.0 root.transitionProgress = 0.0
} else { } else {
currentWallpaper.layer.enabled = true
layer.enabled = true
visible = true visible = true
if (!root.transitioning) { if (!root.transitioning) {
transitionAnimation.start() transitionAnimation.start()
@@ -229,11 +240,36 @@ LazyLoader {
} }
} }
ShaderEffect { Loader {
id: fadeShader id: effectLoader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "fade" && (root.hasCurrent || root.booting) active: root.actualTransitionType !== "none" && (root.hasCurrent || root.booting)
sourceComponent: {
switch (root.actualTransitionType) {
case "fade":
return fadeComp
case "wipe":
return wipeComp
case "disc":
return discComp
case "stripes":
return stripesComp
case "iris bloom":
return irisComp
case "pixelate":
return pixelateComp
case "portal":
return portalComp
default:
return null
}
}
}
Component {
id: fadeComp
ShaderEffect {
anchors.fill: parent
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -245,15 +281,14 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_fade.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_fade.frag.qsb")
} }
}
Component {
id: wipeComp
ShaderEffect { ShaderEffect {
id: wipeShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "wipe" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -267,15 +302,14 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_wipe.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_wipe.frag.qsb")
} }
}
Component {
id: discComp
ShaderEffect { ShaderEffect {
id: discShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "disc" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -291,15 +325,14 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_disc.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_disc.frag.qsb")
} }
}
Component {
id: stripesComp
ShaderEffect { ShaderEffect {
id: stripesShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "stripes" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -315,15 +348,14 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_stripes.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_stripes.frag.qsb")
} }
}
Component {
id: irisComp
ShaderEffect { ShaderEffect {
id: irisBloomShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "iris bloom" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -339,19 +371,18 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_iris_bloom.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_iris_bloom.frag.qsb")
} }
}
Component {
id: pixelateComp
ShaderEffect { ShaderEffect {
id: pixelateShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "pixelate" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
property real smoothness: root.edgeSmoothness // controls starting block size property real smoothness: root.edgeSmoothness
property real fillMode: root.fillMode property real fillMode: root.fillMode
property vector4d fillColor: root.fillColor property vector4d fillColor: root.fillColor
property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width) property real imageWidth1: Math.max(1, root.hasCurrent ? source1.sourceSize.width : modelData.width)
@@ -363,15 +394,14 @@ LazyLoader {
property real centerX: root.discCenterX property real centerX: root.discCenterX
property real centerY: root.discCenterY property real centerY: root.discCenterY
property real aspectRatio: root.width / root.height property real aspectRatio: root.width / root.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_pixelate.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_pixelate.frag.qsb")
} }
}
Component {
id: portalComp
ShaderEffect { ShaderEffect {
id: portalShader
anchors.fill: parent anchors.fill: parent
visible: root.actualTransitionType === "portal" && (root.hasCurrent || root.booting)
property variant source1: root.hasCurrent ? currentWallpaper : transparentSource property variant source1: root.hasCurrent ? currentWallpaper : transparentSource
property variant source2: nextWallpaper property variant source2: nextWallpaper
property real progress: root.transitionProgress property real progress: root.transitionProgress
@@ -387,9 +417,9 @@ LazyLoader {
property real imageHeight2: Math.max(1, source2.sourceSize.height) property real imageHeight2: Math.max(1, source2.sourceSize.height)
property real screenWidth: modelData.width property real screenWidth: modelData.width
property real screenHeight: modelData.height property real screenHeight: modelData.height
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_portal.frag.qsb") fragmentShader: Qt.resolvedUrl("../Shaders/qsb/wp_portal.frag.qsb")
} }
}
NumberAnimation { NumberAnimation {
id: transitionAnimation id: transitionAnimation
@@ -407,8 +437,6 @@ LazyLoader {
nextWallpaper.source = "" nextWallpaper.source = ""
nextWallpaper.visible = false nextWallpaper.visible = false
currentWallpaper.visible = root.actualTransitionType === "none" currentWallpaper.visible = root.actualTransitionType === "none"
currentWallpaper.layer.enabled = false
nextWallpaper.layer.enabled = false
root.transitionProgress = 0.0 root.transitionProgress = 0.0
}) })
} }

View File

@@ -175,6 +175,60 @@ paru -S dms-shell-git
nix profile install github:AvengeMedia/DankMaterialShell nix profile install github:AvengeMedia/DankMaterialShell
``` ```
#### nixOS - via home-manager
To install using home-manager, you need to add this repo into your flake inputs:
``` nix
dankMaterialShell = {
url = "github:AvengeMedia/DankMaterialShell";
inputs.nixpkgs.follows = "nixpkgs";
};
```
Then somewhere in your home-manager config, add this to the imports:
``` nix
imports = [
inputs.dankMaterialShell.homeModules.dankMaterialShell.default
];
```
If you use Niri, the `niri` homeModule provides additional options for Niri integration, such as key bindings and spawn:
``` nix
imports = [
inputs.dankMaterialShell.homeModules.dankMaterialShell.default
inputs.dankMaterialShell.homeModules.dankMaterialShell.niri
];
```
> [!IMPORTANT]
> To use the `niri` homeModule, you must have `sobidoo/niri-flake` in your inputs:
``` nix
niri = {
url = "github:sodiboo/niri-flake";
inputs.nixpkgs.follows = "nixpkgs";
};
```
And import it in home-manager:
``` nix
imports = [
inputs.niri.homeModules.niri
];
```
Now you can enable it with:
``` nix
programs.dankMaterialShell.enable = true;
```
There are a lot of possible configurations that you can enable/disable in the flake, check [nix/default.nix](nix/default.nix) and [nix/niri.nix](nix/niri.nix) to see them all.
#### Other Distributions - via manual installation #### Other Distributions - via manual installation
**1. Install Quickshell (Varies by Distribution)** **1. Install Quickshell (Varies by Distribution)**
@@ -673,5 +727,6 @@ DankMaterialShell welcomes contributions! Whether it's bug fixes, new widgets, t
- [quickshell](https://quickshell.org/) the core of what makes a shell like this possible. - [quickshell](https://quickshell.org/) the core of what makes a shell like this possible.
- [niri](https://github.com/YaLTeR/niri) for the awesome scrolling compositor. - [niri](https://github.com/YaLTeR/niri) for the awesome scrolling compositor.
- [Ly-sec](http://github.com/ly-sec) for awesome wallpaper effects among other things from [Noctalia](https://github.com/noctalia-dev/noctalia-shell)
- [soramanew](https://github.com/soramanew) who built [caelestia](https://github.com/caelestia-dots/shell) which served as inspiration and guidance for many dank widgets. - [soramanew](https://github.com/soramanew) who built [caelestia](https://github.com/caelestia-dots/shell) which served as inspiration and guidance for many dank widgets.
- [end-4](https://github.com/end-4) for [dots-hyprland](https://github.com/end-4/dots-hyprland) which also served as inspiration and guidance for many dank widgets. - [end-4](https://github.com/end-4) for [dots-hyprland](https://github.com/end-4/dots-hyprland) which also served as inspiration and guidance for many dank widgets.

View File

@@ -13,9 +13,18 @@ Singleton {
readonly property PwNode sink: Pipewire.defaultAudioSink readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource readonly property PwNode source: Pipewire.defaultAudioSource
signal volumeChanged property bool suppressOSD: true
signal micMuteChanged signal micMuteChanged
Timer {
id: startupTimer
interval: 500
repeat: false
running: true
onTriggered: root.suppressOSD = false
}
function displayName(node) { function displayName(node) {
if (!node) { if (!node) {
return "" return ""
@@ -95,7 +104,6 @@ Singleton {
const clampedVolume = Math.max(0, Math.min(100, percentage)) const clampedVolume = Math.max(0, Math.min(100, percentage))
root.sink.audio.volume = clampedVolume / 100 root.sink.audio.volume = clampedVolume / 100
root.volumeChanged()
return `Volume set to ${clampedVolume}%` return `Volume set to ${clampedVolume}%`
} }
@@ -148,7 +156,6 @@ Singleton {
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue)) const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue))
root.sink.audio.volume = newVolume / 100 root.sink.audio.volume = newVolume / 100
root.volumeChanged()
return `Volume increased to ${newVolume}%` return `Volume increased to ${newVolume}%`
} }
@@ -166,14 +173,11 @@ Singleton {
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue)) const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue))
root.sink.audio.volume = newVolume / 100 root.sink.audio.volume = newVolume / 100
root.volumeChanged()
return `Volume decreased to ${newVolume}%` return `Volume decreased to ${newVolume}%`
} }
function mute(): string { function mute(): string {
const result = root.toggleMute() return root.toggleMute()
root.volumeChanged()
return result
} }
function setmic(percentage: string): string { function setmic(percentage: string): string {

View File

@@ -82,6 +82,7 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
detectCompositor() detectCompositor()
NiriService.generateNiriLayoutConfig()
} }
function filterCurrentWorkspace(toplevels, screen) { function filterCurrentWorkspace(toplevels, screen) {
@@ -192,6 +193,7 @@ Singleton {
root.isHyprland = false root.isHyprland = false
root.compositor = "niri" root.compositor = "niri"
console.log("CompositorService: Detected Niri with socket:", root.niriSocket) console.log("CompositorService: Detected Niri with socket:", root.niriSocket)
NiriService.generateNiriBinds()
} else { } else {
root.isHyprland = false root.isHyprland = false
root.isNiri = true root.isNiri = true

View File

@@ -2,10 +2,12 @@ pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtCore
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland import Quickshell.Wayland
import qs.Common
Singleton { Singleton {
id: root id: root
@@ -31,6 +33,7 @@ Singleton {
property bool suppressConfigToast: true property bool suppressConfigToast: true
property bool suppressNextConfigToast: false property bool suppressNextConfigToast: false
property bool matugenSuppression: false property bool matugenSuppression: false
property bool configGenerationPending: false
readonly property string socketPath: Quickshell.env("NIRI_SOCKET") readonly property string socketPath: Quickshell.env("NIRI_SOCKET")
@@ -415,7 +418,7 @@ Singleton {
return send({ return send({
"Action": { "Action": {
"DoScreenTransition": { "DoScreenTransition": {
"delay_ms": 100, "delay_ms": 0,
} }
} }
}) })
@@ -652,6 +655,7 @@ Singleton {
return result return result
} }
Timer { Timer {
id: suppressToastTimer id: suppressToastTimer
interval: 3000 interval: 3000
@@ -663,4 +667,101 @@ Singleton {
interval: 2000 interval: 2000
onTriggered: root.matugenSuppression = false onTriggered: root.matugenSuppression = false
} }
Timer {
id: configGenerationDebounce
interval: 100
onTriggered: root.doGenerateNiriLayoutConfig()
}
function generateNiriLayoutConfig() {
const niriSocket = Quickshell.env("NIRI_SOCKET")
if (!niriSocket || niriSocket.length === 0) {
return
}
if (configGenerationPending) {
return
}
configGenerationPending = true
configGenerationDebounce.restart()
}
function doGenerateNiriLayoutConfig() {
console.log("NiriService: Generating layout config...")
const cornerRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12
const gaps = typeof SettingsData !== "undefined" ? Math.max(4, SettingsData.dankBarSpacing) : 4
const configContent = `layout {
gaps ${gaps}
border {
width 2
}
focus-ring {
width 2
}
}
window-rule {
geometry-corner-radius ${cornerRadius}
clip-to-geometry true
tiled-state true
draw-border-with-background false
}`
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
const niriDmsDir = configDir + "/niri/dms"
const configPath = niriDmsDir + "/layout.kdl"
writeConfigProcess.configContent = configContent
writeConfigProcess.configPath = configPath
writeConfigProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${configPath}" << 'EOF'\n${configContent}\nEOF`]
writeConfigProcess.running = true
configGenerationPending = false
}
function generateNiriBinds() {
console.log("NiriService: Generating binds config...")
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
const niriDmsDir = configDir + "/niri/dms"
const bindsPath = niriDmsDir + "/binds.kdl"
const sourceBindsPath = Paths.strip(Qt.resolvedUrl("niri-binds.kdl"))
writeBindsProcess.bindsPath = bindsPath
writeBindsProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cp "${sourceBindsPath}" "${bindsPath}"`]
writeBindsProcess.running = true
}
Process {
id: writeConfigProcess
property string configContent: ""
property string configPath: ""
onExited: exitCode => {
if (exitCode === 0) {
console.log("NiriService: Generated layout config at", configPath)
} else {
console.warn("NiriService: Failed to write layout config, exit code:", exitCode)
}
}
}
Process {
id: writeBindsProcess
property string bindsPath: ""
onExited: exitCode => {
if (exitCode === 0) {
console.log("NiriService: Generated binds config at", bindsPath)
} else {
console.warn("NiriService: Failed to write binds config, exit code:", exitCode)
}
}
}
} }

View File

@@ -226,6 +226,7 @@ Singleton {
} }
const shouldShowPopup = !root.popupsDisabled && !SessionData.doNotDisturb const shouldShowPopup = !root.popupsDisabled && !SessionData.doNotDisturb
const isTransient = notif.transient
const wrapper = notifComponent.createObject(root, { const wrapper = notifComponent.createObject(root, {
"popup": shouldShowPopup, "popup": shouldShowPopup,
"notification": notif "notification": notif
@@ -233,8 +234,10 @@ Singleton {
if (wrapper) { if (wrapper) {
root.allWrappers.push(wrapper) root.allWrappers.push(wrapper)
if (!isTransient) {
root.notifications.push(wrapper) root.notifications.push(wrapper)
_trimStored() _trimStored()
}
Qt.callLater(() => { Qt.callLater(() => {
_initWrapperPersistence(wrapper) _initWrapperPersistence(wrapper)
@@ -407,8 +410,11 @@ Singleton {
addGateBusy = false addGateBusy = false
notificationQueue = [] notificationQueue = []
for (const w of allWrappers) for (const w of allWrappers) {
if (w) {
w.popup = false w.popup = false
}
}
visibleNotifications = [] visibleNotifications = []
_dismissQueue = notifications.slice() _dismissQueue = notifications.slice()
@@ -528,6 +534,7 @@ Singleton {
const groups = {} const groups = {}
for (const notif of notifications) { for (const notif of notifications) {
if (!notif) continue
const groupKey = getGroupKey(notif) const groupKey = getGroupKey(notif)
if (!groups[groupKey]) { if (!groups[groupKey]) {
groups[groupKey] = { groups[groupKey] = {
@@ -563,6 +570,7 @@ Singleton {
const groups = {} const groups = {}
for (const notif of popups) { for (const notif of popups) {
if (!notif) continue
const groupKey = getGroupKey(notif) const groupKey = getGroupKey(notif)
if (!groups[groupKey]) { if (!groups[groupKey]) {
groups[groupKey] = { groups[groupKey] = {
@@ -630,9 +638,11 @@ Singleton {
const currentMessageIds = new Set() const currentMessageIds = new Set()
for (const group of groupedNotifications) { for (const group of groupedNotifications) {
for (const notif of group.notifications) { for (const notif of group.notifications) {
if (notif && notif.notification) {
currentMessageIds.add(notif.notification.id) currentMessageIds.add(notif.notification.id)
} }
} }
}
let newExpandedGroups = {} let newExpandedGroups = {}
for (const key in expandedGroups) { for (const key in expandedGroups) {
if (currentGroupKeys.has(key) && expandedGroups[key]) { if (currentGroupKeys.has(key) && expandedGroups[key]) {

Some files were not shown because too many files have changed in this diff Show More