1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-30 00:12:50 -05:00

meta: Vertical Bar, Notification Popup Position Options, ++

- CC Color picker widget
- Tooltips in more places
- Attempt to improve niri screen transitiosn
This commit is contained in:
bbedward
2025-09-30 09:51:18 -04:00
parent d280505b9f
commit e875d1a5d7
84 changed files with 4937 additions and 2019 deletions

View File

@@ -140,7 +140,8 @@ Singleton {
property bool dankBarSquareCorners: false property bool dankBarSquareCorners: false
property bool dankBarNoBackground: false property bool dankBarNoBackground: false
property bool dankBarGothCornersEnabled: false property bool dankBarGothCornersEnabled: false
property bool dankBarAtBottom: 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"
@@ -148,6 +149,7 @@ 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: ({})
readonly property string defaultFontFamily: "Inter Variable" readonly property string defaultFontFamily: "Inter Variable"
readonly property string defaultMonoFontFamily: "Fira Code" readonly property string defaultMonoFontFamily: "Fira Code"
@@ -335,13 +337,14 @@ Singleton {
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
notificationPopupPosition = settings.notificationPopupPosition !== undefined ? settings.notificationPopupPosition : SettingsData.Position.Top
dankBarSpacing = settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : (settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4) dankBarSpacing = settings.dankBarSpacing !== undefined ? settings.dankBarSpacing : (settings.topBarSpacing !== undefined ? settings.topBarSpacing : 4)
dankBarBottomGap = settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : (settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0) dankBarBottomGap = settings.dankBarBottomGap !== undefined ? settings.dankBarBottomGap : (settings.topBarBottomGap !== undefined ? settings.topBarBottomGap : 0)
dankBarInnerPadding = settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : (settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 4) dankBarInnerPadding = settings.dankBarInnerPadding !== undefined ? settings.dankBarInnerPadding : (settings.topBarInnerPadding !== undefined ? settings.topBarInnerPadding : 4)
dankBarSquareCorners = settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : (settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false) dankBarSquareCorners = settings.dankBarSquareCorners !== undefined ? settings.dankBarSquareCorners : (settings.topBarSquareCorners !== undefined ? settings.topBarSquareCorners : false)
dankBarNoBackground = settings.dankBarNoBackground !== undefined ? settings.dankBarNoBackground : (settings.topBarNoBackground !== undefined ? settings.topBarNoBackground : 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) dankBarGothCornersEnabled = settings.dankBarGothCornersEnabled !== undefined ? settings.dankBarGothCornersEnabled : (settings.topBarGothCornersEnabled !== undefined ? settings.topBarGothCornersEnabled : false)
dankBarAtBottom = settings.dankBarAtBottom !== undefined ? settings.dankBarAtBottom : (settings.topBarAtBottom !== undefined ? settings.topBarAtBottom : 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"
@@ -456,7 +459,7 @@ Singleton {
"dankBarSquareCorners": dankBarSquareCorners, "dankBarSquareCorners": dankBarSquareCorners,
"dankBarNoBackground": dankBarNoBackground, "dankBarNoBackground": dankBarNoBackground,
"dankBarGothCornersEnabled": dankBarGothCornersEnabled, "dankBarGothCornersEnabled": dankBarGothCornersEnabled,
"dankBarAtBottom": dankBarAtBottom, "dankBarPosition": dankBarPosition,
"lockScreenShowPowerActions": lockScreenShowPowerActions, "lockScreenShowPowerActions": lockScreenShowPowerActions,
"hideBrightnessSlider": hideBrightnessSlider, "hideBrightnessSlider": hideBrightnessSlider,
"widgetBackgroundColor": widgetBackgroundColor, "widgetBackgroundColor": widgetBackgroundColor,
@@ -464,6 +467,7 @@ Singleton {
"notificationTimeoutLow": notificationTimeoutLow, "notificationTimeoutLow": notificationTimeoutLow,
"notificationTimeoutNormal": notificationTimeoutNormal, "notificationTimeoutNormal": notificationTimeoutNormal,
"notificationTimeoutCritical": notificationTimeoutCritical, "notificationTimeoutCritical": notificationTimeoutCritical,
"notificationPopupPosition": notificationPopupPosition,
"screenPreferences": screenPreferences "screenPreferences": screenPreferences
}, null, 2)) }, null, 2))
} }
@@ -580,11 +584,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)
}) })
} }
@@ -976,8 +980,13 @@ Singleton {
function setShowDock(enabled) { function setShowDock(enabled) {
showDock = enabled showDock = enabled
if (enabled && dankBarAtBottom && dockPosition === SettingsData.Position.Bottom) { if (enabled && dankBarPosition === SettingsData.Position.Top) {
setDankBarAtBottom(false) setDockPosition(SettingsData.Position.Bottom)
return
}
if (enabled && dankBarPosition === SettingsData.Position.Top) {
setDockPosition(SettingsData.Position.Bottom)
return
} }
saveSettings() saveSettings()
} }
@@ -1042,6 +1051,56 @@ Singleton {
saveSettings() saveSettings()
} }
function setNotificationPopupPosition(position) {
notificationPopupPosition = position
saveSettings()
}
function sendTestNotifications() {
sendTestNotification(0)
testNotifTimer1.start()
testNotifTimer2.start()
}
function sendTestNotification(index) {
const notifications = [
["Notification Position Test", "DMS test notification 1 of 3 ~ Hi there!", "dialog-information"],
["Second Test", "DMS Notification 2 of 3 ~ Check it out!", "emblem-default"],
["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "emblem-favorite"]
]
if (index < 0 || index >= notifications.length) {
return
}
const notif = notifications[index]
testNotificationProcess.command = ["notify-send", "-h", "int:transient:1", "-a", "DMS", "-i", notif[2], notif[0], notif[1]]
testNotificationProcess.running = true
}
property Process testNotificationProcess
testNotificationProcess: Process {
command: []
running: false
}
property Timer testNotifTimer1
testNotifTimer1: Timer {
interval: 400
repeat: false
onTriggered: sendTestNotification(1)
}
property Timer testNotifTimer2
testNotifTimer2: Timer {
interval: 800
repeat: false
onTriggered: sendTestNotification(2)
}
function setDankBarSpacing(spacing) { function setDankBarSpacing(spacing) {
dankBarSpacing = spacing dankBarSpacing = spacing
saveSettings() saveSettings()
@@ -1072,24 +1131,26 @@ Singleton {
saveSettings() saveSettings()
} }
function setDankBarAtBottom(enabled) { function setDankBarPosition(position) {
dankBarAtBottom = enabled dankBarPosition = position
if (enabled && showDock && dockPosition === SettingsData.Position.Bottom) { if (position === SettingsData.Position.Bottom && showDock) {
setDockPosition(SettingsData.Position.Top) setDockPosition(SettingsData.Position.Top)
return
} }
if (!enabled && showDock && dockPosition === SettingsData.Position.Top) { if (position === SettingsData.Position.Top && showDock) {
setDockPosition(SettingsData.Position.Bottom) setDockPosition(SettingsData.Position.Bottom)
return
} }
saveSettings() saveSettings()
} }
function setDockPosition(position) { function setDockPosition(position) {
dockPosition = position dockPosition = position
if (position === SettingsData.Position.Bottom && dankBarAtBottom && showDock) { if (position === SettingsData.Position.Bottom && dankBarPosition === SettingsData.Position.Bottom && showDock) {
setDankBarAtBottom(false) setDankBarPosition(SettingsData.Position.Top)
} }
if (position === SettingsData.Position.Top && !dankBarAtBottom && showDock) { if (position === SettingsData.Position.Top && dankBarPosition === SettingsData.Position.Top && showDock) {
setDankBarAtBottom(true) setDankBarPosition(SettingsData.Position.Bottom)
} }
saveSettings() saveSettings()
Qt.callLater(() => forceDockLayoutRefresh()) Qt.callLater(() => forceDockLayoutRefresh())
@@ -1108,7 +1169,28 @@ Singleton {
} }
function getPopupYPosition(barHeight) { function getPopupYPosition(barHeight) {
return barHeight + dankBarSpacing + dankBarBottomGap - 2 + Theme.popupDistance 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) {

View File

@@ -16,7 +16,8 @@ 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"
@@ -88,7 +89,7 @@ Singleton {
} }
if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) { if (typeof SettingsData !== "undefined" && SettingsData.currentThemeName) {
switchTheme(SettingsData.currentThemeName, false) switchTheme(SettingsData.currentThemeName, false, false)
} }
} }
@@ -268,7 +269,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
@@ -280,7 +286,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 {
@@ -293,8 +298,15 @@ Singleton {
generateSystemThemesFromCurrentTheme() generateSystemThemesFromCurrentTheme()
} }
function setLightMode(light, savePrefs = true) { function setLightMode(light, savePrefs = true, enableTransition = false) {
screenTransition() if (enableTransition) {
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)
@@ -303,11 +315,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
} }
@@ -331,8 +342,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) {
@@ -356,7 +369,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
@@ -640,6 +652,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
@@ -839,12 +852,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"
} }
@@ -852,4 +865,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

@@ -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

@@ -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.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 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: {

View File

@@ -14,6 +14,7 @@ Column {
property var model: null property var model: null
property var expandedWidgetData: null property var expandedWidgetData: null
property var bluetoothCodecSelector: 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)
@@ -25,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)
@@ -131,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
} }
@@ -532,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 || "") {
@@ -551,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
@@ -563,7 +581,7 @@ enabled: !root.editMode
} }
case "darkMode": case "darkMode":
{ {
Theme.toggleLightMode() root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb":
@@ -604,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 || "") {
@@ -623,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
@@ -635,7 +667,7 @@ enabled: !root.editMode
} }
case "darkMode": case "darkMode":
{ {
Theme.toggleLightMode() root.darkModeTransitionPending = true
break break
} }
case "doNotDisturb": case "doNotDisturb":
@@ -671,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

@@ -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.dankBarSpacing + 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)
} }
@@ -154,6 +153,7 @@ DankPopout {
expandedWidgetData: root.expandedWidgetData expandedWidgetData: root.expandedWidgetData
model: widgetModel model: widgetModel
bluetoothCodecSelector: bluetoothCodecSelector bluetoothCodecSelector: bluetoothCodecSelector
colorPickerModal: root.colorPickerModal
onExpandClicked: (widgetData, globalIndex) => { onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex root.expandedWidgetIndex = globalIndex
root.expandedWidgetData = widgetData root.expandedWidgetData = widgetData
@@ -223,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

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

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

@@ -12,8 +12,9 @@ Rectangle {
property bool enabled: true property bool enabled: true
property string secondaryText: "" property string secondaryText: ""
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,214 @@
import QtQuick
import qs.Common
Item {
id: root
required property var barWindow
required property var axis
readonly property real correctWidth: barWindow.isVertical ? barWindow.implicitWidth : parent.width
readonly property real correctHeight: barWindow.isVertical ? parent.height : barWindow.implicitHeight
width: correctWidth
height: correctHeight
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: barWindow.isVertical ? barWindow.implicitWidth : parent.width
readonly property real correctHeight: barWindow.isVertical ? parent.height : barWindow.implicitHeight
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: barWindow.isVertical ? barWindow.implicitWidth : parent.width
readonly property real correctHeight: barWindow.isVertical ? parent.height : barWindow.implicitHeight
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

@@ -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.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
signal toggleBatteryPopup()
width: batteryContent.implicitWidth + horizontalPadding * 2
height: widgetHeight
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
Row {
id: batteryContent
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 screenX = currentScreen.x || 0;
const relativeX = globalPos.x - screenX;
popupTarget.setTriggerPosition(relativeX, SettingsData.getPopupYPosition(barHeight), 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

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

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.dankBarNoBackground ? 2 : Theme.spacingS
signal clockClicked
width: clockRow.implicitWidth + horizontalPadding * 2
height: widgetHeight
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);
}
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, SettingsData.getPopupYPosition(barHeight), width, section, currentScreen)
}
root.clockClicked()
}
}
}

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

@@ -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.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
signal clicked()
width: notificationIcon.width + horizontalPadding * 2
height: widgetHeight
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 {
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.dankBarNoBackground ? 0 : 6
anchors.topMargin: SettingsData.dankBarNoBackground ? 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, SettingsData.getPopupYPosition(barHeight), width, section, currentScreen);
}
root.clicked();
}
}
}

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.dankBarSpacing + 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.dankBarSpacing + 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

@@ -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.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
signal toggleVpnPopup()
width: Theme.iconSize + horizontalPadding * 2
height: widgetHeight
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
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, SettingsData.getPopupYPosition(barHeight), 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

@@ -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,18 +5,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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,14 +16,14 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), width, section, currentScreen);
} }
root.clicked(); root.clicked();
} }

View File

@@ -7,18 +7,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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,18 +7,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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.dankBarNoBackground ? 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,8 +49,8 @@ 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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.dankBarNoBackground ? 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,8 +86,8 @@ 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (!activeWindow || !activeWindow.title) { if (!activeWindow || !activeWindow.title) {
@@ -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.dankBarNoBackground ? 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;
@@ -65,8 +67,8 @@ Rectangle {
} }
} }
width: gpuTempContent.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (gpuTempContent.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (gpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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,14 +8,16 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {

View File

@@ -10,12 +10,15 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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.dankBarNoBackground ? 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, SettingsData.getPopupYPosition(barHeight), width, section, currentScreen);
} }
root.clicked();
} }
} }
@@ -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,24 +23,33 @@ 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.dankBarNoBackground ? 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
height: currentContentHeight
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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.dankBarNoBackground ? 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,8 +28,8 @@ Rectangle {
} }
} }
width: contentRow.implicitWidth + horizontalPadding * 2 width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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

@@ -7,11 +7,13 @@ 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 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.dankBarNoBackground ? 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()
@@ -25,7 +27,6 @@ Rectangle {
return null return null
} }
// Try focused screen first
const targetScreen = focusedScreenName const targetScreen = focusedScreenName
if (targetScreen) { if (targetScreen) {
for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) { for (var i = 0; i < notepadSlideoutVariants.instances.length; i++) {
@@ -36,15 +37,14 @@ Rectangle {
} }
} }
// Fallback to first available
return notepadSlideoutVariants.instances.length > 0 ? notepadSlideoutVariants.instances[0] : null return notepadSlideoutVariants.instances.length > 0 ? notepadSlideoutVariants.instances[0] : null
} }
readonly property var notepadInstance: resolveNotepadInstance() readonly property var notepadInstance: resolveNotepadInstance()
readonly property bool isActive: notepadInstance?.isVisible ?? false readonly property bool isActive: notepadInstance?.isVisible ?? false
width: notepadIcon.width + horizontalPadding * 2 width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2)
height: widgetHeight height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {

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,17 +7,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: hasActivePrivacy visible: hasActivePrivacy
opacity: hasActivePrivacy ? 1 : 0 opacity: hasActivePrivacy ? 1 : 0
@@ -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,18 +7,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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.dankBarNoBackground ? 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,8 +38,8 @@ Rectangle {
} }
} }
width: calculatedWidth width: isVertical ? widgetThickness : calculatedSize
height: widgetHeight height: isVertical ? calculatedSize : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: windowCount > 0 visible: windowCount > 0
clip: false clip: false
@@ -143,18 +144,22 @@ Rectangle {
} }
} }
Row { Loader {
id: windowRow id: layoutLoader
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS sourceComponent: root.isVertical ? columnLayout : rowLayout
}
Repeater { Component {
id: windowRepeater id: rowLayout
Row {
spacing: Theme.spacingXS
model: sortedToplevels Repeater {
id: windowRepeater
model: sortedToplevels
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
property bool isFocused: modelData.activated property bool isFocused: modelData.activated
@@ -288,10 +293,10 @@ 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;
windowContextMenuLoader.active = true; windowContextMenuLoader.active = true;
if (windowContextMenuLoader.item) { if (windowContextMenuLoader.item) {
windowContextMenuLoader.item.currentWindow = toplevelObject; windowContextMenuLoader.item.currentWindow = toplevelObject;
@@ -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.dankBarSpacing - 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.dankBarSpacing + 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;
@@ -330,6 +341,198 @@ 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 {
@@ -337,7 +540,7 @@ Rectangle {
active: false active: false
sourceComponent: RunningAppsTooltip {} sourceComponent: DankTooltip {}
} }
Loader { Loader {

View File

@@ -10,15 +10,17 @@ import qs.Widgets
Rectangle { Rectangle {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var parentWindow: null property var parentWindow: null
property var parentScreen: null property var parentScreen: null
property real widgetHeight: 30 property real widgetThickness: 30
property bool isAtBottom: false property bool isAtBottom: false
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property int calculatedWidth: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0 readonly property int calculatedSize: SystemTray.items.values.length > 0 ? SystemTray.items.values.length * 24 + horizontalPadding * 2 : 0
width: calculatedWidth width: isVertical ? widgetThickness : calculatedSize
height: widgetHeight height: isVertical ? calculatedSize : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SystemTray.items.values.length === 0) { if (SystemTray.items.values.length === 0) {
@@ -34,16 +36,21 @@ Rectangle {
} }
visible: SystemTray.items.values.length > 0 visible: SystemTray.items.values.length > 0
Row { Loader {
id: systemTrayRow id: layoutLoader
anchors.centerIn: parent anchors.centerIn: parent
spacing: 0 sourceComponent: root.isVertical ? columnComp : rowComp
}
Repeater { Component {
model: SystemTray.items.values id: rowComp
Row {
spacing: 0
delegate: Item { Repeater {
model: SystemTray.items.values
delegate: Item {
property var trayItem: modelData property var trayItem: modelData
property string iconSource: { property string iconSource: {
let icon = trayItem && trayItem.icon; let icon = trayItem && trayItem.icon;
@@ -108,7 +115,7 @@ Rectangle {
return ; return ;
} }
if (trayItem.hasMenu) { if (trayItem.hasMenu) {
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom); root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis);
} }
} }
} }
@@ -116,7 +123,89 @@ Rectangle {
} }
} }
}
}
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 { Component {
@@ -129,6 +218,8 @@ Rectangle {
property var anchorItem: null property var anchorItem: null
property var parentScreen: null property var parentScreen: null
property bool isAtBottom: false property bool isAtBottom: false
property bool isVertical: false
property var axis: null
property bool showMenu: false property bool showMenu: false
property var menuHandle: null property var menuHandle: null
@@ -137,11 +228,13 @@ Rectangle {
return entryStack.count ? entryStack.get(entryStack.count - 1).handle : null return entryStack.count ? entryStack.get(entryStack.count - 1).handle : null
} }
function showForTrayItem(item, anchor, screen, atBottom) { function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
trayItem = item trayItem = item
anchorItem = anchor anchorItem = anchor
parentScreen = screen parentScreen = screen
isAtBottom = atBottom isAtBottom = atBottom
isVertical = vertical
axis = axisObj
menuHandle = item?.menu menuHandle = item?.menu
if (parentScreen) { if (parentScreen) {
@@ -185,7 +278,7 @@ Rectangle {
PanelWindow { PanelWindow {
id: menuWindow id: menuWindow
visible: menuRoot.showMenu && menuRoot.trayItem?.hasMenu visible: menuRoot.showMenu && (menuRoot.trayItem?.hasMenu ?? false)
WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1 WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
@@ -218,18 +311,29 @@ Rectangle {
const relativeX = globalPos.x - screenX const relativeX = globalPos.x - screenX
const relativeY = globalPos.y - screenY const relativeY = globalPos.y - screenY
const widgetHeight = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6) const widgetThickness = Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6)
const effectiveBarHeight = Math.max(widgetHeight + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) const effectiveBarThickness = Math.max(widgetThickness + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
let targetY if (menuRoot.isVertical) {
if (menuRoot.isAtBottom) { const edge = menuRoot.axis?.edge
const popupY = effectiveBarHeight + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2 + Theme.popupDistance let targetX
targetY = screen.height - popupY 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 { } else {
targetY = effectiveBarHeight + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 2 + Theme.popupDistance 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)
} }
anchorPos = Qt.point(relativeX + menuRoot.anchorItem.width / 2, targetY)
} }
Rectangle { Rectangle {
@@ -239,19 +343,37 @@ Rectangle {
height: Math.max(40, menuColumn.implicitHeight + Theme.spacingS * 2) height: Math.max(40, menuColumn.implicitHeight + Theme.spacingS * 2)
x: { x: {
const left = 10 if (menuRoot.isVertical) {
const right = menuWindow.width - width - 10 const edge = menuRoot.axis?.edge
const want = menuWindow.anchorPos.x - width / 2 if (edge === "left") {
return Math.max(left, Math.min(right, want)) 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: { y: {
if (menuRoot.isAtBottom) { if (menuRoot.isVertical) {
const targetY = menuWindow.anchorPos.y - height const top = 10
return Math.max(10, targetY) const bottom = menuWindow.height - height - 10
const want = menuWindow.anchorPos.y - height / 2
return Math.max(top, Math.min(bottom, want))
} else { } else {
const targetY = menuWindow.anchorPos.y if (menuRoot.isAtBottom) {
return Math.min(menuWindow.screen.height - height - 10, targetY) 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)
}
} }
} }
@@ -376,15 +498,13 @@ Rectangle {
if (!menuEntry || menuEntry.isSeparator) return; if (!menuEntry || menuEntry.isSeparator) return;
if (menuEntry.hasChildren) { if (menuEntry.hasChildren) {
console.log("Opening submenu for:", menuEntry.text);
menuRoot.showSubMenu(menuEntry); menuRoot.showSubMenu(menuEntry);
} else { } else {
if (typeof menuEntry.activate === "function") { if (typeof menuEntry.activate === "function") {
menuEntry.activate(); // preferred menuEntry.activate();
} else if (typeof menuEntry.triggered === "function") { } else if (typeof menuEntry.triggered === "function") {
menuEntry.triggered(); menuEntry.triggered();
} }
// optional: small delay to let provider flip state before closing
Qt.createQmlObject('import QtQuick; Timer { interval: 80; running: true; repeat: false; onTriggered: menuRoot.close() }', menuRoot); Qt.createQmlObject('import QtQuick; Timer { interval: 80; running: true; repeat: false; onTriggered: menuRoot.close() }', menuRoot);
} }
} }
@@ -497,13 +617,13 @@ Rectangle {
property var currentTrayMenu: null property var currentTrayMenu: null
function showForTrayItem(item, anchor, screen, atBottom) { function showForTrayItem(item, anchor, screen, atBottom, vertical, axisObj) {
if (currentTrayMenu) { if (currentTrayMenu) {
currentTrayMenu.destroy() currentTrayMenu.destroy()
} }
currentTrayMenu = trayMenuComponent.createObject(null) currentTrayMenu = trayMenuComponent.createObject(null)
if (currentTrayMenu) { if (currentTrayMenu) {
currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom) currentTrayMenu.showForTrayItem(item, anchor, screen, atBottom, vertical ?? false, axisObj)
} }
} }

View File

@@ -6,20 +6,22 @@ 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.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) 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 hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking readonly property bool isChecking: SystemUpdateService.isChecking
signal clicked() signal clicked()
width: updaterIcon.width + horizontalPadding * 2 width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2)
height: widgetHeight height: isVertical ? widgetThickness : widgetThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -30,14 +32,63 @@ 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);
} }
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 { Row {
id: updaterIcon id: updaterIcon
anchors.centerIn: parent anchors.centerIn: parent
spacing: Theme.spacingXS spacing: Theme.spacingXS
visible: !root.isVertical
DankIcon { DankIcon {
id: statusIcon id: statusIconHorizontal
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
name: { name: {
@@ -54,8 +105,8 @@ Rectangle {
} }
RotationAnimation { RotationAnimation {
id: rotationAnimation id: rotationAnimationHorizontal
target: statusIcon target: statusIconHorizontal
property: "rotation" property: "rotation"
from: 0 from: 0
to: 360 to: 360
@@ -65,7 +116,7 @@ Rectangle {
onRunningChanged: { onRunningChanged: {
if (!running) { if (!running) {
statusIcon.rotation = 0 statusIconHorizontal.rotation = 0
} }
} }
} }
@@ -91,11 +142,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, SettingsData.getPopupYPosition(barHeight), width, section, currentScreen);
} }
root.clicked(); root.clicked();
} }

View File

@@ -0,0 +1,111 @@
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
RotationAnimation on rotation {
running: VpnService.isBusy
loops: Animation.Infinite
from: 0
to: 360
duration: 900
}
}
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) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const screenY = currentScreen ? currentScreen.y : 0
const relativeY = globalPos.y - screenY
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 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(tooltipText, screenX + tooltipX, relativeY, currentScreen, isLeft, !isLeft)
} else {
tooltipLoader.item.show(tooltipText, relativeX, screenY + root.barThickness + SettingsData.dankBarSpacing + Theme.spacingXS, currentScreen, false, false)
}
}
}
}
onExited: {
if (tooltipLoader.item) {
tooltipLoader.item.hide()
}
tooltipLoader.active = false
}
}
}

View File

@@ -6,18 +6,20 @@ 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.dankBarNoBackground ? 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.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) { if (SettingsData.dankBarNoBackground) {
@@ -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, SettingsData.getPopupYPosition(barHeight), 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,8 +225,8 @@ Rectangle {
} }
} }
width: workspaceRow.implicitWidth + padding * 2 width: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
height: widgetHeight height: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { color: {
if (SettingsData.dankBarNoBackground) if (SettingsData.dankBarNoBackground)
@@ -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,36 @@ Rectangle {
} }
width: { width: {
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) { if (root.isVertical) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons); // Vertical mode: width is like horizontal height (small and fixed)
const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0); return SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6;
const baseWidth = isActive ? root.widgetHeight * 1.0 + Theme.spacingXS : root.widgetHeight * 0.8; } else {
return baseWidth + iconsWidth; // Horizontal mode - original logic
if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons);
const iconsWidth = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0);
const baseWidth = isActive ? root.widgetHeight * 1.0 + Theme.spacingXS : root.widgetHeight * 0.8;
return baseWidth + iconsWidth;
}
return isActive ? root.widgetHeight * 1.2 : root.widgetHeight * 0.8;
} }
return isActive ? root.widgetHeight * 1.2 : root.widgetHeight * 0.8;
} }
height: SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6 height: {
radius: height / 2 if (root.isVertical) {
// Vertical mode: height is like horizontal width (dynamic)
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 * 1.0 + Theme.spacingXS : root.widgetHeight * 0.8;
return baseHeight + iconsHeight;
}
return isActive ? root.widgetHeight * 1.2 : root.widgetHeight * 0.8;
} else {
// Horizontal mode - original logic
return SettingsData.showWorkspaceApps ? widgetHeight * 0.8 : widgetHeight * 0.6;
}
}
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,6 +377,14 @@ 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
@@ -372,74 +405,149 @@ 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
spacing: 4 sourceComponent: root.isVertical ? columnLayout : rowLayout
visible: loadedIcons.length > 0 }
Repeater { Component {
model: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons) id: rowLayout
delegate: Item { Row {
width: 18 spacing: 4
height: 18 visible: loadedIcons.length > 0
IconImage { Repeater {
id: appIcon model: loadedIcons.slice(0, SettingsData.maxWorkspaceIcons)
property var windowId: modelData.windowId delegate: Item {
anchors.fill: parent width: 18
source: modelData.icon height: 18
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6
visible: !modelData.isSteamApp
}
DankIcon { IconImage {
anchors.centerIn: parent id: appIcon
size: 18 property var windowId: modelData.windowId
name: "sports_esports" anchors.fill: parent
color: Theme.surfaceText source: modelData.icon
opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6 opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6
visible: modelData.isSteamApp visible: !modelData.isSteamApp
} }
MouseArea { DankIcon {
id: appMouseArea anchors.centerIn: parent
hoverEnabled: true size: 18
anchors.fill: parent name: "sports_esports"
enabled: isActive color: Theme.surfaceText
cursorShape: Qt.PointingHandCursor opacity: modelData.active ? 1.0 : appMouseArea.containsMouse ? 0.8 : 0.6
onClicked: { visible: modelData.isSteamApp
if (CompositorService.isHyprland) { }
Hyprland.dispatch(`focuswindow address:${appIcon.windowId}`)
} else if (CompositorService.isNiri) { MouseArea {
NiriService.focusWindow(appIcon.windowId) 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"
} }
} }
} }
}
}
}
Rectangle { Component {
visible: modelData.count > 1 && !isActive id: columnLayout
width: 12 Column {
height: 12 spacing: 4
radius: 6 visible: loadedIcons.length > 0
color: "black"
border.color: "white"
border.width: 1
anchors.right: parent.right
anchors.bottom: parent.bottom
z: 2
Text { 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 anchors.centerIn: parent
text: modelData.count size: 18
font.pixelSize: 8 name: "sports_esports"
color: "white" 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"
}
} }
} }
} }

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.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 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

File diff suppressed because it is too large Load Diff

View File

@@ -32,13 +32,16 @@ Variants {
property real backgroundTransparency: SettingsData.dockTransparency property real backgroundTransparency: SettingsData.dockTransparency
property bool groupByApp: SettingsData.dockGroupByApp property bool groupByApp: SettingsData.dockGroupByApp
readonly property bool isDockAtTop: SettingsData.dockPosition === SettingsData.Position.Top
readonly property bool isDankBarAtTop: !SettingsData.dankBarAtBottom
readonly property bool isDankBarVisible: SettingsData.dankBarVisible
readonly property bool needsBarSpacing: isDankBarVisible && (isDockAtTop === isDankBarAtTop)
readonly property real widgetHeight: Math.max(20, 26 + SettingsData.dankBarInnerPadding * 0.6) 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 effectiveBarHeight: Math.max(widgetHeight + SettingsData.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding))
readonly property real barSpacing: needsBarSpacing ? (SettingsData.dankBarSpacing + effectiveBarHeight + SettingsData.dankBarBottomGap) : 0 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 dockMargin: SettingsData.dockSpacing
readonly property real positionSpacing: barSpacing + SettingsData.dockBottomGap readonly property real positionSpacing: barSpacing + SettingsData.dockBottomGap
@@ -92,7 +95,7 @@ Variants {
exclusiveZone: { exclusiveZone: {
if (!SettingsData.showDock || autoHide) return -1 if (!SettingsData.showDock || autoHide) return -1
if (needsBarSpacing) return -1 if (barSpacing > 0) return -1
return px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap) return px(58 + SettingsData.dockSpacing + SettingsData.dockBottomGap)
} }

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.dankBarSpacing + 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

@@ -76,13 +76,6 @@ PanelWindow {
color: "transparent" color: "transparent"
implicitWidth: 400 implicitWidth: 400
implicitHeight: 122 implicitHeight: 122
onScreenYChanged: {
if (SettingsData.dankBarAtBottom) {
margins.bottom = Theme.barHeight - 4 + SettingsData.dankBarSpacing + 4 + screenY
} else {
margins.top = Theme.barHeight - 4 + SettingsData.dankBarSpacing + 4 + screenY
}
}
onHasValidDataChanged: { onHasValidDataChanged: {
if (!hasValidData && !exiting && !_isDestroying) { if (!hasValidData && !exiting && !_isDestroying) {
forceExit() forceExit()
@@ -114,16 +107,94 @@ PanelWindow {
} }
} }
anchors { property bool isTopCenter: SettingsData.notificationPopupPosition === -1
top: !SettingsData.dankBarAtBottom
bottom: SettingsData.dankBarAtBottom anchors.top: isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left
right: true 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: SettingsData.dankBarAtBottom ? 0 : (Theme.barHeight - 4 + SettingsData.dankBarSpacing + 4) top: getTopMargin()
bottom: SettingsData.dankBarAtBottom ? (Theme.barHeight - 4 + SettingsData.dankBarSpacing + 4) : 0 bottom: getBottomMargin()
right: 12 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 {
@@ -131,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 {
@@ -463,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
} }
} }
@@ -471,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()
}
} }
} }
} }
@@ -491,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,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.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 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

@@ -623,11 +623,24 @@ Item {
DankButtonGroup { DankButtonGroup {
id: positionButtonGroup id: positionButtonGroup
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
model: ["Top", "Bottom"] model: ["Top", "Bottom", "Left", "Right"]
currentIndex: SettingsData.dankBarAtBottom ? 1 : 0 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) => { onSelectionChanged: (index, selected) => {
if (selected) { if (selected) {
SettingsData.setDankBarAtBottom(index === 1) 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
}
} }
} }
} }
@@ -876,7 +889,7 @@ Item {
spacing: Theme.spacingS spacing: Theme.spacingS
StyledText { StyledText {
text: "Height to Edge 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
@@ -1086,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
@@ -1100,7 +1113,7 @@ 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: dankBarTab.baseWidgetDefinitions allWidgets: dankBarTab.baseWidgetDefinitions
@@ -1230,7 +1243,7 @@ Item {
} }
} }
// 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
@@ -1244,7 +1257,7 @@ 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: dankBarTab.baseWidgetDefinitions allWidgets: dankBarTab.baseWidgetDefinitions

View File

@@ -985,7 +985,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)
} }
} }
@@ -1331,17 +1332,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
@@ -1352,14 +1353,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
@@ -1367,14 +1368,48 @@ 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()
}
} }
} }
} }
@@ -1611,6 +1646,54 @@ Item {
} }
} }
} }
// 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
} }
} }

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.dankBarInnerPadding + 4, Theme.barHeight - 4 - (8 - SettingsData.dankBarInnerPadding)) + SettingsData.dankBarSpacing + SettingsData.dankBarBottomGap - 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

@@ -412,13 +412,13 @@ Singleton {
} }
function doScreenTransition() { function doScreenTransition() {
return send({ send({
"Action": { "Action": {
"DoScreenTransition": { "DoScreenTransition": {
"delay_ms": 100, "delay_ms": 0,
}
} }
}) }
})
} }
function switchToWorkspace(workspaceIndex) { function switchToWorkspace(workspaceIndex) {

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)
root.notifications.push(wrapper) if (!isTransient) {
_trimStored() root.notifications.push(wrapper)
_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) {
w.popup = false if (w) {
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,7 +638,9 @@ 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) {
currentMessageIds.add(notif.notification.id) if (notif && notif.notification) {
currentMessageIds.add(notif.notification.id)
}
} }
} }
let newExpandedGroups = {} let newExpandedGroups = {}

View File

@@ -13,6 +13,8 @@ StyledRect {
property int buttonSize: 32 property int buttonSize: 32
signal clicked signal clicked
signal entered
signal exited
width: buttonSize width: buttonSize
height: buttonSize height: buttonSize
@@ -30,5 +32,7 @@ StyledRect {
stateColor: Theme.primary stateColor: Theme.primary
cornerRadius: root.radius cornerRadius: root.radius
onClicked: root.clicked() onClicked: root.clicked()
onEntered: root.entered()
onExited: root.exited()
} }
} }

View File

@@ -19,9 +19,16 @@ Flow {
property int textSize: Theme.fontSizeMedium property int textSize: Theme.fontSizeMedium
signal selectionChanged(int index, bool selected) signal selectionChanged(int index, bool selected)
signal animationCompleted()
spacing: Theme.spacingXS spacing: Theme.spacingXS
Timer {
id: animationTimer
interval: Theme.shortDuration
onTriggered: root.animationCompleted()
}
function isSelected(index) { function isSelected(index) {
if (multiSelect) { if (multiSelect) {
return repeater.itemAt(index)?.selected || false return repeater.itemAt(index)?.selected || false
@@ -43,6 +50,7 @@ Flow {
currentSelection = newSelection currentSelection = newSelection
selectionChanged(index, !isCurrentlySelected) selectionChanged(index, !isCurrentlySelected)
animationTimer.restart()
} else { } else {
const oldIndex = currentIndex const oldIndex = currentIndex
currentIndex = index currentIndex = index
@@ -50,6 +58,7 @@ Flow {
if (oldIndex !== index && oldIndex >= 0) { if (oldIndex !== index && oldIndex >= 0) {
selectionChanged(oldIndex, false) selectionChanged(oldIndex, false)
} }
animationTimer.restart()
} }
} }

View File

@@ -124,6 +124,7 @@ Rectangle {
} }
size: 18 size: 18
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
visible: name !== "" && root.width > 60 visible: name !== "" && root.width > 60
} }
@@ -131,6 +132,7 @@ Rectangle {
text: root.currentValue text: root.currentValue
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
width: root.width <= 60 ? dropdown.width - expandIcon.width - Theme.spacingS * 2 : dropdown.width - contentRow.x - expandIcon.width - Theme.spacingM - Theme.spacingS width: root.width <= 60 ? dropdown.width - expandIcon.width - Theme.spacingS * 2 : dropdown.width - contentRow.x - expandIcon.width - Theme.spacingM - Theme.spacingS
elide: root.width <= 60 ? Text.ElideNone : Text.ElideRight elide: root.width <= 60 ? Text.ElideNone : Text.ElideRight
horizontalAlignment: root.width <= 60 ? Text.AlignHCenter : Text.AlignLeft horizontalAlignment: root.width <= 60 ? Text.AlignHCenter : Text.AlignLeft

View File

@@ -12,6 +12,8 @@ StyledText {
property int grade: Theme.isLightMode ? 0 : -25 property int grade: Theme.isLightMode ? 0 : -25
property int weight: filled ? 500 : 400 property int weight: filled ? 500 : 400
signal rotationCompleted()
font.family: "Material Symbols Rounded" font.family: "Material Symbols Rounded"
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
font.weight: weight font.weight: weight
@@ -40,4 +42,15 @@ StyledText {
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
Timer {
id: rotationTimer
interval: 16
repeat: false
onTriggered: icon.rotationCompleted()
}
onRotationChanged: {
rotationTimer.restart()
}
} }

View File

@@ -244,8 +244,8 @@ Rectangle {
width: 36 width: 36
height: 36 height: 36
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: iconMouseArea.containsMouse ? Theme.primaryHover : "transparent" color: iconMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.primaryHover, 0)
border.color: root.currentIcon === modelData ? Theme.primary : "transparent" border.color: root.currentIcon === modelData ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.width: 2 border.width: 2
DankIcon { DankIcon {

View File

@@ -16,6 +16,7 @@ PanelWindow {
property real triggerX: 0 property real triggerX: 0
property real triggerY: 0 property real triggerY: 0
property real triggerWidth: 40 property real triggerWidth: 40
property string triggerSection: ""
property string positioning: "center" property string positioning: "center"
property int animationDuration: Theme.mediumDuration property int animationDuration: Theme.mediumDuration
property var animationEasing: Theme.emphasizedEasing property var animationEasing: Theme.emphasizedEasing
@@ -89,25 +90,29 @@ PanelWindow {
Item { Item {
id: contentContainer id: contentContainer
layer.enabled: true
readonly property real screenWidth: root.screen ? root.screen.width : 1920 readonly property real screenWidth: root.screen ? root.screen.width : 1920
readonly property real screenHeight: root.screen ? root.screen.height : 1080 readonly property real screenHeight: root.screen ? root.screen.height : 1080
readonly property real gothOffset: SettingsData.dankBarGothCornersEnabled ? Theme.cornerRadius : 0
readonly property real calculatedX: { readonly property real calculatedX: {
if (positioning === "center") { if (SettingsData.dankBarPosition === SettingsData.Position.Left) {
var centerX = triggerX + (triggerWidth / 2) - (popupWidth / 2) return triggerY
return Math.max(Theme.spacingM, Math.min(screenWidth - popupWidth - Theme.spacingM, centerX)) } else if (SettingsData.dankBarPosition === SettingsData.Position.Right) {
} else if (positioning === "left") { return screenWidth - triggerY - popupWidth
return Math.max(Theme.spacingM, triggerX) } else {
} else if (positioning === "right") { const centerX = triggerX + (triggerWidth / 2) - (popupWidth / 2)
return Math.min(screenWidth - popupWidth - Theme.spacingM, triggerX + triggerWidth - popupWidth) return Math.max(Theme.popupDistance, Math.min(screenWidth - popupWidth - Theme.popupDistance, centerX))
} }
return triggerX
} }
readonly property real calculatedY: { readonly property real calculatedY: {
if (SettingsData.dankBarAtBottom) { if (SettingsData.dankBarPosition === SettingsData.Position.Left || SettingsData.dankBarPosition === SettingsData.Position.Right) {
return Math.max(Theme.spacingM, Math.min(screenHeight - popupHeight - Theme.spacingM, screenHeight - triggerY - popupHeight - SettingsData.dankBarSpacing - 10)) const centerY = triggerX + (triggerWidth / 2) - (popupHeight / 2)
return Math.max(Theme.popupDistance, Math.min(screenHeight - popupHeight - Theme.popupDistance, centerY))
} else if (SettingsData.dankBarPosition === SettingsData.Position.Bottom) {
return Math.max(Theme.popupDistance, Math.min(screenHeight - popupHeight - Theme.popupDistance, screenHeight - triggerY - popupHeight + Theme.popupDistance))
} else { } else {
return Math.max(0, Math.min(screenHeight - popupHeight - Theme.spacingM, triggerY + SettingsData.dankBarSpacing + 10)) return Math.max(Theme.popupDistance, Math.min(screenHeight - popupHeight - Theme.popupDistance, triggerY + Theme.popupDistance))
} }
} }

View File

@@ -57,6 +57,7 @@ PanelWindow {
StyledRect { StyledRect {
id: contentRect id: contentRect
layer.enabled: true
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: parent.bottom anchors.bottom: parent.bottom

View File

@@ -21,7 +21,6 @@ Item {
property color thumbOutlineColor: Theme.surfaceContainer property color thumbOutlineColor: Theme.surfaceContainer
property color trackColor: enabled ? Theme.outline : Theme.outline property color trackColor: enabled ? Theme.outline : Theme.outline
function withAlpha(c, a) { return Qt.rgba(c.r, c.g, c.b, a) }
signal sliderValueChanged(int newValue) signal sliderValueChanged(int newValue)
signal sliderDragFinished(int finalValue) signal sliderDragFinished(int finalValue)
@@ -73,7 +72,7 @@ Item {
const center = (travel * ratio) + sliderHandle.width / 2 const center = (travel * ratio) + sliderHandle.width / 2
return Math.max(0, Math.min(sliderTrack.width, center)) return Math.max(0, Math.min(sliderTrack.width, center))
} }
color: slider.enabled ? Theme.primary : withAlpha(Theme.onSurface, 0.12) color: slider.enabled ? Theme.primary : Theme.withAlpha(Theme.onSurface, 0.12)
} }
@@ -91,7 +90,7 @@ Item {
return Math.max(0, Math.min(travel, travel * ratio)) return Math.max(0, Math.min(travel, travel * ratio))
} }
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: slider.enabled ? Theme.primary : withAlpha(Theme.onSurface, 0.12) color: slider.enabled ? Theme.primary : Theme.withAlpha(Theme.onSurface, 0.12)
border.width: 3 border.width: 3
border.color: slider.thumbOutlineColor border.color: slider.thumbOutlineColor

View File

@@ -15,6 +15,7 @@ Item {
signal clicked signal clicked
signal toggled(bool checked) signal toggled(bool checked)
signal toggleCompleted(bool checked)
readonly property bool showText: text && !hideText readonly property bool showText: text && !hideText
@@ -113,10 +114,17 @@ Item {
x: (checked && enabled) ? toggleTrack.edgeRight : toggleTrack.edgeLeft x: (checked && enabled) ? toggleTrack.edgeRight : toggleTrack.edgeLeft
Behavior on x { Behavior on x {
NumberAnimation { SequentialAnimation {
duration: Appearance.anim.durations.normal NumberAnimation {
easing.type: Easing.BezierSpline duration: Appearance.anim.durations.normal
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
}
ScriptAction {
script: {
toggle.toggleCompleted(toggle.checked)
}
}
} }
} }

View File

@@ -6,21 +6,30 @@ import qs.Common
PanelWindow { PanelWindow {
id: root id: root
property string tooltipText: "" property string text: ""
property real targetX: 0 property real targetX: 0
property real targetY: 0 property real targetY: 0
property var targetScreen: null property var targetScreen: null
property bool alignLeft: false
property bool alignRight: false
function showTooltip(text, x, y, screen) { function show(text, x, y, screen, leftAlign, rightAlign) {
tooltipText = text; root.text = text;
targetScreen = screen; if (screen) {
const screenX = screen ? screen.x : 0; targetScreen = screen;
targetX = x - screenX; const screenX = screen.x || 0;
targetX = x - screenX;
} else {
targetScreen = null;
targetX = x;
}
targetY = y; targetY = y;
alignLeft = leftAlign ?? false;
alignRight = rightAlign ?? false;
visible = true; visible = true;
} }
function hideTooltip() { function hide() {
visible = false; visible = false;
} }
@@ -38,8 +47,15 @@ PanelWindow {
} }
margins { margins {
left: Math.round(targetX - implicitWidth / 2) left: {
top: Math.round(targetY) if (alignLeft) return Math.round(targetX)
if (alignRight) return Math.round(targetX - implicitWidth)
return Math.round(targetX - implicitWidth / 2)
}
top: {
if (alignLeft || alignRight) return Math.round(targetY - implicitHeight / 2)
return Math.round(targetY)
}
} }
Rectangle { Rectangle {
@@ -53,15 +69,13 @@ PanelWindow {
id: textContent id: textContent
anchors.centerIn: parent anchors.centerIn: parent
text: root.tooltipText text: root.text
font.pixelSize: Theme.fontSizeSmall font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText color: Theme.surfaceText
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
maximumLineCount: 1 maximumLineCount: 1
elide: Text.ElideRight elide: Text.ElideRight
width: parent.width - Theme.spacingM * 2 width: Math.min(implicitWidth, 300 - Theme.spacingM * 2)
} }
} }
}
}

View File

@@ -11,14 +11,6 @@ Rectangle {
"easing.bezierCurve": Appearance.anim.curves.standard "easing.bezierCurve": Appearance.anim.curves.standard
} }
Behavior on color {
ColorAnimation {
duration: standardAnimation.duration
easing.type: standardAnimation["easing.type"]
easing.bezierCurve: standardAnimation["easing.bezierCurve"]
}
}
Behavior on radius { Behavior on radius {
NumberAnimation { NumberAnimation {
duration: standardAnimation.duration duration: standardAnimation.duration

View File

@@ -33,14 +33,6 @@ Text {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
antialiasing: true antialiasing: true
Behavior on color {
ColorAnimation {
duration: standardAnimation.duration
easing.type: standardAnimation["easing.type"]
easing.bezierCurve: standardAnimation["easing.bezierCurve"]
}
}
Behavior on opacity { Behavior on opacity {
NumberAnimation { NumberAnimation {
duration: standardAnimation.duration duration: standardAnimation.duration

View File

@@ -25,6 +25,7 @@ import qs.Modules.OSD
import qs.Modules.ProcessList import qs.Modules.ProcessList
import qs.Modules.Settings import qs.Modules.Settings
import qs.Modules.DankBar import qs.Modules.DankBar
import qs.Modules.DankBar.Popouts
import qs.Services import qs.Services
ShellRoot { ShellRoot {
@@ -48,21 +49,19 @@ ShellRoot {
Loader { Loader {
id: dankBarLoader id: dankBarLoader
active: true
asynchronous: false asynchronous: false
property var currentPosition: SettingsData.dankBarAtBottom property var currentPosition: SettingsData.dankBarPosition
sourceComponent: DankBar { sourceComponent: DankBar {
onColorPickerRequested: colorPickerModal.show() onColorPickerRequested: colorPickerModal.show()
} }
onCurrentPositionChanged: { onCurrentPositionChanged: {
console.log("DEBUG: DankBar position changed to:", currentPosition, "- recreating bar") const component = sourceComponent
const comp = sourceComponent
sourceComponent = null sourceComponent = null
Qt.callLater(() => { Qt.callLater(() => {
sourceComponent = comp sourceComponent = component
}) })
} }
} }
@@ -140,8 +139,11 @@ ShellRoot {
active: false active: false
property var modalRef: colorPickerModal
ControlCenterPopout { ControlCenterPopout {
id: controlCenterPopout id: controlCenterPopout
colorPickerModal: controlCenterLoader.modalRef
onPowerActionRequested: (action, title, message) => { onPowerActionRequested: (action, title, message) => {
powerConfirmModalLoader.active = true powerConfirmModalLoader.active = true