1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

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
This commit is contained in:
bbedward
2025-10-22 00:12:41 -04:00
parent 8ab25ef8e4
commit 951136bc4c
28 changed files with 2507 additions and 2923 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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