From 951136bc4c1fb77f1407e36aa2ff611769fe51b3 Mon Sep 17 00:00:00 2001 From: bbedward Date: Wed, 22 Oct 2025 00:12:41 -0400 Subject: [PATCH] dankbar: enhance widget click targets - Fitt's law stuff, whole height on horiz, whole width in vertical - Probably missed stuff or breaks stuff, pretty big refactor --- DMSShellIPC.qml | 11 + Modules/DankBar/DankBar.qml | 63 +- Modules/DankBar/Widgets/Battery.qml | 160 ++--- Modules/DankBar/Widgets/ClipboardButton.qml | 54 +- Modules/DankBar/Widgets/Clock.qml | 458 ++++++------- Modules/DankBar/Widgets/ColorPicker.qml | 52 +- .../DankBar/Widgets/ControlCenterButton.qml | 411 ++++++----- Modules/DankBar/Widgets/CpuMonitor.qml | 232 +++---- Modules/DankBar/Widgets/CpuTemperature.qml | 233 +++---- Modules/DankBar/Widgets/DiskUsage.qml | 249 ++++--- Modules/DankBar/Widgets/FocusedApp.qml | 265 ++++---- Modules/DankBar/Widgets/GpuTemperature.qml | 237 +++---- Modules/DankBar/Widgets/IdleInhibitor.qml | 45 +- .../DankBar/Widgets/KeyboardLayoutName.qml | 114 ++-- Modules/DankBar/Widgets/LauncherButton.qml | 178 ++--- Modules/DankBar/Widgets/Media.qml | 636 ++++++++---------- Modules/DankBar/Widgets/NetworkMonitor.qml | 295 ++++---- Modules/DankBar/Widgets/NotepadButton.qml | 78 +-- .../Widgets/NotificationCenterButton.qml | 83 +-- Modules/DankBar/Widgets/PrivacyIndicator.qml | 312 +++++---- Modules/DankBar/Widgets/RamMonitor.qml | 233 +++---- Modules/DankBar/Widgets/RunningApps.qml | 61 +- Modules/DankBar/Widgets/SystemTrayBar.qml | 284 ++++---- Modules/DankBar/Widgets/SystemUpdate.qml | 224 +++--- Modules/DankBar/Widgets/Vpn.qml | 57 +- Modules/DankBar/Widgets/Weather.qml | 149 ++-- Modules/DankBar/Widgets/WorkspaceSwitcher.qml | 206 +++--- Modules/Plugins/BasePill.qml | 50 +- 28 files changed, 2507 insertions(+), 2923 deletions(-) diff --git a/DMSShellIPC.qml b/DMSShellIPC.qml index abc84747..33554cb4 100644 --- a/DMSShellIPC.qml +++ b/DMSShellIPC.qml @@ -374,4 +374,15 @@ Item { target: "hypr" } + + IpcHandler { + function wallpaper(): string { + if (root.dankBarLoader.item && root.dankBarLoader.item.triggerWallpaperBrowserOnFocusedScreen()) { + return "SUCCESS: Toggled wallpaper browser" + } + return "ERROR: Failed to toggle wallpaper browser" + } + + target: "dankdash" + } } diff --git a/Modules/DankBar/DankBar.qml b/Modules/DankBar/DankBar.qml index 7d4ce087..ed3f03a9 100644 --- a/Modules/DankBar/DankBar.qml +++ b/Modules/DankBar/DankBar.qml @@ -546,9 +546,9 @@ Item { anchors.fill: parent anchors.leftMargin: !barWindow.isVertical ? Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) : SettingsData.dankBarInnerPadding / 2 anchors.rightMargin: !barWindow.isVertical ? Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) : SettingsData.dankBarInnerPadding / 2 - anchors.topMargin: !barWindow.isVertical ? SettingsData.dankBarInnerPadding / 2 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) - anchors.bottomMargin: !barWindow.isVertical ? SettingsData.dankBarInnerPadding / 2 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) - clip: true + anchors.topMargin: !barWindow.isVertical ? 0 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) + anchors.bottomMargin: !barWindow.isVertical ? 0 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) + clip: false property int componentMapRevision: 0 @@ -801,6 +801,7 @@ Item { ClipboardButton { widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) parentScreen: barWindow.screen onClicked: { @@ -817,7 +818,7 @@ Item { widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness section: topBarContent.getWidgetSection(parent) - popupTarget: appDrawerLoader.item + popoutTarget: appDrawerLoader.item parentScreen: barWindow.screen hyprlandOverviewLoader: root.hyprlandOverviewLoader onClicked: { @@ -831,8 +832,11 @@ Item { id: workspaceSwitcherComponent WorkspaceSwitcher { + axis: barWindow.axis screenName: barWindow.screenName widgetHeight: barWindow.widgetThickness + barThickness: barWindow.effectiveBarThickness + parentScreen: barWindow.screen hyprlandOverviewLoader: root.hyprlandOverviewLoader } } @@ -841,8 +845,10 @@ Item { id: focusedWindowComponent FocusedApp { + axis: barWindow.axis availableWidth: topBarContent.leftToMediaGap widgetThickness: barWindow.widgetThickness + barThickness: barWindow.effectiveBarThickness parentScreen: barWindow.screen } } @@ -862,11 +868,12 @@ Item { id: clockComponent Clock { + axis: barWindow.axis compactMode: topBarContent.overlapping barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness section: topBarContent.getWidgetSection(parent) || "center" - popupTarget: { + popoutTarget: { dankDashPopoutLoader.active = true return dankDashPopoutLoader.item } @@ -896,11 +903,12 @@ Item { id: mediaComponent Media { + axis: barWindow.axis compactMode: topBarContent.spacingTight || topBarContent.overlapping barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness section: topBarContent.getWidgetSection(parent) || "center" - popupTarget: { + popoutTarget: { dankDashPopoutLoader.active = true return dankDashPopoutLoader.item } @@ -919,10 +927,11 @@ Item { id: weatherComponent Weather { + axis: barWindow.axis barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness section: topBarContent.getWidgetSection(parent) || "center" - popupTarget: { + popoutTarget: { dankDashPopoutLoader.active = true return dankDashPopoutLoader.item } @@ -965,8 +974,9 @@ Item { CpuMonitor { barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { processListPopoutLoader.active = true return processListPopoutLoader.item } @@ -985,8 +995,9 @@ Item { RamMonitor { barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { processListPopoutLoader.active = true return processListPopoutLoader.item } @@ -1015,8 +1026,9 @@ Item { CpuTemperature { barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { processListPopoutLoader.active = true return processListPopoutLoader.item } @@ -1035,8 +1047,9 @@ Item { GpuTemperature { barThickness: barWindow.effectiveBarThickness widgetThickness: barWindow.widgetThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { processListPopoutLoader.active = true return processListPopoutLoader.item } @@ -1063,8 +1076,9 @@ Item { isActive: notificationCenterLoader.item ? notificationCenterLoader.item.shouldBeVisible : false widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { notificationCenterLoader.active = true return notificationCenterLoader.item } @@ -1083,8 +1097,9 @@ Item { batteryPopupVisible: batteryPopoutLoader.item ? batteryPopoutLoader.item.shouldBeVisible : false widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { batteryPopoutLoader.active = true return batteryPopoutLoader.item } @@ -1102,8 +1117,9 @@ Item { Vpn { widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { vpnPopoutLoader.active = true return vpnPopoutLoader.item } @@ -1122,8 +1138,9 @@ Item { isActive: controlCenterLoader.item ? controlCenterLoader.item.shouldBeVisible : false widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { controlCenterLoader.active = true return controlCenterLoader.item } @@ -1217,9 +1234,9 @@ Item { id: notepadButtonComponent NotepadButton { - isVertical: barWindow.isVertical widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" parentScreen: barWindow.screen } @@ -1246,8 +1263,9 @@ Item { isActive: systemUpdateLoader.item ? systemUpdateLoader.item.shouldBeVisible : false widgetThickness: barWindow.widgetThickness barThickness: barWindow.effectiveBarThickness + axis: barWindow.axis section: topBarContent.getWidgetSection(parent) || "right" - popupTarget: { + popoutTarget: { systemUpdateLoader.active = true return systemUpdateLoader.item } @@ -1265,15 +1283,4 @@ Item { } } } - - IpcHandler { - target: "dankdash" - - function wallpaper(): string { - if (root.triggerWallpaperBrowserOnFocusedScreen()) { - return "SUCCESS: Toggled wallpaper browser" - } - return "ERROR: Failed to toggle wallpaper browser" - } - } } diff --git a/Modules/DankBar/Widgets/Battery.qml b/Modules/DankBar/Widgets/Battery.qml index 472fc49c..5239ddf0 100644 --- a/Modules/DankBar/Widgets/Battery.qml +++ b/Modules/DankBar/Widgets/Battery.qml @@ -1,124 +1,112 @@ import QtQuick import Quickshell.Services.UPower import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { 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)) + property var popoutTarget: null 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 + content: Component { + Item { + implicitWidth: battery.isVerticalOrientation ? (battery.widgetThickness - battery.horizontalPadding * 2) : batteryContent.implicitWidth + implicitHeight: battery.isVerticalOrientation ? batteryColumn.implicitHeight : (battery.widgetThickness - battery.horizontalPadding * 2) - DankIcon { - name: BatteryService.getBatteryIcon() - size: Theme.barIconSize(barThickness) - color: { - if (!BatteryService.batteryAvailable) { - return Theme.surfaceText + Column { + id: batteryColumn + visible: battery.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: BatteryService.getBatteryIcon() + size: Theme.barIconSize(battery.barThickness) + 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 } - if (BatteryService.isLowBattery && !BatteryService.isCharging) { - return Theme.error + StyledText { + text: BatteryService.batteryLevel.toString() + font.pixelSize: Theme.barTextSize(battery.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + visible: BatteryService.batteryAvailable } - - if (BatteryService.isCharging || BatteryService.isPluggedIn) { - return Theme.primary - } - - return Theme.surfaceText } - anchors.horizontalCenter: parent.horizontalCenter - } - StyledText { - text: BatteryService.batteryLevel.toString() - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - visible: BatteryService.batteryAvailable - } - } + Row { + id: batteryContent + visible: !battery.isVerticalOrientation + anchors.centerIn: parent + spacing: SettingsData.dankBarNoBackground ? 1 : 2 - Row { - id: batteryContent - visible: !battery.isVertical - anchors.centerIn: parent - spacing: SettingsData.dankBarNoBackground ? 1 : 2 + DankIcon { + name: BatteryService.getBatteryIcon() + size: Theme.barIconSize(battery.barThickness, -4) + color: { + if (!BatteryService.batteryAvailable) { + return Theme.surfaceText; + } - DankIcon { - name: BatteryService.getBatteryIcon() - size: Theme.barIconSize(barThickness, -4) - 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 } - if (BatteryService.isLowBattery && !BatteryService.isCharging) { - return Theme.error; + StyledText { + text: `${BatteryService.batteryLevel}%` + font.pixelSize: Theme.barTextSize(battery.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + visible: BatteryService.batteryAvailable } - - if (BatteryService.isCharging || BatteryService.isPluggedIn) { - return Theme.primary; - } - - return Theme.surfaceText; } - anchors.verticalCenter: parent.verticalCenter } - - StyledText { - text: `${BatteryService.batteryLevel}%` - font.pixelSize: Theme.barTextSize(barThickness) - 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 + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = battery.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, battery.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } toggleBatteryPopup(); } diff --git a/Modules/DankBar/Widgets/ClipboardButton.qml b/Modules/DankBar/Widgets/ClipboardButton.qml index f2e0c1cd..4e737c76 100644 --- a/Modules/DankBar/Widgets/ClipboardButton.qml +++ b/Modules/DankBar/Widgets/ClipboardButton.qml @@ -1,57 +1,25 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Widgets -Item { +BasePill { 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() + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 - 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" + DankIcon { + anchors.centerIn: parent + name: "content_paste" + size: Theme.barIconSize(root.barThickness) + color: Theme.surfaceText } - - 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: Theme.barIconSize(barThickness) - color: Theme.surfaceText } } } \ No newline at end of file diff --git a/Modules/DankBar/Widgets/Clock.qml b/Modules/DankBar/Widgets/Clock.qml index 281d1f49..1b966d82 100644 --- a/Modules/DankBar/Widgets/Clock.qml +++ b/Modules/DankBar/Widgets/Clock.qml @@ -1,270 +1,254 @@ import QtQuick import Quickshell import qs.Common +import qs.Modules.Plugins import qs.Widgets -Rectangle { +BasePill { 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.barTextSize(barThickness) - color: Theme.surfaceText - 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.barTextSize(barThickness) - color: Theme.surfaceText - 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.barTextSize(barThickness) - color: Theme.surfaceText - font.weight: Font.Normal - width: 9 - horizontalAlignment: Text.AlignHCenter - } - - StyledText { - text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1) - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - font.weight: Font.Normal - width: 9 - horizontalAlignment: Text.AlignHCenter - } - } - - Row { - visible: SettingsData.showSeconds - spacing: 0 - anchors.horizontalCenter: parent.horizontalCenter - - StyledText { - text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(0) - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - font.weight: Font.Normal - width: 9 - horizontalAlignment: Text.AlignHCenter - } - - StyledText { - text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(1) - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - font.weight: Font.Normal - width: 9 - horizontalAlignment: Text.AlignHCenter - } - } - + content: Component { Item { - width: 12 - height: Theme.spacingM - anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth + implicitHeight: root.isVerticalOrientation ? clockColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) - Rectangle { - width: 12 - height: 1 - color: Theme.outlineButton + Column { + id: clockColumn + visible: root.isVerticalOrientation anchors.centerIn: parent - } - } + spacing: -2 - Row { - spacing: 0 - anchors.horizontalCenter: parent.horizontalCenter + 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.barTextSize(barThickness) - color: Theme.primary - font.weight: Font.Light - width: 9 - horizontalAlignment: Text.AlignHCenter - } + 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.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: 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?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0') - return value.charAt(1) - } - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.primary - font.weight: 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.barTextSize(barThickness) - color: Theme.primary - font.weight: 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?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0') - return value.charAt(1) - } - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.primary - font.weight: Font.Light - width: 9 - horizontalAlignment: Text.AlignHCenter - } - } - } - - Row { - id: clockRow - - visible: !root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingS - - StyledText { - text: { - return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat()) - } - font.pixelSize: Theme.barTextSize(barThickness) - 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) + 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.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: Font.Normal + width: 9 + horizontalAlignment: Text.AlignHCenter + } } - return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d") - } - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - visible: !SettingsData.clockCompactMode - } - } + Row { + spacing: 0 + anchors.horizontalCenter: parent.horizontalCenter - SystemClock { - id: systemClock - precision: SystemClock.Seconds + StyledText { + text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0) + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: Font.Normal + width: 9 + horizontalAlignment: Text.AlignHCenter + } + + StyledText { + text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(1) + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: Font.Normal + width: 9 + horizontalAlignment: Text.AlignHCenter + } + } + + Row { + visible: SettingsData.showSeconds + spacing: 0 + anchors.horizontalCenter: parent.horizontalCenter + + StyledText { + text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(0) + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: Font.Normal + width: 9 + horizontalAlignment: Text.AlignHCenter + } + + StyledText { + text: String(systemClock?.date?.getSeconds()).padStart(2, '0').charAt(1) + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + 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.barTextSize(root.barThickness) + color: Theme.primary + font.weight: 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.barTextSize(root.barThickness) + color: Theme.primary + font.weight: 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.barTextSize(root.barThickness) + color: Theme.primary + font.weight: 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?.getMonth() + 1).padStart(2, '0') : String(systemClock?.date?.getDate()).padStart(2, '0') + return value.charAt(1) + } + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.primary + font.weight: Font.Light + width: 9 + horizontalAlignment: Text.AlignHCenter + } + } + } + + Row { + id: clockRow + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingS + + StyledText { + text: { + return systemClock?.date?.toLocaleTimeString(Qt.locale(), SettingsData.getEffectiveTimeFormat()) + } + font.pixelSize: Theme.barTextSize(root.barThickness) + 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.barTextSize(root.barThickness) + 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) { + if (root.popoutTarget && root.popoutTarget.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) + const currentScreen = root.parentScreen || Screen + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, width) + root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen) } root.clockClicked() } } - } diff --git a/Modules/DankBar/Widgets/ColorPicker.qml b/Modules/DankBar/Widgets/ColorPicker.qml index 579014a5..06317bfc 100644 --- a/Modules/DankBar/Widgets/ColorPicker.qml +++ b/Modules/DankBar/Widgets/ColorPicker.qml @@ -1,54 +1,36 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool isActive: false - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - signal clicked() + signal colorPickerRequested() - width: isVertical ? widgetThickness : (colorPickerIcon.width + horizontalPadding * 2) - height: isVertical ? (colorPickerIcon.height + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 + + DankIcon { + anchors.centerIn: parent + name: "palette" + size: Theme.barIconSize(root.barThickness, -4) + color: root.isActive ? Theme.primary : Theme.surfaceText + } } - - const baseColor = colorPickerArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - - DankIcon { - id: colorPickerIcon - - anchors.centerIn: parent - name: "palette" - size: Theme.barIconSize(barThickness, -4) - color: colorPickerArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText } MouseArea { - id: colorPickerArea - + z: 1 anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onPressed: { - root.colorPickerRequested(); + root.colorPickerRequested() } } - - signal colorPickerRequested() - -} \ No newline at end of file +} diff --git a/Modules/DankBar/Widgets/ControlCenterButton.qml b/Modules/DankBar/Widgets/ControlCenterButton.qml index 3725401f..86b637a6 100644 --- a/Modules/DankBar/Widgets/ControlCenterButton.qml +++ b/Modules/DankBar/Widgets/ControlCenterButton.qml @@ -1,266 +1,245 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool isActive: false - property string section: "right" - property var popupTarget: null - property var parentScreen: null + property var popoutTarget: null property var widgetData: null property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon - 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() + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : controlIndicators.implicitWidth + implicitHeight: root.isVerticalOrientation ? controlColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) - width: isVertical ? widgetThickness : (controlIndicators.implicitWidth + horizontalPadding * 2) - height: isVertical ? (controlColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } + Column { + id: controlColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingXS - const baseColor = controlCenterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } + DankIcon { + name: { + if (NetworkService.wifiToggling) { + return "sync" + } - Column { - id: controlColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingXS + if (NetworkService.networkStatus === "ethernet") { + return "lan" + } - DankIcon { - name: { - if (NetworkService.wifiToggling) { - return "sync" + return NetworkService.wifiSignalIcon + } + size: Theme.barIconSize(root.barThickness) + color: { + if (NetworkService.wifiToggling) { + return Theme.primary + } + + return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton + } + anchors.horizontalCenter: parent.horizontalCenter + visible: root.showNetworkIcon && NetworkService.networkAvailable } - if (NetworkService.networkStatus === "ethernet") { - return "lan" + DankIcon { + name: "bluetooth" + size: Theme.barIconSize(root.barThickness) + color: BluetoothService.connected ? Theme.primary : Theme.outlineButton + anchors.horizontalCenter: parent.horizontalCenter + visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled } - return NetworkService.wifiSignalIcon - } - size: Theme.barIconSize(barThickness) - color: { - if (NetworkService.wifiToggling) { - return Theme.primary - } + Rectangle { + width: audioIconV.implicitWidth + 4 + height: audioIconV.implicitHeight + 4 + color: "transparent" + anchors.horizontalCenter: parent.horizontalCenter + visible: root.showAudioIcon - return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton - } - anchors.horizontalCenter: parent.horizontalCenter - visible: root.showNetworkIcon && NetworkService.networkAvailable - } + DankIcon { + id: audioIconV - DankIcon { - name: "bluetooth" - size: Theme.barIconSize(barThickness) - color: BluetoothService.connected ? 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 { + 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.barIconSize(root.barThickness) + color: Theme.surfaceText + anchors.centerIn: parent } - return "volume_up" - } - size: Theme.barIconSize(barThickness) - 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 - } - wheelEvent.accepted = true - } - } - } - - DankIcon { - name: "settings" - size: Theme.barIconSize(barThickness) - color: controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon - } - } - - Row { - id: controlIndicators - visible: !root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingXS - - DankIcon { - id: networkIcon - - name: { - if (NetworkService.wifiToggling) { - return "sync"; - } - - if (NetworkService.networkStatus === "ethernet") { - return "lan"; - } - - return NetworkService.wifiSignalIcon; - } - size: Theme.barIconSize(barThickness) - color: { - if (NetworkService.wifiToggling) { - return Theme.primary; - } - - return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton; - } - anchors.verticalCenter: parent.verticalCenter - visible: root.showNetworkIcon && NetworkService.networkAvailable - - - } - - DankIcon { - id: bluetoothIcon - - name: "bluetooth" - size: Theme.barIconSize(barThickness) - color: BluetoothService.connected ? Theme.primary : Theme.outlineButton - anchors.verticalCenter: parent.verticalCenter - visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled - } - - Rectangle { - width: audioIcon.implicitWidth + 4 - height: audioIcon.implicitHeight + 4 - color: "transparent" - anchors.verticalCenter: parent.verticalCenter - visible: root.showAudioIcon - - DankIcon { - id: audioIcon - - 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"; + 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 + } + wheelEvent.accepted = true } } - return "volume_up"; } - size: Theme.barIconSize(barThickness) - color: Theme.surfaceText + + DankIcon { + name: "settings" + size: Theme.barIconSize(root.barThickness) + color: root.isActive ? Theme.primary : Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon + } + } + + Row { + id: controlIndicators + visible: !root.isVerticalOrientation anchors.centerIn: parent - } + spacing: Theme.spacingXS - MouseArea { - id: audioWheelArea + DankIcon { + id: networkIcon - 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); + name: { + if (NetworkService.wifiToggling) { + return "sync"; + } + + if (NetworkService.networkStatus === "ethernet") { + return "lan"; + } + + return NetworkService.wifiSignalIcon; } - if (AudioService.sink && AudioService.sink.audio) { - AudioService.sink.audio.muted = false; - AudioService.sink.audio.volume = newVolume / 100; + size: Theme.barIconSize(root.barThickness) + color: { + if (NetworkService.wifiToggling) { + return Theme.primary; + } + + return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton; } - wheelEvent.accepted = true; + anchors.verticalCenter: parent.verticalCenter + visible: root.showNetworkIcon && NetworkService.networkAvailable + } + + DankIcon { + id: bluetoothIcon + + name: "bluetooth" + size: Theme.barIconSize(root.barThickness) + color: BluetoothService.connected ? Theme.primary : Theme.outlineButton + anchors.verticalCenter: parent.verticalCenter + visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled + } + + Rectangle { + width: audioIcon.implicitWidth + 4 + height: audioIcon.implicitHeight + 4 + color: "transparent" + anchors.verticalCenter: parent.verticalCenter + visible: root.showAudioIcon + + DankIcon { + id: audioIcon + + 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.barIconSize(root.barThickness) + color: Theme.surfaceText + anchors.centerIn: parent + } + + MouseArea { + id: audioWheelArea + + 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; + } + wheelEvent.accepted = true; + } + } + } + + DankIcon { + name: "mic" + size: Theme.barIconSize(root.barThickness) + color: Theme.primary + anchors.verticalCenter: parent.verticalCenter + visible: false + } + + DankIcon { + name: "settings" + size: Theme.barIconSize(root.barThickness) + color: root.isActive ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon } } - } - - DankIcon { - name: "mic" - size: Theme.barIconSize(barThickness) - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - visible: false // TODO: Add mic detection - } - - // Fallback settings icon when all other icons are hidden - DankIcon { - name: "settings" - size: Theme.barIconSize(barThickness) - color: controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - visible: !root.showNetworkIcon && !root.showBluetoothIcon && !root.showAudioIcon - } - } MouseArea { - id: controlCenterArea - anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } - root.clicked(); + root.clicked() } } - - } diff --git a/Modules/DankBar/Widgets/CpuMonitor.qml b/Modules/DankBar/Widgets/CpuMonitor.qml index 981fda65..8df217ff 100644 --- a/Modules/DankBar/Widgets/CpuMonitor.qml +++ b/Modules/DankBar/Widgets/CpuMonitor.qml @@ -1,37 +1,20 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool showPercentage: true property bool showIcon: true property var toggleProcessList - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real barThickness: 48 - property real widgetThickness: 30 + property var popoutTarget: null property var widgetData: null property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - width: isVertical ? widgetThickness : (cpuContent.implicitWidth + horizontalPadding * 2) - height: isVertical ? (cpuColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = cpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Component.onCompleted: { DgopService.addRef(["cpu"]); } @@ -39,120 +22,123 @@ Rectangle { DgopService.removeRef(["cpu"]); } - MouseArea { - id: cpuArea + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuContent.implicitWidth + implicitHeight: root.isVerticalOrientation ? cpuColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) + Column { + id: cpuColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "memory" + size: Theme.barIconSize(root.barThickness) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: cpuContent + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: 3 + + DankIcon { + name: "memory" + size: Theme.barIconSize(root.barThickness) + color: { + if (DgopService.cpuUsage > 80) { + return Theme.tempDanger; + } + + if (DgopService.cpuUsage > 60) { + return Theme.tempWarning; + } + + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: { + if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) { + return "--%"; + } + + return DgopService.cpuUsage.toFixed(0) + "%"; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + + StyledTextMetrics { + id: cpuBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "100%" + } + + width: root.minimumWidth ? Math.max(cpuBaseline.width, paintedWidth) : paintedWidth + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + } + } + + MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } DgopService.setSortBy("cpu"); if (root.toggleProcessList) { root.toggleProcessList(); } - } } - - Column { - id: cpuColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: "memory" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: cpuContent - visible: !root.isVertical - anchors.centerIn: parent - spacing: 3 - - DankIcon { - name: "memory" - size: Theme.barIconSize(barThickness) - color: { - if (DgopService.cpuUsage > 80) { - return Theme.tempDanger; - } - - if (DgopService.cpuUsage > 60) { - return Theme.tempWarning; - } - - return Theme.surfaceText; - } - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null || DgopService.cpuUsage === 0) { - return "--%"; - } - - return DgopService.cpuUsage.toFixed(0) + "%"; - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - - StyledTextMetrics { - id: cpuBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "100%" - } - - width: root.minimumWidth ? Math.max(cpuBaseline.width, paintedWidth) : paintedWidth - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - - } - } diff --git a/Modules/DankBar/Widgets/CpuTemperature.qml b/Modules/DankBar/Widgets/CpuTemperature.qml index 04350575..d0c7dd0a 100644 --- a/Modules/DankBar/Widgets/CpuTemperature.qml +++ b/Modules/DankBar/Widgets/CpuTemperature.qml @@ -1,37 +1,20 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool showPercentage: true property bool showIcon: true property var toggleProcessList - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real barThickness: 48 - property real widgetThickness: 30 + property var popoutTarget: null property var widgetData: null property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - width: isVertical ? widgetThickness : (cpuTempContent.implicitWidth + horizontalPadding * 2) - height: isVertical ? (cpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = cpuTempArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Component.onCompleted: { DgopService.addRef(["cpu"]); } @@ -39,121 +22,123 @@ Rectangle { DgopService.removeRef(["cpu"]); } - MouseArea { - id: cpuTempArea + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : cpuTempContent.implicitWidth + implicitHeight: root.isVerticalOrientation ? cpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) + Column { + id: cpuTempColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "device_thermostat" + size: Theme.barIconSize(root.barThickness) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: cpuTempContent + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: 3 + + DankIcon { + name: "device_thermostat" + size: Theme.barIconSize(root.barThickness) + color: { + if (DgopService.cpuTemperature > 85) { + return Theme.tempDanger; + } + + if (DgopService.cpuTemperature > 69) { + return Theme.tempWarning; + } + + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: { + if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) { + return "--°"; + } + + return Math.round(DgopService.cpuTemperature) + "°"; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + + StyledTextMetrics { + id: tempBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "100°" + } + + width: root.minimumWidth ? Math.max(tempBaseline.width, paintedWidth) : paintedWidth + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + } + } + + MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } DgopService.setSortBy("cpu"); if (root.toggleProcessList) { root.toggleProcessList(); } - } } - - Column { - id: cpuTempColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: "device_thermostat" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: cpuTempContent - visible: !root.isVertical - anchors.centerIn: parent - spacing: 3 - - DankIcon { - name: "device_thermostat" - size: Theme.barIconSize(barThickness) - color: { - if (DgopService.cpuTemperature > 85) { - return Theme.tempDanger; - } - - if (DgopService.cpuTemperature > 69) { - return Theme.tempWarning; - } - - return Theme.surfaceText; - } - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature < 0) { - return "--°"; - } - - return Math.round(DgopService.cpuTemperature) + "°"; - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - - StyledTextMetrics { - id: tempBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "100°" - } - - width: root.minimumWidth ? Math.max(tempBaseline.width, paintedWidth) : paintedWidth - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - - } - - } diff --git a/Modules/DankBar/Widgets/DiskUsage.qml b/Modules/DankBar/Widgets/DiskUsage.qml index a39fba41..8391c34f 100644 --- a/Modules/DankBar/Widgets/DiskUsage.qml +++ b/Modules/DankBar/Widgets/DiskUsage.qml @@ -1,44 +1,35 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property var widgetData: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/" - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) property var selectedMount: { if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) { return null } - // Force re-evaluation when mountPath changes const currentMountPath = root.mountPath || "/" - // First try to find exact match for (let i = 0; i < DgopService.diskMounts.length; i++) { if (DgopService.diskMounts[i].mount === currentMountPath) { return DgopService.diskMounts[i] } } - // Fallback to root for (let i = 0; i < DgopService.diskMounts.length; i++) { if (DgopService.diskMounts[i].mount === "/") { return DgopService.diskMounts[i] } } - // Last resort - first mount return DgopService.diskMounts[0] || null } @@ -50,17 +41,6 @@ Rectangle { return parseFloat(percentStr) || 0 } - width: isVertical ? widgetThickness : (diskContent.implicitWidth + horizontalPadding * 2) - height: isVertical ? (diskColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent" - } - - const baseColor = Theme.widgetBaseBackgroundColor - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) - } Component.onCompleted: { DgopService.addRef(["diskmounts"]) } @@ -70,7 +50,6 @@ Rectangle { Connections { function onWidgetDataChanged() { - // Force property re-evaluation by triggering change detection root.mountPath = Qt.binding(() => { return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/" }) @@ -82,21 +61,18 @@ Rectangle { const currentMountPath = root.mountPath || "/" - // First try to find exact match for (let i = 0; i < DgopService.diskMounts.length; i++) { if (DgopService.diskMounts[i].mount === currentMountPath) { return DgopService.diskMounts[i] } } - // Fallback to root for (let i = 0; i < DgopService.diskMounts.length; i++) { if (DgopService.diskMounts[i].mount === "/") { return DgopService.diskMounts[i] } } - // Last resort - first mount return DgopService.diskMounts[0] || null }) } @@ -104,6 +80,116 @@ Rectangle { target: SettingsData } + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : diskContent.implicitWidth + implicitHeight: root.isVerticalOrientation ? diskColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) + + Column { + id: diskColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "storage" + size: Theme.barIconSize(root.barThickness) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: diskContent + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: 3 + + DankIcon { + name: "storage" + size: Theme.barIconSize(root.barThickness) + color: { + if (root.diskUsagePercent > 90) { + return Theme.tempDanger + } + if (root.diskUsagePercent > 75) { + return Theme.tempWarning + } + return Theme.surfaceText + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: { + if (!root.selectedMount) { + return "--" + } + return root.selectedMount.mount + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + } + + StyledText { + text: { + if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) { + return "--%" + } + return root.diskUsagePercent.toFixed(0) + "%" + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + + StyledTextMetrics { + id: diskBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "100%" + } + + width: Math.max(diskBaseline.width, paintedWidth) + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + } + } + Loader { id: tooltipLoader active: false @@ -111,11 +197,11 @@ Rectangle { } MouseArea { - id: diskArea + z: 1 anchors.fill: parent - hoverEnabled: root.isVertical + hoverEnabled: root.isVerticalOrientation onEntered: { - if (root.isVertical && root.selectedMount) { + if (root.isVerticalOrientation && root.selectedMount) { tooltipLoader.active = true if (tooltipLoader.item) { const globalPos = mapToGlobal(width / 2, height / 2) @@ -136,107 +222,4 @@ Rectangle { tooltipLoader.active = false } } - - Column { - id: diskColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: "storage" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: diskContent - visible: !root.isVertical - anchors.centerIn: parent - spacing: 3 - - DankIcon { - name: "storage" - size: Theme.barIconSize(barThickness) - color: { - if (root.diskUsagePercent > 90) { - return Theme.tempDanger - } - if (root.diskUsagePercent > 75) { - return Theme.tempWarning - } - return Theme.surfaceText - } - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - if (!root.selectedMount) { - return "--" - } - return root.selectedMount.mount - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - } - - StyledText { - text: { - if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) { - return "--%" - } - return root.diskUsagePercent.toFixed(0) + "%" - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - - StyledTextMetrics { - id: diskBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "100%" - } - - width: Math.max(diskBaseline.width, paintedWidth) - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - } -} \ No newline at end of file +} diff --git a/Modules/DankBar/Widgets/FocusedApp.qml b/Modules/DankBar/Widgets/FocusedApp.qml index 327c8339..dcc04e0a 100644 --- a/Modules/DankBar/Widgets/FocusedApp.qml +++ b/Modules/DankBar/Widgets/FocusedApp.qml @@ -4,21 +4,15 @@ import Quickshell.Wayland import Quickshell.Widgets import Quickshell.Hyprland import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property var parentScreen property bool compactMode: SettingsData.focusedWindowCompactMode property int availableWidth: 400 - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS - readonly property int baseWidth: contentRow.implicitWidth + horizontalPadding * 2 readonly property int maxNormalWidth: 456 readonly property int maxCompactWidth: 288 readonly property Toplevel activeWindow: ToplevelManager.activeToplevel @@ -93,148 +87,141 @@ Rectangle { return activeWindow && activeWindow.title } - width: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : (compactMode ? Math.min(baseWidth, maxCompactWidth) : Math.min(baseWidth, maxNormalWidth))) - height: !hasWindowsOnCurrentWorkspace ? 0 : (isVertical ? widgetThickness : widgetThickness) - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (!activeWindow || !activeWindow.title) { - return "transparent"; - } - - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - clip: true visible: hasWindowsOnCurrentWorkspace - IconImage { - id: appIcon - anchors.centerIn: parent - width: 18 - height: 18 - visible: root.isVertical && activeWindow && status === Image.Ready - source: { - if (!activeWindow || !activeWindow.appId) return "" - const moddedId = Paths.moddedAppId(activeWindow.appId) - if (moddedId.toLowerCase().includes("steam_app")) return "" - return Quickshell.iconPath(activeDesktopEntry?.icon, true) - } - smooth: true - mipmap: true - asynchronous: true - } - - DankIcon { - anchors.centerIn: parent - size: 18 - name: "sports_esports" - color: Theme.surfaceText - visible: { - if (!root.isVertical || !activeWindow || !activeWindow.appId) return false - const moddedId = Paths.moddedAppId(activeWindow.appId) - return moddedId.toLowerCase().includes("steam_app") - } - } - - Text { - anchors.centerIn: parent - visible: { - if (!root.isVertical || !activeWindow || !activeWindow.appId) return false - if (appIcon.status === Image.Ready) return false - const moddedId = Paths.moddedAppId(activeWindow.appId) - return !moddedId.toLowerCase().includes("steam_app") - } - text: { - if (!activeWindow || !activeWindow.appId) return "?" - if (activeDesktopEntry && activeDesktopEntry.name) { - return activeDesktopEntry.name.charAt(0).toUpperCase() + content: Component { + Item { + implicitWidth: { + if (!root.hasWindowsOnCurrentWorkspace) return 0 + if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2 + const baseWidth = contentRow.implicitWidth + return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2) } - return activeWindow.appId.charAt(0).toUpperCase() - } - font.pixelSize: 10 - color: Theme.surfaceText - font.weight: Font.Medium - } + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 + clip: true - Row { - id: contentRow - - anchors.centerIn: parent - spacing: Theme.spacingS - visible: !root.isVertical - - StyledText { - id: appText - - text: { - if (!activeWindow || !activeWindow.appId) { - return ""; + IconImage { + id: appIcon + anchors.centerIn: parent + width: 18 + height: 18 + visible: root.isVerticalOrientation && 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) } - - const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId); - return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId; + smooth: true + mipmap: true + asynchronous: true } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - elide: Text.ElideRight - maximumLineCount: 1 - width: Math.min(implicitWidth, compactMode ? 80 : 180) - visible: !compactMode && text.length > 0 - } - StyledText { - text: "•" - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.outlineButton - anchors.verticalCenter: parent.verticalCenter - visible: !compactMode && appText.text && titleText.text - } - - StyledText { - id: titleText - - text: { - const title = activeWindow && activeWindow.title ? activeWindow.title : ""; - const appName = appText.text; - if (!title || !appName) { - return title; + DankIcon { + anchors.centerIn: parent + size: 18 + name: "sports_esports" + color: Theme.surfaceText + visible: { + if (!root.isVerticalOrientation || !activeWindow || !activeWindow.appId) return false + const moddedId = Paths.moddedAppId(activeWindow.appId) + return moddedId.toLowerCase().includes("steam_app") } - - if (title.endsWith(" - " + appName)) { - return title.substring(0, title.length - (" - " + appName).length); - } - - if (title.endsWith(appName)) { - return title.substring(0, title.length - appName.length).replace(/ - $/, ""); - } - - return title; } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - elide: Text.ElideRight - maximumLineCount: 1 - width: Math.min(implicitWidth, compactMode ? 280 : 250) - visible: text.length > 0 - } + Text { + anchors.centerIn: parent + visible: { + if (!root.isVerticalOrientation || !activeWindow || !activeWindow.appId) return false + if (appIcon.status === Image.Ready) return false + const moddedId = Paths.moddedAppId(activeWindow.appId) + return !moddedId.toLowerCase().includes("steam_app") + } + text: { + if (!activeWindow || !activeWindow.appId) return "?" + if (activeDesktopEntry && activeDesktopEntry.name) { + return activeDesktopEntry.name.charAt(0).toUpperCase() + } + return activeWindow.appId.charAt(0).toUpperCase() + } + font.pixelSize: 10 + color: Theme.surfaceText + font.weight: Font.Medium + } + + Row { + id: contentRow + anchors.centerIn: parent + spacing: Theme.spacingS + visible: !root.isVerticalOrientation + + StyledText { + id: appText + text: { + if (!activeWindow || !activeWindow.appId) { + return ""; + } + + const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId); + return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + maximumLineCount: 1 + width: Math.min(implicitWidth, compactMode ? 80 : 180) + visible: !compactMode && text.length > 0 + } + + StyledText { + text: "•" + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.outlineButton + anchors.verticalCenter: parent.verticalCenter + visible: !compactMode && appText.text && titleText.text + } + + StyledText { + id: titleText + text: { + const title = activeWindow && activeWindow.title ? activeWindow.title : ""; + const appName = appText.text; + if (!title || !appName) { + return title; + } + + if (title.endsWith(" - " + appName)) { + return title.substring(0, title.length - (" - " + appName).length); + } + + if (title.endsWith(appName)) { + return title.substring(0, title.length - appName.length).replace(/ - $/, ""); + } + + return title; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + maximumLineCount: 1 + width: Math.min(implicitWidth, compactMode ? 280 : 250) + visible: text.length > 0 + } + } + } } MouseArea { id: mouseArea - anchors.fill: parent - hoverEnabled: root.isVertical + hoverEnabled: root.isVerticalOrientation + acceptedButtons: Qt.NoButton onEntered: { - if (root.isVertical && activeWindow && activeWindow.appId && root.parentScreen) { + if (root.isVerticalOrientation && activeWindow && activeWindow.appId && root.parentScreen) { tooltipLoader.active = true if (tooltipLoader.item) { const globalPos = mapToGlobal(width / 2, height / 2) @@ -266,14 +253,4 @@ Rectangle { active: false sourceComponent: DankTooltip {} } - - - Behavior on width { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - } diff --git a/Modules/DankBar/Widgets/GpuTemperature.qml b/Modules/DankBar/Widgets/GpuTemperature.qml index a295c327..e8e03b76 100644 --- a/Modules/DankBar/Widgets/GpuTemperature.qml +++ b/Modules/DankBar/Widgets/GpuTemperature.qml @@ -1,26 +1,20 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool showPercentage: true property bool showIcon: true property var toggleProcessList - property string section: "right" - property var popupTarget: null - property var parentScreen: null + property var popoutTarget: null property var widgetData: null - property real barThickness: 48 - property real widgetThickness: 30 property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0 property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) property real displayTemp: { if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) { return 0; @@ -34,7 +28,6 @@ Rectangle { } function updateWidgetPciId(pciId) { - // Find and update this widget's pciId in the settings const sections = ["left", "center", "right"]; for (let s = 0; s < sections.length; s++) { const sectionId = sections[s]; @@ -68,17 +61,6 @@ Rectangle { } } - width: isVertical ? widgetThickness : (gpuTempContent.implicitWidth + horizontalPadding * 2) - height: isVertical ? (gpuTempColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = gpuArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Component.onCompleted: { DgopService.addRef(["gpu"]); if (widgetData && widgetData.pciId) { @@ -92,12 +74,10 @@ Rectangle { if (widgetData && widgetData.pciId) { DgopService.removeGpuPciId(widgetData.pciId); } - } Connections { function onWidgetDataChanged() { - // Force property re-evaluation by triggering change detection root.selectedGpuIndex = Qt.binding(() => { return (root.widgetData && root.widgetData.selectedGpuIndex !== undefined) ? root.widgetData.selectedGpuIndex : 0; }); @@ -106,122 +86,126 @@ Rectangle { target: SettingsData } - MouseArea { - id: gpuArea + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : gpuTempContent.implicitWidth + implicitHeight: root.isVerticalOrientation ? gpuTempColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) + Column { + id: gpuTempColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "auto_awesome_mosaic" + size: Theme.barIconSize(root.barThickness) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: gpuTempContent + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: 3 + + DankIcon { + name: "auto_awesome_mosaic" + size: Theme.barIconSize(root.barThickness) + color: { + if (root.displayTemp > 80) { + return Theme.tempDanger; + } + + if (root.displayTemp > 65) { + return Theme.tempWarning; + } + + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: { + if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) { + return "--°"; + } + + return Math.round(root.displayTemp) + "°"; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + + StyledTextMetrics { + id: gpuTempBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "100°" + } + + width: root.minimumWidth ? Math.max(gpuTempBaseline.width, paintedWidth) : paintedWidth + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + } + } + + MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } DgopService.setSortBy("cpu"); if (root.toggleProcessList) { root.toggleProcessList(); } - } } - Column { - id: gpuTempColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: "auto_awesome_mosaic" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: gpuTempContent - visible: !root.isVertical - anchors.centerIn: parent - spacing: 3 - - DankIcon { - name: "auto_awesome_mosaic" - size: Theme.barIconSize(barThickness) - color: { - if (root.displayTemp > 80) { - return Theme.tempDanger; - } - - if (root.displayTemp > 65) { - return Theme.tempWarning; - } - - return Theme.surfaceText; - } - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - if (root.displayTemp === undefined || root.displayTemp === null || root.displayTemp === 0) { - return "--°"; - } - - return Math.round(root.displayTemp) + "°"; - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - - StyledTextMetrics { - id: gpuTempBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "100°" - } - - width: root.minimumWidth ? Math.max(gpuTempBaseline.width, paintedWidth) : paintedWidth - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - - } - Timer { id: autoSaveTimer @@ -231,13 +215,10 @@ Rectangle { if (DgopService.availableGpus && DgopService.availableGpus.length > 0) { const firstGpu = DgopService.availableGpus[0]; if (firstGpu && firstGpu.pciId) { - // Save the first GPU's PCI ID to this widget's settings updateWidgetPciId(firstGpu.pciId); DgopService.addGpuPciId(firstGpu.pciId); } } } } - - } diff --git a/Modules/DankBar/Widgets/IdleInhibitor.qml b/Modules/DankBar/Widgets/IdleInhibitor.qml index c35eb131..e28bae46 100644 --- a/Modules/DankBar/Widgets/IdleInhibitor.qml +++ b/Modules/DankBar/Widgets/IdleInhibitor.qml @@ -2,51 +2,34 @@ import QtQuick import QtQuick.Controls import Quickshell import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real widgetThickness: 30 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 - width: isVertical ? widgetThickness : (idleIcon.width + horizontalPadding * 2) - height: isVertical ? (idleIcon.height + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; + DankIcon { + anchors.centerIn: parent + name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle" + size: Theme.barIconSize(root.barThickness, -4) + color: Theme.surfaceText + } } - - const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - - DankIcon { - id: idleIcon - - anchors.centerIn: parent - name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle" - size: Theme.barIconSize(barThickness, -4) - color: Theme.surfaceText } MouseArea { - id: mouseArea - + z: 1 anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - SessionService.toggleIdleInhibit(); + SessionService.toggleIdleInhibit() } } - - } diff --git a/Modules/DankBar/Widgets/KeyboardLayoutName.qml b/Modules/DankBar/Widgets/KeyboardLayoutName.qml index 0314f006..bbd56bbd 100644 --- a/Modules/DankBar/Widgets/KeyboardLayoutName.qml +++ b/Modules/DankBar/Widgets/KeyboardLayoutName.qml @@ -3,36 +3,69 @@ import QtQuick.Controls import Quickshell import Quickshell.Io import qs.Common +import qs.Modules.Plugins import qs.Modules.ProcessList import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) property string currentLayout: "" property string hyprlandKeyboard: "" - width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2) - height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth + implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) - const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); + Column { + id: contentColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "keyboard" + size: Theme.barIconSize(root.barThickness) + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + + StyledText { + text: { + if (!root.currentLayout) return "" + const parts = root.currentLayout.split(" ") + if (parts.length > 0) { + return parts[0].substring(0, 2).toUpperCase() + } + return root.currentLayout.substring(0, 2).toUpperCase() + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: contentRow + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingS + + StyledText { + text: root.currentLayout + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + } + } } MouseArea { - id: mouseArea - + z: 1 anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -51,53 +84,6 @@ Rectangle { } } - Column { - id: contentColumn - - anchors.centerIn: parent - spacing: 1 - visible: root.isVertical - - DankIcon { - name: "keyboard" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: contentRow - - anchors.centerIn: parent - spacing: Theme.spacingS - visible: !root.isVertical - - StyledText { - text: currentLayout - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - } - - Timer { id: updateTimer interval: 1000 diff --git a/Modules/DankBar/Widgets/LauncherButton.qml b/Modules/DankBar/Widgets/LauncherButton.qml index 685c6d43..67190bc5 100644 --- a/Modules/DankBar/Widgets/LauncherButton.qml +++ b/Modules/DankBar/Widgets/LauncherButton.qml @@ -3,127 +3,95 @@ import QtQuick.Effects import Quickshell import Quickshell.Widgets import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Item { +BasePill { id: root property bool isActive: false - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "left" - property var popupTarget: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 property var hyprlandOverviewLoader: null - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - signal clicked() + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 - width: widgetThickness - height: widgetThickness - - MouseArea { - id: launcherArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - acceptedButtons: Qt.LeftButton | Qt.RightButton - onPressed: function (mouse){ - if (mouse.button === Qt.RightButton) { - if (CompositorService.isNiri) { - NiriService.toggleOverview() - } else if (CompositorService.isHyprland && root.hyprlandOverviewLoader?.item) { - root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen - } - return + DankIcon { + visible: SettingsData.launcherLogoMode === "apps" + anchors.centerIn: parent + name: "apps" + size: Theme.barIconSize(root.barThickness, -4) + color: Theme.surfaceText } - root.clicked(); - 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); + SystemLogo { + visible: SettingsData.launcherLogoMode === "os" + anchors.centerIn: parent + width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + colorOverride: Theme.effectiveLogoColor + brightnessOverride: SettingsData.launcherLogoBrightness + contrastOverride: SettingsData.launcherLogoContrast + } + + IconImage { + visible: SettingsData.launcherLogoMode === "compositor" + anchors.centerIn: parent + width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + smooth: true + asynchronous: true + source: { + if (CompositorService.isNiri) { + return "file://" + Theme.shellDir + "/assets/niri.svg" + } else if (CompositorService.isHyprland) { + return "file://" + Theme.shellDir + "/assets/hyprland.svg" + } + return "" + } + layer.enabled: Theme.effectiveLogoColor !== "" + layer.effect: MultiEffect { + saturation: 0 + colorization: 1 + colorizationColor: Theme.effectiveLogoColor + brightness: SettingsData.launcherLogoBrightness + contrast: SettingsData.launcherLogoContrast + } + } + + IconImage { + visible: SettingsData.launcherLogoMode === "custom" && SettingsData.launcherLogoCustomPath !== "" + anchors.centerIn: parent + width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset) + smooth: true + asynchronous: true + source: SettingsData.launcherLogoCustomPath ? "file://" + SettingsData.launcherLogoCustomPath.replace("file://", "") : "" + layer.enabled: Theme.effectiveLogoColor !== "" + layer.effect: MultiEffect { + saturation: 0 + colorization: 1 + colorizationColor: Theme.effectiveLogoColor + brightness: SettingsData.launcherLogoBrightness + contrast: SettingsData.launcherLogoContrast + } } } } - Rectangle { - id: launcherContent - + MouseArea { + id: customMouseArea anchors.fill: parent - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = launcherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - - DankIcon { - visible: SettingsData.launcherLogoMode === "apps" - anchors.centerIn: parent - name: "apps" - size: Theme.barIconSize(barThickness, -4) - color: Theme.surfaceText - } - - SystemLogo { - visible: SettingsData.launcherLogoMode === "os" - anchors.centerIn: parent - width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - colorOverride: Theme.effectiveLogoColor - brightnessOverride: SettingsData.launcherLogoBrightness - contrastOverride: SettingsData.launcherLogoContrast - } - - IconImage { - visible: SettingsData.launcherLogoMode === "compositor" - anchors.centerIn: parent - width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - smooth: true - asynchronous: true - source: { - if (CompositorService.isNiri) { - return "file://" + Theme.shellDir + "/assets/niri.svg" - } else if (CompositorService.isHyprland) { - return "file://" + Theme.shellDir + "/assets/hyprland.svg" - } - return "" - } - layer.enabled: Theme.effectiveLogoColor !== "" - layer.effect: MultiEffect { - saturation: 0 - colorization: 1 - colorizationColor: Theme.effectiveLogoColor - brightness: SettingsData.launcherLogoBrightness - contrast: SettingsData.launcherLogoContrast - } - } - - IconImage { - visible: SettingsData.launcherLogoMode === "custom" && SettingsData.launcherLogoCustomPath !== "" - anchors.centerIn: parent - width: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - height: Theme.barIconSize(barThickness, SettingsData.launcherLogoSizeOffset) - smooth: true - asynchronous: true - source: SettingsData.launcherLogoCustomPath ? "file://" + SettingsData.launcherLogoCustomPath.replace("file://", "") : "" - layer.enabled: Theme.effectiveLogoColor !== "" - layer.effect: MultiEffect { - saturation: 0 - colorization: 1 - colorizationColor: Theme.effectiveLogoColor - brightness: SettingsData.launcherLogoBrightness - contrast: SettingsData.launcherLogoContrast + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.RightButton + onPressed: function (mouse){ + if (CompositorService.isNiri) { + NiriService.toggleOverview() + } else if (root.hyprlandOverviewLoader?.item) { + root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen } } } diff --git a/Modules/DankBar/Widgets/Media.qml b/Modules/DankBar/Widgets/Media.qml index 4d532ad7..fc3f10c9 100644 --- a/Modules/DankBar/Widgets/Media.qml +++ b/Modules/DankBar/Widgets/Media.qml @@ -1,412 +1,358 @@ import QtQuick import Quickshell.Services.Mpris import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property bool playerAvailable: activePlayer !== null property bool compactMode: false readonly property int textWidth: { switch (SettingsData.mediaSize) { case 0: - return 0; // No text in small mode + return 0; case 2: - return 180; // Large text area + return 180; default: - return 120; // Medium text area + return 120; } } readonly property int currentContentWidth: { - if (isVertical) { - return widgetThickness; + if (isVerticalOrientation) { + return widgetThickness - horizontalPadding * 2; } const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20; const audioVizWidth = 20; const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth; - return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0) + horizontalPadding * 2; + return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0); } readonly property int currentContentHeight: { - if (!isVertical) { - return widgetThickness; + if (!isVerticalOrientation) { + return widgetThickness - horizontalPadding * 2; } const audioVizHeight = 20; const playButtonHeight = 24; - return audioVizHeight + Theme.spacingXS + playButtonHeight + horizontalPadding * 2; - } - property string section: "center" - property var popupTarget: null - property var parentScreen: null - property real barThickness: 48 - property real widgetThickness: 30 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - - signal clicked() - - width: currentContentWidth - height: currentContentHeight - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - states: [ - State { - name: "shown" - when: playerAvailable - - PropertyChanges { - target: root - opacity: 1 - width: currentContentWidth - height: currentContentHeight - } - - }, - State { - name: "hidden" - when: !playerAvailable - - PropertyChanges { - target: root - opacity: 0 - width: isVertical ? widgetThickness : 0 - height: isVertical ? 0 : widgetThickness - } - - } - ] - transitions: [ - Transition { - from: "shown" - to: "hidden" - - SequentialAnimation { - PauseAnimation { - duration: 500 - } - - NumberAnimation { - properties: isVertical ? "opacity,height" : "opacity,width" - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - - }, - Transition { - from: "hidden" - to: "shown" - - NumberAnimation { - properties: isVertical ? "opacity,height" : "opacity,width" - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - ] - - Column { - id: verticalLayout - visible: root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingXS - - AudioVisualization { - anchors.horizontalCenter: parent.horizontalCenter - - MouseArea { - anchors.fill: parent - 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() - } - } - } - - 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 - cursorShape: Qt.PointingHandCursor - 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() - } - } - } - } + return audioVizHeight + Theme.spacingXS + playButtonHeight; } - Row { - id: mediaRow + content: Component { + Item { + implicitWidth: root.playerAvailable ? root.currentContentWidth : 0 + implicitHeight: root.playerAvailable ? root.currentContentHeight : 0 + opacity: root.playerAvailable ? 1 : 0 - visible: !root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingXS - - Row { - id: mediaInfo - - spacing: Theme.spacingXS - - AudioVisualization { - anchors.verticalCenter: parent.verticalCenter - } - - Rectangle { - id: textContainer - - property string displayText: { - if (!activePlayer || !activePlayer.trackTitle) { - return ""; + states: [ + State { + name: "shown" + when: root.playerAvailable + PropertyChanges { + target: parent + opacity: 1 + implicitWidth: root.currentContentWidth + implicitHeight: root.currentContentHeight } - - let identity = activePlayer.identity || ""; - let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari"); - let title = ""; - let subtitle = ""; - if (isWebMedia && activePlayer.trackTitle) { - title = activePlayer.trackTitle; - subtitle = activePlayer.trackArtist || identity; - } else { - title = activePlayer.trackTitle || "Unknown Track"; - subtitle = activePlayer.trackArtist || ""; + }, + State { + name: "hidden" + when: !root.playerAvailable + PropertyChanges { + target: parent + opacity: 0 + implicitWidth: 0 + implicitHeight: 0 } - return subtitle.length > 0 ? title + " • " + subtitle : title; } - - anchors.verticalCenter: parent.verticalCenter - width: textWidth - height: 20 - visible: SettingsData.mediaSize > 0 - clip: true - color: "transparent" - - StyledText { - id: mediaText - - property bool needsScrolling: implicitWidth > textContainer.width - property real scrollOffset: 0 - - anchors.verticalCenter: parent.verticalCenter - text: textContainer.displayText - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - font.weight: Font.Medium - wrapMode: Text.NoWrap - x: needsScrolling ? -scrollOffset : 0 - onTextChanged: { - scrollOffset = 0; - scrollAnimation.restart(); - } - + ] + transitions: [ + Transition { + from: "shown" + to: "hidden" SequentialAnimation { - id: scrollAnimation - - running: mediaText.needsScrolling && textContainer.visible - loops: Animation.Infinite - PauseAnimation { - duration: 2000 + duration: 500 } - NumberAnimation { - target: mediaText - property: "scrollOffset" - from: 0 - to: mediaText.implicitWidth - textContainer.width + 5 - duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) - easing.type: Easing.Linear + properties: "opacity,implicitWidth,implicitHeight" + duration: Theme.shortDuration + easing.type: Theme.standardEasing } - - PauseAnimation { - duration: 2000 - } - - NumberAnimation { - target: mediaText - property: "scrollOffset" - to: 0 - duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) - easing.type: Easing.Linear - } - } - + }, + Transition { + from: "hidden" + to: "shown" + NumberAnimation { + properties: "opacity,implicitWidth,implicitHeight" + duration: Theme.shortDuration + easing.type: Theme.standardEasing + } } + ] - MouseArea { - anchors.fill: parent - enabled: root.playerAvailable - cursorShape: Qt.PointingHandCursor - onPressed: { - if (root.popupTarget && root.popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) - const currentScreen = root.parentScreen || Screen - const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.width) - root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen) + Column { + id: verticalLayout + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingXS + + AudioVisualization { + anchors.horizontalCenter: parent.horizontalCenter + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (root.popoutTarget && root.popoutTarget.setTriggerPosition) { + const globalPos = parent.mapToGlobal(0, 0) + const currentScreen = root.parentScreen || Screen + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, parent.width) + root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen) + } + root.clicked() } - root.clicked() } } + 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 + cursorShape: Qt.PointingHandCursor + 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() + } + } + } + } } - } + Row { + id: mediaRow + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingXS - Row { - spacing: Theme.spacingXS - anchors.verticalCenter: parent.verticalCenter + Row { + id: mediaInfo + spacing: Theme.spacingXS - Rectangle { - width: 20 - height: 20 - radius: 10 - anchors.verticalCenter: parent.verticalCenter - color: prevArea.containsMouse ? Theme.primaryHover : "transparent" - visible: root.playerAvailable - opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3 + AudioVisualization { + anchors.verticalCenter: parent.verticalCenter + } - DankIcon { - anchors.centerIn: parent - name: "skip_previous" - size: 12 - color: Theme.surfaceText - } + Rectangle { + id: textContainer + property string displayText: { + if (!activePlayer || !activePlayer.trackTitle) { + return ""; + } - MouseArea { - id: prevArea + let identity = activePlayer.identity || ""; + let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari"); + let title = ""; + let subtitle = ""; + if (isWebMedia && activePlayer.trackTitle) { + title = activePlayer.trackTitle; + subtitle = activePlayer.trackArtist || identity; + } else { + title = activePlayer.trackTitle || "Unknown Track"; + subtitle = activePlayer.trackArtist || ""; + } + return subtitle.length > 0 ? title + " • " + subtitle : title; + } - anchors.fill: parent - enabled: root.playerAvailable - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (activePlayer) { - activePlayer.previous(); + anchors.verticalCenter: parent.verticalCenter + width: textWidth + height: 20 + visible: SettingsData.mediaSize > 0 + clip: true + color: "transparent" + + StyledText { + id: mediaText + property bool needsScrolling: implicitWidth > textContainer.width + property real scrollOffset: 0 + + anchors.verticalCenter: parent.verticalCenter + text: textContainer.displayText + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + font.weight: Font.Medium + wrapMode: Text.NoWrap + x: needsScrolling ? -scrollOffset : 0 + onTextChanged: { + scrollOffset = 0; + scrollAnimation.restart(); + } + + SequentialAnimation { + id: scrollAnimation + running: mediaText.needsScrolling && textContainer.visible + loops: Animation.Infinite + + PauseAnimation { + duration: 2000 + } + + NumberAnimation { + target: mediaText + property: "scrollOffset" + from: 0 + to: mediaText.implicitWidth - textContainer.width + 5 + duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) + easing.type: Easing.Linear + } + + PauseAnimation { + duration: 2000 + } + + NumberAnimation { + target: mediaText + property: "scrollOffset" + to: 0 + duration: Math.max(1000, (mediaText.implicitWidth - textContainer.width + 5) * 60) + easing.type: Easing.Linear + } + } + } + + MouseArea { + anchors.fill: parent + enabled: root.playerAvailable + cursorShape: Qt.PointingHandCursor + onPressed: { + if (root.popoutTarget && root.popoutTarget.setTriggerPosition) { + const globalPos = mapToGlobal(0, 0) + const currentScreen = root.parentScreen || Screen + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, root.width) + root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen) + } + root.clicked() + } } } } - } + Row { + spacing: Theme.spacingXS + anchors.verticalCenter: parent.verticalCenter - Rectangle { - width: 24 - height: 24 - radius: 12 - anchors.verticalCenter: parent.verticalCenter - color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover - visible: root.playerAvailable - opacity: activePlayer ? 1 : 0.3 + Rectangle { + width: 20 + height: 20 + radius: 10 + anchors.verticalCenter: parent.verticalCenter + color: prevArea.containsMouse ? Theme.primaryHover : "transparent" + visible: root.playerAvailable + opacity: (activePlayer && activePlayer.canGoPrevious) ? 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 - } + DankIcon { + anchors.centerIn: parent + name: "skip_previous" + size: 12 + color: Theme.surfaceText + } - MouseArea { - anchors.fill: parent - enabled: root.playerAvailable - cursorShape: Qt.PointingHandCursor - onClicked: { - if (activePlayer) { - activePlayer.togglePlaying(); + MouseArea { + id: prevArea + anchors.fill: parent + enabled: root.playerAvailable + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (activePlayer) { + activePlayer.previous(); + } + } + } + } + + Rectangle { + width: 24 + height: 24 + radius: 12 + anchors.verticalCenter: parent.verticalCenter + 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 + cursorShape: Qt.PointingHandCursor + onClicked: { + if (activePlayer) { + activePlayer.togglePlaying(); + } + } + } + } + + Rectangle { + width: 20 + height: 20 + radius: 10 + anchors.verticalCenter: parent.verticalCenter + color: nextArea.containsMouse ? Theme.primaryHover : "transparent" + visible: playerAvailable + opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3 + + DankIcon { + anchors.centerIn: parent + name: "skip_next" + size: 12 + color: Theme.surfaceText + } + + MouseArea { + id: nextArea + anchors.fill: parent + enabled: root.playerAvailable + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (activePlayer) { + activePlayer.next(); + } + } } } } - } - - Rectangle { - width: 20 - height: 20 - radius: 10 - anchors.verticalCenter: parent.verticalCenter - color: nextArea.containsMouse ? Theme.primaryHover : "transparent" - visible: playerAvailable - opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3 - - DankIcon { - anchors.centerIn: parent - name: "skip_next" - size: 12 - color: Theme.surfaceText - } - - MouseArea { - id: nextArea - - anchors.fill: parent - enabled: root.playerAvailable - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (activePlayer) { - activePlayer.next(); - } - } - } - - } - - } - - } - - - Behavior on width { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing } } - - Behavior on height { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - } - } diff --git a/Modules/DankBar/Widgets/NetworkMonitor.qml b/Modules/DankBar/Widgets/NetworkMonitor.qml index eb146946..1070926b 100644 --- a/Modules/DankBar/Widgets/NetworkMonitor.qml +++ b/Modules/DankBar/Widgets/NetworkMonitor.qml @@ -1,194 +1,167 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Modules.ProcessList import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property int availableWidth: 400 - readonly property int baseWidth: contentRow.implicitWidth + Theme.spacingS * 2 - readonly property int maxNormalWidth: 456 - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - function formatNetworkSpeed(bytesPerSec) { if (bytesPerSec < 1024) { - return bytesPerSec.toFixed(0) + " B/s"; + return bytesPerSec.toFixed(0) + " B/s" } else if (bytesPerSec < 1024 * 1024) { - return (bytesPerSec / 1024).toFixed(1) + " KB/s"; + return (bytesPerSec / 1024).toFixed(1) + " KB/s" } else if (bytesPerSec < 1024 * 1024 * 1024) { - return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"; + return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s" } else { - return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s"; + return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s" } } - width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2) - height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = networkArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Component.onCompleted: { - DgopService.addRef(["network"]); + DgopService.addRef(["network"]) } Component.onDestruction: { - DgopService.removeRef(["network"]); + DgopService.removeRef(["network"]) } - MouseArea { - id: networkArea + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth + implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - } + Column { + id: contentColumn + anchors.centerIn: parent + spacing: 2 + visible: root.isVerticalOrientation - Column { - id: contentColumn - - anchors.centerIn: parent - spacing: 2 - visible: root.isVertical - - DankIcon { - name: "network_check" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.error - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: contentRow - - anchors.centerIn: parent - spacing: Theme.spacingS - visible: !root.isVertical - - DankIcon { - name: "network_check" - size: Theme.barIconSize(barThickness) - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - - Row { - anchors.verticalCenter: parent.verticalCenter - spacing: 4 - - StyledText { - text: "↓" - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.info - } - - StyledText { - text: DgopService.networkRxRate > 0 ? formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s" - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - wrapMode: Text.NoWrap - - StyledTextMetrics { - id: rxBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "88.8 MB/s" + DankIcon { + name: "network_check" + size: Theme.barIconSize(root.barThickness) + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter } - width: Math.max(rxBaseline.width, paintedWidth) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.info + anchors.horizontalCenter: parent.horizontalCenter + } - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.error + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: contentRow + anchors.centerIn: parent + spacing: Theme.spacingS + visible: !root.isVerticalOrientation + + DankIcon { + name: "network_check" + size: Theme.barIconSize(root.barThickness) + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + Row { + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + StyledText { + text: "↓" + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.info + } + + StyledText { + text: DgopService.networkRxRate > 0 ? root.formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s" + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + wrapMode: Text.NoWrap + + StyledTextMetrics { + id: rxBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "88.8 MB/s" + } + + width: Math.max(rxBaseline.width, paintedWidth) + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + + Row { + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + StyledText { + text: "↑" + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.error + } + + StyledText { + text: DgopService.networkTxRate > 0 ? root.formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s" + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + wrapMode: Text.NoWrap + + StyledTextMetrics { + id: txBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "88.8 MB/s" + } + + width: Math.max(txBaseline.width, paintedWidth) + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } } } } - } - - Row { - anchors.verticalCenter: parent.verticalCenter - spacing: 4 - - StyledText { - text: "↑" - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.error - } - - StyledText { - text: DgopService.networkTxRate > 0 ? formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s" - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - wrapMode: Text.NoWrap - - StyledTextMetrics { - id: txBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "88.8 MB/s" - } - - width: Math.max(txBaseline.width, paintedWidth) - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - - } - } - - } diff --git a/Modules/DankBar/Widgets/NotepadButton.qml b/Modules/DankBar/Widgets/NotepadButton.qml index dcc15a37..e446cbfa 100644 --- a/Modules/DankBar/Widgets/NotepadButton.qml +++ b/Modules/DankBar/Widgets/NotepadButton.qml @@ -1,22 +1,13 @@ import QtQuick import Quickshell.Hyprland import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - property string section: "right" - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - - signal clicked() - readonly property string focusedScreenName: ( CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") : CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : "" @@ -43,54 +34,43 @@ Rectangle { readonly property var notepadInstance: resolveNotepadInstance() readonly property bool isActive: notepadInstance?.isVisible ?? false - width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2) - height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 + + DankIcon { + id: notepadIcon + + anchors.centerIn: parent + name: "assignment" + size: Theme.barIconSize(root.barThickness, -4) + color: root.isActive ? Theme.primary : Theme.surfaceText + } + + Rectangle { + width: 6 + height: 6 + radius: 3 + color: Theme.primary + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4 + anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4 + visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0 + opacity: 0.8 + } } - - const baseColor = notepadArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - - DankIcon { - id: notepadIcon - - anchors.centerIn: parent - name: "assignment" - size: Theme.barIconSize(barThickness, -4) - color: notepadArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText - } - - Rectangle { - width: 6 - height: 6 - radius: 3 - color: Theme.primary - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 4 - anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 4 - visible: NotepadStorageService.tabs && NotepadStorageService.tabs.length > 0 - opacity: 0.8 } MouseArea { - id: notepadArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { const inst = root.notepadInstance if (inst) { inst.toggle() } - root.clicked() } } - - } \ No newline at end of file diff --git a/Modules/DankBar/Widgets/NotificationCenterButton.qml b/Modules/DankBar/Widgets/NotificationCenterButton.qml index 3d79a17c..5e7d7171 100644 --- a/Modules/DankBar/Widgets/NotificationCenterButton.qml +++ b/Modules/DankBar/Widgets/NotificationCenterButton.qml @@ -1,76 +1,37 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Widgets -Item { +BasePill { 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() + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 - 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" + DankIcon { + anchors.centerIn: parent + name: SessionData.doNotDisturb ? "notifications_off" : "notifications" + size: Theme.barIconSize(root.barThickness, -4) + color: SessionData.doNotDisturb ? Theme.error : (root.isActive ? Theme.primary : Theme.surfaceText) } - 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: Theme.barIconSize(barThickness, -4) - 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 + 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 + } } } } diff --git a/Modules/DankBar/Widgets/PrivacyIndicator.qml b/Modules/DankBar/Widgets/PrivacyIndicator.qml index 3ca4b1fe..697690dd 100644 --- a/Modules/DankBar/Widgets/PrivacyIndicator.qml +++ b/Modules/DankBar/Widgets/PrivacyIndicator.qml @@ -4,7 +4,7 @@ import qs.Common import qs.Services import qs.Widgets -Rectangle { +Item { id: root property bool isVertical: axis?.isVertical ?? false @@ -19,26 +19,167 @@ Rectangle { readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 + readonly property real visualWidth: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0) + readonly property real visualHeight: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : widgetThickness - width: isVertical ? widgetThickness : (hasActivePrivacy ? (contentWidth + horizontalPadding * 2) : 0) - height: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : (hasActivePrivacy ? widgetThickness : 0) - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + width: isVertical ? barThickness : visualWidth + height: isVertical ? visualHeight : barThickness visible: hasActivePrivacy opacity: hasActivePrivacy ? 1 : 0 enabled: hasActivePrivacy - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; + + Rectangle { + id: visualContent + width: root.visualWidth + height: root.visualHeight + anchors.centerIn: parent + radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + color: { + if (SettingsData.dankBarNoBackground) { + return "transparent" + } + + return Qt.rgba(privacyArea.containsMouse ? Theme.errorPressed.r : Theme.errorHover.r, privacyArea.containsMouse ? Theme.errorPressed.g : Theme.errorHover.g, privacyArea.containsMouse ? Theme.errorPressed.b : Theme.errorHover.b, (privacyArea.containsMouse ? Theme.errorPressed.a : Theme.errorHover.a) * Theme.widgetTransparency) } - return Qt.rgba(privacyArea.containsMouse ? Theme.errorPressed.r : Theme.errorHover.r, privacyArea.containsMouse ? Theme.errorPressed.g : Theme.errorHover.g, privacyArea.containsMouse ? Theme.errorPressed.b : Theme.errorHover.b, (privacyArea.containsMouse ? Theme.errorPressed.a : Theme.errorHover.a) * Theme.widgetTransparency); + Column { + anchors.centerIn: parent + spacing: Theme.spacingXS + visible: root.isVertical && root.hasActivePrivacy + + Item { + width: 18 + height: 18 + visible: PrivacyService.microphoneActive + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + name: { + const sourceAudio = AudioService.source?.audio + const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0 + if (muted) return "mic_off" + return "mic" + } + size: Theme.iconSizeSmall + color: Theme.error + filled: true + anchors.centerIn: parent + } + } + + Item { + width: 18 + height: 18 + visible: PrivacyService.cameraActive + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + name: "camera_video" + size: Theme.iconSizeSmall + color: Theme.surfaceText + filled: true + anchors.centerIn: parent + } + + Rectangle { + width: 6 + height: 6 + radius: 3 + color: Theme.error + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: -2 + anchors.topMargin: -1 + } + } + + Item { + width: 18 + height: 18 + visible: PrivacyService.screensharingActive + anchors.horizontalCenter: parent.horizontalCenter + + DankIcon { + name: "screen_share" + size: Theme.iconSizeSmall + color: Theme.warning + filled: true + anchors.centerIn: parent + } + } + } + + Row { + anchors.centerIn: parent + spacing: Theme.spacingXS + visible: !root.isVertical && root.hasActivePrivacy + + Item { + width: 18 + height: 18 + visible: PrivacyService.microphoneActive + anchors.verticalCenter: parent.verticalCenter + + DankIcon { + name: { + const sourceAudio = AudioService.source?.audio + const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0 + if (muted) return "mic_off" + return "mic" + } + size: Theme.iconSizeSmall + color: Theme.error + filled: true + anchors.centerIn: parent + } + } + + Item { + width: 18 + height: 18 + visible: PrivacyService.cameraActive + anchors.verticalCenter: parent.verticalCenter + + 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.verticalCenter: parent.verticalCenter + + DankIcon { + name: "screen_share" + size: Theme.iconSizeSmall + color: Theme.warning + filled: true + anchors.centerIn: parent + } + } + } } MouseArea { - // Privacy indicator click handler - id: privacyArea - + z: -1 anchors.fill: parent hoverEnabled: hasActivePrivacy enabled: hasActivePrivacy @@ -47,151 +188,8 @@ 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: { - const sourceAudio = AudioService.source?.audio - const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0 - if (muted) return "mic_off" - return "mic" - } - size: Theme.iconSizeSmall - color: Theme.error - filled: true - anchors.centerIn: parent - } - - } - - Item { - width: 18 - height: 18 - visible: PrivacyService.cameraActive - anchors.horizontalCenter: parent.horizontalCenter - - DankIcon { - name: "camera_video" - size: Theme.iconSizeSmall - color: Theme.surfaceText - filled: true - anchors.centerIn: parent - } - - Rectangle { - width: 6 - height: 6 - radius: 3 - color: Theme.error - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: -2 - anchors.topMargin: -1 - } - - } - - Item { - width: 18 - height: 18 - visible: PrivacyService.screensharingActive - anchors.horizontalCenter: parent.horizontalCenter - - DankIcon { - name: "screen_share" - size: Theme.iconSizeSmall - color: Theme.warning - filled: true - anchors.centerIn: parent - } - - } - - } - - Row { - anchors.centerIn: parent - spacing: Theme.spacingXS - visible: !root.isVertical && hasActivePrivacy - - Item { - width: 18 - height: 18 - visible: PrivacyService.microphoneActive - anchors.verticalCenter: parent.verticalCenter - - DankIcon { - name: { - const sourceAudio = AudioService.source?.audio - const muted = !sourceAudio || sourceAudio.muted || sourceAudio.volume === 0.0 - if (muted) return "mic_off" - return "mic" - } - size: Theme.iconSizeSmall - color: Theme.error - filled: true - anchors.centerIn: parent - } - - } - - Item { - width: 18 - height: 18 - visible: PrivacyService.cameraActive - anchors.verticalCenter: parent.verticalCenter - - 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.verticalCenter: parent.verticalCenter - - DankIcon { - name: "screen_share" - size: Theme.iconSizeSmall - color: Theme.warning - filled: true - anchors.centerIn: parent - } - - } - - } - Rectangle { id: tooltip - width: tooltipText.contentWidth + Theme.spacingM * 2 height: tooltipText.contentHeight + Theme.spacingS * 2 radius: Theme.cornerRadius @@ -206,7 +204,6 @@ Rectangle { StyledText { id: tooltipText - anchors.centerIn: parent text: PrivacyService.getPrivacySummary() font.pixelSize: Theme.barTextSize(barThickness) @@ -232,9 +229,7 @@ Rectangle { duration: Theme.shortDuration easing.type: Theme.standardEasing } - } - } Behavior on width { @@ -244,7 +239,6 @@ Rectangle { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } - } Behavior on height { @@ -254,7 +248,5 @@ Rectangle { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } - } - } diff --git a/Modules/DankBar/Widgets/RamMonitor.qml b/Modules/DankBar/Widgets/RamMonitor.qml index 1e599137..c59b4e29 100644 --- a/Modules/DankBar/Widgets/RamMonitor.qml +++ b/Modules/DankBar/Widgets/RamMonitor.qml @@ -1,37 +1,19 @@ import QtQuick import QtQuick.Controls import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool showPercentage: true property bool showIcon: true property var toggleProcessList - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real barThickness: 48 - property real widgetThickness: 30 + property var popoutTarget: null property var widgetData: null property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) - - width: isVertical ? widgetThickness : (ramContent.implicitWidth + horizontalPadding * 2) - height: isVertical ? (ramColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = ramArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Component.onCompleted: { DgopService.addRef(["memory"]); @@ -40,120 +22,123 @@ Rectangle { DgopService.removeRef(["memory"]); } - MouseArea { - id: ramArea + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : ramContent.implicitWidth + implicitHeight: root.isVerticalOrientation ? ramColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) + Column { + id: ramColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 + + DankIcon { + name: "developer_board" + size: Theme.barIconSize(root.barThickness) + 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.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Row { + id: ramContent + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: 3 + + DankIcon { + name: "developer_board" + size: Theme.barIconSize(root.barThickness) + color: { + if (DgopService.memoryUsage > 90) { + return Theme.tempDanger; + } + + if (DgopService.memoryUsage > 75) { + return Theme.tempWarning; + } + + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: { + if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) { + return "--%"; + } + + return DgopService.memoryUsage.toFixed(0) + "%"; + } + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + elide: Text.ElideNone + + StyledTextMetrics { + id: ramBaseline + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + text: "100%" + } + + width: root.minimumWidth ? Math.max(ramBaseline.width, paintedWidth) : paintedWidth + + Behavior on width { + NumberAnimation { + duration: 120 + easing.type: Easing.OutCubic + } + } + } + } + } + } + + MouseArea { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } DgopService.setSortBy("memory"); if (root.toggleProcessList) { root.toggleProcessList(); } - } } - - Column { - id: ramColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: "developer_board" - size: Theme.barIconSize(barThickness) - 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.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } - - Row { - id: ramContent - visible: !root.isVertical - anchors.centerIn: parent - spacing: 3 - - DankIcon { - name: "developer_board" - size: Theme.barIconSize(barThickness) - color: { - if (DgopService.memoryUsage > 90) { - return Theme.tempDanger; - } - - if (DgopService.memoryUsage > 75) { - return Theme.tempWarning; - } - - return Theme.surfaceText; - } - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - if (DgopService.memoryUsage === undefined || DgopService.memoryUsage === null || DgopService.memoryUsage === 0) { - return "--%"; - } - - return DgopService.memoryUsage.toFixed(0) + "%"; - } - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - elide: Text.ElideNone - - StyledTextMetrics { - id: ramBaseline - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - text: "100%" - } - - width: root.minimumWidth ? Math.max(ramBaseline.width, paintedWidth) : paintedWidth - - Behavior on width { - NumberAnimation { - duration: 120 - easing.type: Easing.OutCubic - } - } - } - - } - } diff --git a/Modules/DankBar/Widgets/RunningApps.qml b/Modules/DankBar/Widgets/RunningApps.qml index dfe0413f..8feea3cd 100644 --- a/Modules/DankBar/Widgets/RunningApps.qml +++ b/Modules/DankBar/Widgets/RunningApps.qml @@ -7,7 +7,7 @@ import qs.Common import qs.Services import qs.Widgets -Rectangle { +Item { id: root property bool isVertical: axis?.isVertical ?? false @@ -66,22 +66,29 @@ Rectangle { } } - width: isVertical ? widgetThickness : calculatedSize - height: isVertical ? calculatedSize : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + width: isVertical ? barThickness : calculatedSize + height: isVertical ? calculatedSize : barThickness visible: windowCount > 0 - clip: false - color: { - if (windowCount === 0) { - return "transparent"; - } - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } + Rectangle { + id: visualBackground + width: root.isVertical ? root.widgetThickness : root.calculatedSize + height: root.isVertical ? root.calculatedSize : root.widgetThickness + anchors.centerIn: parent + radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + clip: false + color: { + if (windowCount === 0) { + return "transparent"; + } - const baseColor = Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); + if (SettingsData.dankBarNoBackground) { + return "transparent"; + } + + const baseColor = Theme.widgetBaseBackgroundColor; + return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); + } } MouseArea { @@ -210,12 +217,16 @@ Rectangle { } return appName + (windowTitle ? " • " + windowTitle : "") } + readonly property real visualWidth: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120) - width: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120) - height: 24 + width: visualWidth + height: root.barThickness Rectangle { - anchors.fill: parent + id: visualContent + width: delegateItem.visualWidth + height: 24 + anchors.centerIn: parent radius: Theme.cornerRadius color: { if (isFocused) { @@ -237,8 +248,6 @@ Rectangle { } } - } - // App icon IconImage { id: iconImg @@ -334,10 +343,10 @@ Rectangle { elide: Text.ElideRight maximumLineCount: 1 } + } MouseArea { id: mouseArea - anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -442,12 +451,16 @@ Rectangle { } return appName + (windowTitle ? " • " + windowTitle : "") } + readonly property real visualWidth: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120) - width: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120) + width: root.barThickness height: 24 Rectangle { - anchors.fill: parent + id: visualContent + width: delegateItem.visualWidth + height: 24 + anchors.centerIn: parent radius: Theme.cornerRadius color: { if (isFocused) { @@ -469,8 +482,6 @@ Rectangle { } } - } - IconImage { id: iconImg anchors.left: parent.left @@ -563,10 +574,10 @@ Rectangle { elide: Text.ElideRight maximumLineCount: 1 } + } MouseArea { id: mouseArea - anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor diff --git a/Modules/DankBar/Widgets/SystemTrayBar.qml b/Modules/DankBar/Widgets/SystemTrayBar.qml index cf7d964d..e6d2a0e9 100644 --- a/Modules/DankBar/Widgets/SystemTrayBar.qml +++ b/Modules/DankBar/Widgets/SystemTrayBar.qml @@ -7,7 +7,7 @@ import Quickshell.Widgets import qs.Common import qs.Widgets -Rectangle { +Item { id: root property bool isVertical: axis?.isVertical ?? false @@ -15,6 +15,7 @@ Rectangle { property var parentWindow: null property var parentScreen: null property real widgetThickness: 30 + property real barThickness: 48 property bool isAtBottom: false readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS readonly property var hiddenTrayIds: { @@ -31,24 +32,33 @@ Rectangle { }) } readonly property int calculatedSize: visibleTrayItems.length > 0 ? visibleTrayItems.length * 24 + horizontalPadding * 2 : 0 + readonly property real visualWidth: isVertical ? widgetThickness : calculatedSize + readonly property real visualHeight: isVertical ? calculatedSize : widgetThickness - width: isVertical ? widgetThickness : calculatedSize - height: isVertical ? calculatedSize : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (visibleTrayItems.length === 0) { - return "transparent"; - } - - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } + width: isVertical ? barThickness : visualWidth + height: isVertical ? visualHeight : barThickness visible: visibleTrayItems.length > 0 + Rectangle { + id: visualBackground + width: root.visualWidth + height: root.visualHeight + anchors.centerIn: parent + radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + color: { + if (visibleTrayItems.length === 0) { + return "transparent"; + } + + if (SettingsData.dankBarNoBackground) { + return "transparent"; + } + + const baseColor = Theme.widgetBaseBackgroundColor; + return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); + } + } + Loader { id: layoutLoader anchors.centerIn: parent @@ -64,81 +74,81 @@ Rectangle { model: root.visibleTrayItems 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; + id: delegateRoot + 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]; - let fileName = name.substring(name.lastIndexOf("/") + 1); - if (fileName.startsWith("dropboxstatus")) { - fileName = `hicolor/16x16/status/${fileName}`; + const name = split[0]; + const path = split[1]; + let fileName = name.substring(name.lastIndexOf("/") + 1); + if (fileName.startsWith("dropboxstatus")) { + fileName = `hicolor/16x16/status/${fileName}`; + } + return `file://${path}/${fileName}`; } - return `file://${path}/${fileName}`; + if (icon.startsWith("/") && !icon.startsWith("file://")) { + return `file://${icon}`; + } + return icon; } - if (icon.startsWith("/") && !icon.startsWith("file://")) { - return `file://${icon}`; - } - return icon; + return ""; } - return ""; - } - width: 24 - height: 24 + width: 24 + height: root.barThickness - Rectangle { - anchors.fill: parent - radius: Theme.cornerRadius - color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent" + Rectangle { + id: visualContent + width: 24 + height: 24 + anchors.centerIn: 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; + IconImage { + anchors.centerIn: parent + width: 16 + height: 16 + source: delegateRoot.iconSource + asynchronous: true + smooth: true + mipmap: true } + } - if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) { - trayItem.activate(); - return ; - } - if (trayItem.hasMenu) { - root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + MouseArea { + id: trayItemArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: (mouse) => { + if (!delegateRoot.trayItem) { + return; + } + + if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) { + delegateRoot.trayItem.activate(); + return ; + } + if (delegateRoot.trayItem.hasMenu) { + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + } } } } - } - - } } } @@ -151,79 +161,81 @@ Rectangle { model: root.visibleTrayItems 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; + id: delegateRoot + 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]; - let fileName = name.substring(name.lastIndexOf("/") + 1); - if (fileName.startsWith("dropboxstatus")) { - fileName = `hicolor/16x16/status/${fileName}`; + const name = split[0]; + const path = split[1]; + let fileName = name.substring(name.lastIndexOf("/") + 1); + if (fileName.startsWith("dropboxstatus")) { + fileName = `hicolor/16x16/status/${fileName}`; + } + return `file://${path}/${fileName}`; } - return `file://${path}/${fileName}`; + if (icon.startsWith("/") && !icon.startsWith("file://")) { + return `file://${icon}`; + } + return icon; } - if (icon.startsWith("/") && !icon.startsWith("file://")) { - return `file://${icon}`; - } - return icon; + return ""; } - return ""; - } - width: 24 - height: 24 + width: root.barThickness + height: 24 - Rectangle { - anchors.fill: parent - radius: Theme.cornerRadius - color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent" - } + Rectangle { + id: visualContent + width: 24 + height: 24 + anchors.centerIn: 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; + IconImage { + anchors.centerIn: parent + width: 16 + height: 16 + source: delegateRoot.iconSource + asynchronous: true + smooth: true + mipmap: true } + } - if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) { - trayItem.activate(); - return ; - } - if (trayItem.hasMenu) { - root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + MouseArea { + id: trayItemArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: (mouse) => { + if (!delegateRoot.trayItem) { + return; + } + + if (mouse.button === Qt.LeftButton && !delegateRoot.trayItem.onlyMenu) { + delegateRoot.trayItem.activate(); + return ; + } + if (delegateRoot.trayItem.hasMenu) { + root.showForTrayItem(delegateRoot.trayItem, visualContent, parentScreen, root.isAtBottom, root.isVertical, root.axis); + } } } } - } - - } } } diff --git a/Modules/DankBar/Widgets/SystemUpdate.qml b/Modules/DankBar/Widgets/SystemUpdate.qml index 2a3d6652..cd86fc7b 100644 --- a/Modules/DankBar/Widgets/SystemUpdate.qml +++ b/Modules/DankBar/Widgets/SystemUpdate.qml @@ -1,158 +1,138 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null property bool isActive: false - property string section: "right" - property var popupTarget: null - property var parentScreen: null - property real widgetThickness: 30 - property real barThickness: 48 - readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) readonly property bool hasUpdates: SystemUpdateService.updateCount > 0 readonly property bool isChecking: SystemUpdateService.isChecking - signal clicked() - Ref { service: SystemUpdateService } - width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2) - height: isVertical ? widgetThickness : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } + content: Component { + Item { + implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : updaterIcon.implicitWidth + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 - const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } - - DankIcon { - id: statusIcon - - anchors.centerIn: parent - visible: root.isVertical - name: { - if (isChecking) return "refresh"; - if (SystemUpdateService.hasError) return "error"; - if (hasUpdates) return "system_update_alt"; - return "check_circle"; - } - size: Theme.barIconSize(barThickness, -4) - 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 + DankIcon { + id: statusIcon + anchors.centerIn: parent + visible: root.isVerticalOrientation + name: { + if (root.isChecking) return "refresh" + if (SystemUpdateService.hasError) return "error" + if (root.hasUpdates) return "system_update_alt" + return "check_circle" + } + size: Theme.barIconSize(root.barThickness, -4) + color: { + if (SystemUpdateService.hasError) return Theme.error + if (root.hasUpdates) return Theme.primary + return 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.isVertical && root.hasUpdates && !root.isChecking - } + RotationAnimation { + id: rotationAnimation + target: statusIcon + property: "rotation" + from: 0 + to: 360 + duration: 1000 + running: root.isChecking + loops: Animation.Infinite - Row { - id: updaterIcon - - anchors.centerIn: parent - spacing: Theme.spacingXS - visible: !root.isVertical - - DankIcon { - id: statusIconHorizontal - - anchors.verticalCenter: parent.verticalCenter - name: { - if (isChecking) return "refresh"; - if (SystemUpdateService.hasError) return "error"; - if (hasUpdates) return "system_update_alt"; - return "check_circle"; - } - size: Theme.barIconSize(barThickness, -4) - color: { - if (SystemUpdateService.hasError) return Theme.error; - if (hasUpdates) return Theme.primary; - return (updaterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText); - } - - RotationAnimation { - id: rotationAnimationHorizontal - target: statusIconHorizontal - property: "rotation" - from: 0 - to: 360 - duration: 1000 - running: isChecking - loops: Animation.Infinite - - onRunningChanged: { - if (!running) { - statusIconHorizontal.rotation = 0 + onRunningChanged: { + if (!running) { + statusIcon.rotation = 0 + } } } } - } - StyledText { - id: countText + 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.isVerticalOrientation && root.hasUpdates && !root.isChecking + } - anchors.verticalCenter: parent.verticalCenter - text: SystemUpdateService.updateCount.toString() - font.pixelSize: Theme.barTextSize(barThickness) - font.weight: Font.Medium - color: Theme.surfaceText - visible: hasUpdates && !isChecking + Row { + id: updaterIcon + anchors.centerIn: parent + spacing: Theme.spacingXS + visible: !root.isVerticalOrientation + + DankIcon { + id: statusIconHorizontal + anchors.verticalCenter: parent.verticalCenter + name: { + if (root.isChecking) return "refresh" + if (SystemUpdateService.hasError) return "error" + if (root.hasUpdates) return "system_update_alt" + return "check_circle" + } + size: Theme.barIconSize(root.barThickness, -4) + color: { + if (SystemUpdateService.hasError) return Theme.error + if (root.hasUpdates) return Theme.primary + return root.isActive ? Theme.primary : Theme.surfaceText + } + + RotationAnimation { + id: rotationAnimationHorizontal + target: statusIconHorizontal + property: "rotation" + from: 0 + to: 360 + duration: 1000 + running: root.isChecking + loops: Animation.Infinite + + onRunningChanged: { + if (!running) { + statusIconHorizontal.rotation = 0 + } + } + } + } + + StyledText { + id: countText + anchors.verticalCenter: parent.verticalCenter + text: SystemUpdateService.updateCount.toString() + font.pixelSize: Theme.barTextSize(root.barThickness) + font.weight: Font.Medium + color: Theme.surfaceText + visible: root.hasUpdates && !root.isChecking + } + } } } MouseArea { - id: updaterArea - + z: 1 anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } - root.clicked(); + root.clicked() } } - -} \ No newline at end of file +} diff --git a/Modules/DankBar/Widgets/Vpn.qml b/Modules/DankBar/Widgets/Vpn.qml index e7cf7f9c..13e92a58 100644 --- a/Modules/DankBar/Widgets/Vpn.qml +++ b/Modules/DankBar/Widgets/Vpn.qml @@ -1,46 +1,35 @@ import QtQuick import Quickshell import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root Ref { service: VpnService } - 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)) + property var popoutTarget: null 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"; + content: Component { + Item { + implicitWidth: root.widgetThickness - root.horizontalPadding * 2 + implicitHeight: root.widgetThickness - root.horizontalPadding * 2 + + DankIcon { + id: icon + + name: VpnService.isBusy ? "sync" : (VpnService.connected ? "vpn_lock" : "vpn_key_off") + size: Theme.barIconSize(root.barThickness, -4) + color: VpnService.connected ? Theme.primary : Theme.surfaceText + anchors.centerIn: parent + } } - - 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.barIconSize(barThickness, -4) - color: VpnService.connected ? Theme.primary : Theme.surfaceText - anchors.centerIn: parent } Loader { @@ -55,17 +44,18 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton onPressed: { - if (popupTarget && popupTarget.setTriggerPosition) { - const globalPos = mapToGlobal(0, 0) + if (popoutTarget && popoutTarget.setTriggerPosition) { + const globalPos = root.visualContent.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) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) + popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } root.toggleVpnPopup(); } onEntered: { - if (root.parentScreen && !(popupTarget && popupTarget.shouldBeVisible)) { + if (root.parentScreen && !(popoutTarget && popoutTarget.shouldBeVisible)) { tooltipLoader.active = true if (tooltipLoader.item) { let tooltipText = "" @@ -80,7 +70,7 @@ Rectangle { } } - if (root.isVertical) { + if (root.isVerticalOrientation) { const globalPos = mapToGlobal(width / 2, height / 2) const screenX = root.parentScreen ? root.parentScreen.x : 0 const screenY = root.parentScreen ? root.parentScreen.y : 0 @@ -103,5 +93,4 @@ Rectangle { tooltipLoader.active = false } } - } diff --git a/Modules/DankBar/Widgets/Weather.qml b/Modules/DankBar/Widgets/Weather.qml index 421d8180..4bcc9adb 100644 --- a/Modules/DankBar/Widgets/Weather.qml +++ b/Modules/DankBar/Widgets/Weather.qml @@ -1,120 +1,81 @@ import QtQuick import qs.Common +import qs.Modules.Plugins import qs.Services import qs.Widgets -Rectangle { +BasePill { id: root - property bool isVertical: axis?.isVertical ?? false - property var axis: null - 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 clicked() - visible: SettingsData.weatherEnabled - width: isVertical ? widgetThickness : (visible ? Math.min(100, weatherRow.implicitWidth + horizontalPadding * 2) : 0) - height: isVertical ? (weatherColumn.implicitHeight + horizontalPadding * 2) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent"; - } - - const baseColor = weatherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); - } Ref { service: WeatherService } - Column { - id: weatherColumn - visible: root.isVertical - anchors.centerIn: parent - spacing: 1 - - DankIcon { - name: WeatherService.getWeatherIcon(WeatherService.weather.wCode) - size: Theme.barIconSize(barThickness, -6) - 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; + content: Component { + Item { + implicitWidth: { + if (!SettingsData.weatherEnabled) return 0 + if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2 + return Math.min(100 - root.horizontalPadding * 2, weatherRow.implicitWidth) } - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - } - } + implicitHeight: root.isVerticalOrientation ? weatherColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2) - Row { - id: weatherRow + Column { + id: weatherColumn + visible: root.isVerticalOrientation + anchors.centerIn: parent + spacing: 1 - visible: !root.isVertical - anchors.centerIn: parent - spacing: Theme.spacingXS - - DankIcon { - name: WeatherService.getWeatherIcon(WeatherService.weather.wCode) - size: Theme.barIconSize(barThickness, -6) - color: Theme.primary - anchors.verticalCenter: parent.verticalCenter - } - - StyledText { - text: { - const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp; - if (temp === undefined || temp === null || temp === 0) { - return "--°" + (SettingsData.useFahrenheit ? "F" : "C"); + DankIcon { + name: WeatherService.getWeatherIcon(WeatherService.weather.wCode) + size: Theme.barIconSize(root.barThickness, -6) + color: Theme.primary + anchors.horizontalCenter: parent.horizontalCenter } - return temp + "°" + (SettingsData.useFahrenheit ? "F" : "C"); + 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.barTextSize(root.barThickness) + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + } } - font.pixelSize: Theme.barTextSize(barThickness) - color: Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - } - } + Row { + id: weatherRow + visible: !root.isVerticalOrientation + anchors.centerIn: parent + spacing: Theme.spacingXS - MouseArea { - id: weatherArea + DankIcon { + name: WeatherService.getWeatherIcon(WeatherService.weather.wCode) + size: Theme.barIconSize(root.barThickness, -6) + color: Theme.primary + anchors.verticalCenter: parent.verticalCenter + } - 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) + StyledText { + text: { + const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp; + if (temp === undefined || temp === null || temp === 0) { + return "--°" + (SettingsData.useFahrenheit ? "F" : "C"); + } + + return temp + "°" + (SettingsData.useFahrenheit ? "F" : "C"); + } + font.pixelSize: Theme.barTextSize(root.barThickness) + color: Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } } - root.clicked(); } } - - - Behavior on width { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.standardEasing - } - - } - } diff --git a/Modules/DankBar/Widgets/WorkspaceSwitcher.qml b/Modules/DankBar/Widgets/WorkspaceSwitcher.qml index f1432a17..7d88b455 100644 --- a/Modules/DankBar/Widgets/WorkspaceSwitcher.qml +++ b/Modules/DankBar/Widgets/WorkspaceSwitcher.qml @@ -7,7 +7,7 @@ import qs.Common import qs.Services import qs.Widgets -Rectangle { +Item { id: root property bool isVertical: axis?.isVertical ?? false @@ -16,6 +16,7 @@ Rectangle { property real widgetHeight: 30 property real barThickness: 48 property var hyprlandOverviewLoader: null + property var parentScreen: null readonly property var sortedToplevels: { return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name); } @@ -201,9 +202,9 @@ Rectangle { return currentMonitor.activeWorkspace?.id ?? 1 } - readonly property real padding: isVertical - ? Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) - : (widgetHeight - workspaceRow.implicitHeight) / 2 + readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) + readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2) + readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight function getRealWorkspaces() { return root.workspaceList.filter(ws => { @@ -232,17 +233,24 @@ Rectangle { } } - width: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2) - height: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) - return "transparent" - const baseColor = Theme.widgetBaseBackgroundColor - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) - } + width: isVertical ? barThickness : visualWidth + height: isVertical ? visualHeight : barThickness visible: CompositorService.isNiri || CompositorService.isHyprland + Rectangle { + id: visualBackground + width: root.visualWidth + height: root.visualHeight + anchors.centerIn: parent + radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + color: { + if (SettingsData.dankBarNoBackground) + return "transparent" + const baseColor = Theme.widgetBaseBackgroundColor + return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) + } + } + MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton @@ -357,7 +365,7 @@ Rectangle { Repeater { model: root.workspaceList - Rectangle { + Item { id: delegateRoot property bool isActive: { @@ -389,6 +397,33 @@ Rectangle { property bool loadedHasIcon: false property var loadedIcons: [] + readonly property real visualWidth: { + if (root.isVertical) { + return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5 + } else { + 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 * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7 + return baseWidth + iconsWidth + } + return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7 + } + } + readonly property real visualHeight: { + if (root.isVertical) { + if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) { + const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons) + const iconsHeight = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0) + const baseHeight = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7 + return baseHeight + iconsHeight + } + return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7 + } else { + return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5 + } + } + Timer { id: dataUpdateTimer interval: 50 @@ -430,92 +465,54 @@ Rectangle { dataUpdateTimer.restart() } - width: { - if (root.isVertical) { - return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5; - } else { - 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 * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7; - return baseWidth + iconsWidth; - } - return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7; - } - } - height: { - if (root.isVertical) { - if (SettingsData.showWorkspaceApps && loadedIcons.length > 0) { - const numIcons = Math.min(loadedIcons.length, SettingsData.maxWorkspaceIcons); - const iconsHeight = numIcons * 18 + (numIcons > 0 ? (numIcons - 1) * Theme.spacingXS : 0); - const baseHeight = isActive ? root.widgetHeight * 0.9 + Theme.spacingXS : root.widgetHeight * 0.7; - return baseHeight + iconsHeight; - } - return isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7; - } else { - return SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5; - } - } - radius: Theme.cornerRadius - color: isActive ? Theme.primary : isUrgent ? Theme.error : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha + width: root.isVertical ? root.barThickness : visualWidth + height: root.isVertical ? visualHeight : root.barThickness - border.width: isUrgent && !isActive ? 2 : 0 - border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0) + Rectangle { + id: visualContent + width: delegateRoot.visualWidth + height: delegateRoot.visualHeight + anchors.centerIn: parent + radius: Theme.cornerRadius + color: isActive ? Theme.primary : isUrgent ? Theme.error : isPlaceholder ? Theme.surfaceTextLight : isHovered ? Theme.outlineButton : Theme.surfaceTextAlpha - Behavior on width { - enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } + border.width: isUrgent && !isActive ? 2 : 0 + border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0) - Behavior on height { - enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - Behavior on color { - ColorAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - - Behavior on border.width { - NumberAnimation { - duration: Theme.shortDuration - easing.type: Theme.emphasizedEasing - } - } - - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: !isPlaceholder - cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor - enabled: !isPlaceholder - onClicked: { - if (isPlaceholder) { - return - } - - if (CompositorService.isNiri) { - NiriService.switchToWorkspace(modelData - 1) - } else if (CompositorService.isHyprland && modelData?.id) { - Hyprland.dispatch(`workspace ${modelData.id}`) + Behavior on width { + enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } - } - Loader { - id: appIconsLoader - anchors.fill: parent - active: SettingsData.showWorkspaceApps + Behavior on height { + enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing + } + } + + Behavior on color { + ColorAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing + } + } + + Behavior on border.width { + NumberAnimation { + duration: Theme.shortDuration + easing.type: Theme.emphasizedEasing + } + } + + Loader { + id: appIconsLoader + anchors.fill: parent + active: SettingsData.showWorkspaceApps sourceComponent: Item { Loader { id: contentRow @@ -716,8 +713,27 @@ Rectangle { } } } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: !isPlaceholder + cursorShape: isPlaceholder ? Qt.ArrowCursor : Qt.PointingHandCursor + enabled: !isPlaceholder + onClicked: { + if (isPlaceholder) { + return + } + + if (CompositorService.isNiri) { + NiriService.switchToWorkspace(modelData - 1) + } else if (CompositorService.isHyprland && modelData?.id) { + Hyprland.dispatch(`workspace ${modelData.id}`) + } + } + } - // --- LOGIC / TRIGGERS --- Component.onCompleted: updateAllData() Connections { diff --git a/Modules/Plugins/BasePill.qml b/Modules/Plugins/BasePill.qml index 618f2e49..3c73286e 100644 --- a/Modules/Plugins/BasePill.qml +++ b/Modules/Plugins/BasePill.qml @@ -3,7 +3,7 @@ import qs.Common import qs.Services import qs.Widgets -Rectangle { +Item { id: root property var axis: null @@ -12,44 +12,54 @@ Rectangle { property var parentScreen: null property real widgetThickness: 30 property real barThickness: 48 - property bool isVerticalOrientation: false property alias content: contentLoader.sourceComponent - + property bool isVerticalOrientation: axis?.isVertical ?? false readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30)) + readonly property real visualWidth: isVerticalOrientation ? widgetThickness : (contentLoader.item ? (contentLoader.item.implicitWidth + horizontalPadding * 2) : 0) + readonly property real visualHeight: isVerticalOrientation ? (contentLoader.item ? (contentLoader.item.implicitHeight + horizontalPadding * 2) : 0) : widgetThickness + readonly property alias visualContent: visualContent signal clicked() - width: isVerticalOrientation ? widgetThickness : contentLoader.item ? (contentLoader.item.implicitWidth + horizontalPadding * 2) : 0 - height: isVerticalOrientation ? (contentLoader.item ? (contentLoader.item.implicitHeight + horizontalPadding * 2) : 0) : widgetThickness - radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius - color: { - if (SettingsData.dankBarNoBackground) { - return "transparent" + width: isVerticalOrientation ? barThickness : visualWidth + height: isVerticalOrientation ? visualHeight : barThickness + + Rectangle { + id: visualContent + width: root.visualWidth + height: root.visualHeight + anchors.centerIn: parent + radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius + color: { + if (SettingsData.dankBarNoBackground) { + return "transparent" + } + + const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor + return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) } - const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor - return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) - } - - Loader { - id: contentLoader - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter + Loader { + id: contentLoader + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } } MouseArea { id: mouseArea + z: -1 anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onPressed: { + onClicked: { + root.clicked() if (popoutTarget && popoutTarget.setTriggerPosition) { const globalPos = mapToGlobal(0, 0) const currentScreen = parentScreen || Screen - const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) + const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) } - root.clicked() } } }