1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 22:15:38 -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" 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.fill: parent
anchors.leftMargin: !barWindow.isVertical ? Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) : SettingsData.dankBarInnerPadding / 2 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.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.topMargin: !barWindow.isVertical ? 0 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8)
anchors.bottomMargin: !barWindow.isVertical ? SettingsData.dankBarInnerPadding / 2 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8) anchors.bottomMargin: !barWindow.isVertical ? 0 : Math.max(Theme.spacingXS, SettingsData.dankBarInnerPadding * 0.8)
clip: true clip: false
property int componentMapRevision: 0 property int componentMapRevision: 0
@@ -801,6 +801,7 @@ Item {
ClipboardButton { ClipboardButton {
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) section: topBarContent.getWidgetSection(parent)
parentScreen: barWindow.screen parentScreen: barWindow.screen
onClicked: { onClicked: {
@@ -817,7 +818,7 @@ Item {
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
section: topBarContent.getWidgetSection(parent) section: topBarContent.getWidgetSection(parent)
popupTarget: appDrawerLoader.item popoutTarget: appDrawerLoader.item
parentScreen: barWindow.screen parentScreen: barWindow.screen
hyprlandOverviewLoader: root.hyprlandOverviewLoader hyprlandOverviewLoader: root.hyprlandOverviewLoader
onClicked: { onClicked: {
@@ -831,8 +832,11 @@ Item {
id: workspaceSwitcherComponent id: workspaceSwitcherComponent
WorkspaceSwitcher { WorkspaceSwitcher {
axis: barWindow.axis
screenName: barWindow.screenName screenName: barWindow.screenName
widgetHeight: barWindow.widgetThickness widgetHeight: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness
parentScreen: barWindow.screen
hyprlandOverviewLoader: root.hyprlandOverviewLoader hyprlandOverviewLoader: root.hyprlandOverviewLoader
} }
} }
@@ -841,8 +845,10 @@ Item {
id: focusedWindowComponent id: focusedWindowComponent
FocusedApp { FocusedApp {
axis: barWindow.axis
availableWidth: topBarContent.leftToMediaGap availableWidth: topBarContent.leftToMediaGap
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness
parentScreen: barWindow.screen parentScreen: barWindow.screen
} }
} }
@@ -862,11 +868,12 @@ Item {
id: clockComponent id: clockComponent
Clock { Clock {
axis: barWindow.axis
compactMode: topBarContent.overlapping compactMode: topBarContent.overlapping
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
section: topBarContent.getWidgetSection(parent) || "center" section: topBarContent.getWidgetSection(parent) || "center"
popupTarget: { popoutTarget: {
dankDashPopoutLoader.active = true dankDashPopoutLoader.active = true
return dankDashPopoutLoader.item return dankDashPopoutLoader.item
} }
@@ -896,11 +903,12 @@ Item {
id: mediaComponent id: mediaComponent
Media { Media {
axis: barWindow.axis
compactMode: topBarContent.spacingTight || topBarContent.overlapping compactMode: topBarContent.spacingTight || topBarContent.overlapping
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
section: topBarContent.getWidgetSection(parent) || "center" section: topBarContent.getWidgetSection(parent) || "center"
popupTarget: { popoutTarget: {
dankDashPopoutLoader.active = true dankDashPopoutLoader.active = true
return dankDashPopoutLoader.item return dankDashPopoutLoader.item
} }
@@ -919,10 +927,11 @@ Item {
id: weatherComponent id: weatherComponent
Weather { Weather {
axis: barWindow.axis
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
section: topBarContent.getWidgetSection(parent) || "center" section: topBarContent.getWidgetSection(parent) || "center"
popupTarget: { popoutTarget: {
dankDashPopoutLoader.active = true dankDashPopoutLoader.active = true
return dankDashPopoutLoader.item return dankDashPopoutLoader.item
} }
@@ -965,8 +974,9 @@ Item {
CpuMonitor { CpuMonitor {
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item return processListPopoutLoader.item
} }
@@ -985,8 +995,9 @@ Item {
RamMonitor { RamMonitor {
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item return processListPopoutLoader.item
} }
@@ -1015,8 +1026,9 @@ Item {
CpuTemperature { CpuTemperature {
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item return processListPopoutLoader.item
} }
@@ -1035,8 +1047,9 @@ Item {
GpuTemperature { GpuTemperature {
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
processListPopoutLoader.active = true processListPopoutLoader.active = true
return processListPopoutLoader.item return processListPopoutLoader.item
} }
@@ -1063,8 +1076,9 @@ Item {
isActive: notificationCenterLoader.item ? notificationCenterLoader.item.shouldBeVisible : false isActive: notificationCenterLoader.item ? notificationCenterLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
notificationCenterLoader.active = true notificationCenterLoader.active = true
return notificationCenterLoader.item return notificationCenterLoader.item
} }
@@ -1083,8 +1097,9 @@ Item {
batteryPopupVisible: batteryPopoutLoader.item ? batteryPopoutLoader.item.shouldBeVisible : false batteryPopupVisible: batteryPopoutLoader.item ? batteryPopoutLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
batteryPopoutLoader.active = true batteryPopoutLoader.active = true
return batteryPopoutLoader.item return batteryPopoutLoader.item
} }
@@ -1102,8 +1117,9 @@ Item {
Vpn { Vpn {
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
vpnPopoutLoader.active = true vpnPopoutLoader.active = true
return vpnPopoutLoader.item return vpnPopoutLoader.item
} }
@@ -1122,8 +1138,9 @@ Item {
isActive: controlCenterLoader.item ? controlCenterLoader.item.shouldBeVisible : false isActive: controlCenterLoader.item ? controlCenterLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
controlCenterLoader.active = true controlCenterLoader.active = true
return controlCenterLoader.item return controlCenterLoader.item
} }
@@ -1217,9 +1234,9 @@ Item {
id: notepadButtonComponent id: notepadButtonComponent
NotepadButton { NotepadButton {
isVertical: barWindow.isVertical
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
parentScreen: barWindow.screen parentScreen: barWindow.screen
} }
@@ -1246,8 +1263,9 @@ Item {
isActive: systemUpdateLoader.item ? systemUpdateLoader.item.shouldBeVisible : false isActive: systemUpdateLoader.item ? systemUpdateLoader.item.shouldBeVisible : false
widgetThickness: barWindow.widgetThickness widgetThickness: barWindow.widgetThickness
barThickness: barWindow.effectiveBarThickness barThickness: barWindow.effectiveBarThickness
axis: barWindow.axis
section: topBarContent.getWidgetSection(parent) || "right" section: topBarContent.getWidgetSection(parent) || "right"
popupTarget: { popoutTarget: {
systemUpdateLoader.active = true systemUpdateLoader.active = true
return systemUpdateLoader.item 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 QtQuick
import Quickshell.Services.UPower import Quickshell.Services.UPower
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: battery id: battery
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool batteryPopupVisible: false property bool batteryPopupVisible: false
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal toggleBatteryPopup() 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 visible: true
Column { content: Component {
id: batteryColumn Item {
visible: battery.isVertical implicitWidth: battery.isVerticalOrientation ? (battery.widgetThickness - battery.horizontalPadding * 2) : batteryContent.implicitWidth
anchors.centerIn: parent implicitHeight: battery.isVerticalOrientation ? batteryColumn.implicitHeight : (battery.widgetThickness - battery.horizontalPadding * 2)
spacing: 1
DankIcon { Column {
name: BatteryService.getBatteryIcon() id: batteryColumn
size: Theme.barIconSize(barThickness) visible: battery.isVerticalOrientation
color: { anchors.centerIn: parent
if (!BatteryService.batteryAvailable) { spacing: 1
return Theme.surfaceText
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) { StyledText {
return Theme.error 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 { Row {
text: BatteryService.batteryLevel.toString() id: batteryContent
font.pixelSize: Theme.barTextSize(barThickness) visible: !battery.isVerticalOrientation
font.weight: Font.Medium anchors.centerIn: parent
color: Theme.surfaceText spacing: SettingsData.dankBarNoBackground ? 1 : 2
anchors.horizontalCenter: parent.horizontalCenter
visible: BatteryService.batteryAvailable
}
}
Row { DankIcon {
id: batteryContent name: BatteryService.getBatteryIcon()
visible: !battery.isVertical size: Theme.barIconSize(battery.barThickness, -4)
anchors.centerIn: parent color: {
spacing: SettingsData.dankBarNoBackground ? 1 : 2 if (!BatteryService.batteryAvailable) {
return Theme.surfaceText;
}
DankIcon { if (BatteryService.isLowBattery && !BatteryService.isCharging) {
name: BatteryService.getBatteryIcon() return Theme.error;
size: Theme.barIconSize(barThickness, -4) }
color: {
if (!BatteryService.batteryAvailable) { if (BatteryService.isCharging || BatteryService.isPluggedIn) {
return Theme.surfaceText; return Theme.primary;
}
return Theme.surfaceText;
}
anchors.verticalCenter: parent.verticalCenter
} }
if (BatteryService.isLowBattery && !BatteryService.isCharging) { StyledText {
return Theme.error; 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 { MouseArea {
id: batteryArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = battery.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, battery.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
toggleBatteryPopup(); toggleBatteryPopup();
} }

View File

@@ -1,57 +1,25 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Widgets import qs.Widgets
Item { BasePill {
id: root id: root
property bool isActive: false property bool isActive: false
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "right"
property var clipboardHistoryModal: null 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 DankIcon {
height: widgetThickness anchors.centerIn: parent
name: "content_paste"
MouseArea { size: Theme.barIconSize(root.barThickness)
id: clipboardArea color: Theme.surfaceText
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
root.clicked()
}
}
Rectangle {
id: clipboardContent
anchors.fill: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent"
} }
const baseColor = clipboardArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
}
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
name: "content_paste"
size: Theme.barIconSize(barThickness)
color: Theme.surfaceText
} }
} }
} }

View File

@@ -1,270 +1,254 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool compactMode: false 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 signal clockClicked
width: isVertical ? widgetThickness : (clockRow.implicitWidth + horizontalPadding * 2) content: Component {
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
}
}
Item { Item {
width: 12 implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : clockRow.implicitWidth
height: Theme.spacingM implicitHeight: root.isVerticalOrientation ? clockColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
anchors.horizontalCenter: parent.horizontalCenter
Rectangle { Column {
width: 12 id: clockColumn
height: 1 visible: root.isVerticalOrientation
color: Theme.outlineButton
anchors.centerIn: parent anchors.centerIn: parent
} spacing: -2
}
Row { Row {
spacing: 0 spacing: 0
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
StyledText { StyledText {
text: { text: {
const locale = Qt.locale() if (SettingsData.use24HourClock) {
const dateFormatShort = locale.dateFormat(Locale.ShortFormat) return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(0)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M') } else {
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0') const hours = systemClock?.date?.getHours()
return value.charAt(0) 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.primary }
font.weight: Font.Light font.pixelSize: Theme.barTextSize(root.barThickness)
width: 9 color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter font.weight: Font.Normal
} width: 9
horizontalAlignment: Text.AlignHCenter
}
StyledText { StyledText {
text: { text: {
const locale = Qt.locale() if (SettingsData.use24HourClock) {
const dateFormatShort = locale.dateFormat(Locale.ShortFormat) return String(systemClock?.date?.getHours()).padStart(2, '0').charAt(1)
const dayFirst = dateFormatShort.indexOf('d') < dateFormatShort.indexOf('M') } else {
const value = dayFirst ? String(systemClock?.date?.getDate()).padStart(2, '0') : String(systemClock?.date?.getMonth() + 1).padStart(2, '0') const hours = systemClock?.date?.getHours()
return value.charAt(1) 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.primary }
font.weight: Font.Light font.pixelSize: Theme.barTextSize(root.barThickness)
width: 9 color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter font.weight: Font.Normal
} 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)
} }
return systemClock?.date?.toLocaleDateString(Qt.locale(), "ddd d") Row {
} spacing: 0
font.pixelSize: Theme.barTextSize(barThickness) anchors.horizontalCenter: parent.horizontalCenter
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: !SettingsData.clockCompactMode
}
}
SystemClock { StyledText {
id: systemClock text: String(systemClock?.date?.getMinutes()).padStart(2, '0').charAt(0)
precision: SystemClock.Seconds 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 { MouseArea {
id: clockMouseArea id: clockMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (root.popoutTarget && root.popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = root.parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, root.barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) root.popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen)
} }
root.clockClicked() root.clockClicked()
} }
} }
} }

View File

@@ -1,54 +1,36 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false property bool isActive: false
property string section: "right"
property 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) content: Component {
height: isVertical ? (colorPickerIcon.height + horizontalPadding * 2) : widgetThickness Item {
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius implicitWidth: root.widgetThickness - root.horizontalPadding * 2
color: { implicitHeight: root.widgetThickness - root.horizontalPadding * 2
if (SettingsData.dankBarNoBackground) {
return "transparent"; 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 { MouseArea {
id: colorPickerArea z: 1
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
root.colorPickerRequested(); root.colorPickerRequested()
} }
} }
signal colorPickerRequested()
} }

View File

@@ -1,266 +1,245 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false property bool isActive: false
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property var widgetData: null property var widgetData: null
property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon property bool showNetworkIcon: SettingsData.controlCenterShowNetworkIcon
property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon property bool showBluetoothIcon: SettingsData.controlCenterShowBluetoothIcon
property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon property bool showAudioIcon: SettingsData.controlCenterShowAudioIcon
property real 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) Column {
height: isVertical ? (controlColumn.implicitHeight + horizontalPadding * 2) : widgetThickness id: controlColumn
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius visible: root.isVerticalOrientation
color: { anchors.centerIn: parent
if (SettingsData.dankBarNoBackground) { spacing: Theme.spacingXS
return "transparent";
}
const baseColor = controlCenterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; DankIcon {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); name: {
} if (NetworkService.wifiToggling) {
return "sync"
}
Column { if (NetworkService.networkStatus === "ethernet") {
id: controlColumn return "lan"
visible: root.isVertical }
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon { return NetworkService.wifiSignalIcon
name: { }
if (NetworkService.wifiToggling) { size: Theme.barIconSize(root.barThickness)
return "sync" 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") { DankIcon {
return "lan" 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 Rectangle {
} width: audioIconV.implicitWidth + 4
size: Theme.barIconSize(barThickness) height: audioIconV.implicitHeight + 4
color: { color: "transparent"
if (NetworkService.wifiToggling) { anchors.horizontalCenter: parent.horizontalCenter
return Theme.primary visible: root.showAudioIcon
}
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.outlineButton DankIcon {
} id: audioIconV
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showNetworkIcon && NetworkService.networkAvailable
}
DankIcon { name: {
name: "bluetooth" if (AudioService.sink && AudioService.sink.audio) {
size: Theme.barIconSize(barThickness) if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
color: BluetoothService.connected ? Theme.primary : Theme.outlineButton return "volume_off"
anchors.horizontalCenter: parent.horizontalCenter } else if (AudioService.sink.audio.volume * 100 < 33) {
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled return "volume_down"
} } else {
return "volume_up"
Rectangle { }
width: audioIconV.implicitWidth + 4 }
height: audioIconV.implicitHeight + 4
color: "transparent"
anchors.horizontalCenter: parent.horizontalCenter
visible: root.showAudioIcon
DankIcon {
id: audioIconV
name: {
if (AudioService.sink && AudioService.sink.audio) {
if (AudioService.sink.audio.muted || AudioService.sink.audio.volume === 0) {
return "volume_off"
} else if (AudioService.sink.audio.volume * 100 < 33) {
return "volume_down"
} else {
return "volume_up" return "volume_up"
} }
size: Theme.barIconSize(root.barThickness)
color: Theme.surfaceText
anchors.centerIn: parent
} }
return "volume_up"
}
size: Theme.barIconSize(barThickness)
color: Theme.surfaceText
anchors.centerIn: parent
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
acceptedButtons: Qt.NoButton acceptedButtons: Qt.NoButton
onWheel: function(wheelEvent) { onWheel: function(wheelEvent) {
let delta = wheelEvent.angleDelta.y let delta = wheelEvent.angleDelta.y
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0 let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0
let newVolume let newVolume
if (delta > 0) { if (delta > 0) {
newVolume = Math.min(100, currentVolume + 5) newVolume = Math.min(100, currentVolume + 5)
} else { } else {
newVolume = Math.max(0, currentVolume - 5) newVolume = Math.max(0, currentVolume - 5)
} }
if (AudioService.sink && AudioService.sink.audio) { if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = false AudioService.sink.audio.muted = false
AudioService.sink.audio.volume = newVolume / 100 AudioService.sink.audio.volume = newVolume / 100
} }
wheelEvent.accepted = true 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";
} }
} }
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 anchors.centerIn: parent
} spacing: Theme.spacingXS
MouseArea { DankIcon {
id: audioWheelArea id: networkIcon
anchors.fill: parent name: {
hoverEnabled: true if (NetworkService.wifiToggling) {
acceptedButtons: Qt.NoButton return "sync";
onWheel: function(wheelEvent) { }
let delta = wheelEvent.angleDelta.y;
let currentVolume = (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0; if (NetworkService.networkStatus === "ethernet") {
let newVolume; return "lan";
if (delta > 0) { }
newVolume = Math.min(100, currentVolume + 5);
} else { return NetworkService.wifiSignalIcon;
newVolume = Math.max(0, currentVolume - 5);
} }
if (AudioService.sink && AudioService.sink.audio) { size: Theme.barIconSize(root.barThickness)
AudioService.sink.audio.muted = false; color: {
AudioService.sink.audio.volume = newVolume / 100; 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 { MouseArea {
id: controlCenterArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
root.clicked(); root.clicked()
} }
} }
} }

View File

@@ -1,37 +1,20 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property real barThickness: 48
property real widgetThickness: 30
property var widgetData: null property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true 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: { Component.onCompleted: {
DgopService.addRef(["cpu"]); DgopService.addRef(["cpu"]);
} }
@@ -39,120 +22,123 @@ Rectangle {
DgopService.removeRef(["cpu"]); DgopService.removeRef(["cpu"]);
} }
MouseArea { content: Component {
id: cpuArea 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 anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { if (root.toggleProcessList) {
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
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property real barThickness: 48
property real widgetThickness: 30
property var widgetData: null property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true 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: { Component.onCompleted: {
DgopService.addRef(["cpu"]); DgopService.addRef(["cpu"]);
} }
@@ -39,121 +22,123 @@ Rectangle {
DgopService.removeRef(["cpu"]); DgopService.removeRef(["cpu"]);
} }
MouseArea { content: Component {
id: cpuTempArea 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 anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { if (root.toggleProcessList) {
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
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var widgetData: 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 : "/" 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: { property var selectedMount: {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) { if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
return null return null
} }
// Force re-evaluation when mountPath changes
const currentMountPath = root.mountPath || "/" const currentMountPath = root.mountPath || "/"
// First try to find exact match
for (let i = 0; i < DgopService.diskMounts.length; i++) { for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === currentMountPath) { if (DgopService.diskMounts[i].mount === currentMountPath) {
return DgopService.diskMounts[i] return DgopService.diskMounts[i]
} }
} }
// Fallback to root
for (let i = 0; i < DgopService.diskMounts.length; i++) { for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === "/") { if (DgopService.diskMounts[i].mount === "/") {
return DgopService.diskMounts[i] return DgopService.diskMounts[i]
} }
} }
// Last resort - first mount
return DgopService.diskMounts[0] || null return DgopService.diskMounts[0] || null
} }
@@ -50,17 +41,6 @@ Rectangle {
return parseFloat(percentStr) || 0 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: { Component.onCompleted: {
DgopService.addRef(["diskmounts"]) DgopService.addRef(["diskmounts"])
} }
@@ -70,7 +50,6 @@ Rectangle {
Connections { Connections {
function onWidgetDataChanged() { function onWidgetDataChanged() {
// Force property re-evaluation by triggering change detection
root.mountPath = Qt.binding(() => { root.mountPath = Qt.binding(() => {
return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/" return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/"
}) })
@@ -82,21 +61,18 @@ Rectangle {
const currentMountPath = root.mountPath || "/" const currentMountPath = root.mountPath || "/"
// First try to find exact match
for (let i = 0; i < DgopService.diskMounts.length; i++) { for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === currentMountPath) { if (DgopService.diskMounts[i].mount === currentMountPath) {
return DgopService.diskMounts[i] return DgopService.diskMounts[i]
} }
} }
// Fallback to root
for (let i = 0; i < DgopService.diskMounts.length; i++) { for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === "/") { if (DgopService.diskMounts[i].mount === "/") {
return DgopService.diskMounts[i] return DgopService.diskMounts[i]
} }
} }
// Last resort - first mount
return DgopService.diskMounts[0] || null return DgopService.diskMounts[0] || null
}) })
} }
@@ -104,6 +80,116 @@ Rectangle {
target: SettingsData 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 { Loader {
id: tooltipLoader id: tooltipLoader
active: false active: false
@@ -111,11 +197,11 @@ Rectangle {
} }
MouseArea { MouseArea {
id: diskArea z: 1
anchors.fill: parent anchors.fill: parent
hoverEnabled: root.isVertical hoverEnabled: root.isVerticalOrientation
onEntered: { onEntered: {
if (root.isVertical && root.selectedMount) { if (root.isVerticalOrientation && root.selectedMount) {
tooltipLoader.active = true tooltipLoader.active = true
if (tooltipLoader.item) { if (tooltipLoader.item) {
const globalPos = mapToGlobal(width / 2, height / 2) const globalPos = mapToGlobal(width / 2, height / 2)
@@ -136,107 +222,4 @@ Rectangle {
tooltipLoader.active = false 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.Widgets
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property var parentScreen
property bool compactMode: SettingsData.focusedWindowCompactMode property bool compactMode: SettingsData.focusedWindowCompactMode
property int availableWidth: 400 property int availableWidth: 400
property real 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 maxNormalWidth: 456
readonly property int maxCompactWidth: 288 readonly property int maxCompactWidth: 288
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
@@ -93,148 +87,141 @@ Rectangle {
return activeWindow && activeWindow.title 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 visible: hasWindowsOnCurrentWorkspace
IconImage { content: Component {
id: appIcon Item {
anchors.centerIn: parent implicitWidth: {
width: 18 if (!root.hasWindowsOnCurrentWorkspace) return 0
height: 18 if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2
visible: root.isVertical && activeWindow && status === Image.Ready const baseWidth = contentRow.implicitWidth
source: { return compactMode ? Math.min(baseWidth, maxCompactWidth - root.horizontalPadding * 2) : Math.min(baseWidth, maxNormalWidth - root.horizontalPadding * 2)
if (!activeWindow || !activeWindow.appId) return ""
const moddedId = Paths.moddedAppId(activeWindow.appId)
if (moddedId.toLowerCase().includes("steam_app")) return ""
return Quickshell.iconPath(activeDesktopEntry?.icon, true)
}
smooth: true
mipmap: true
asynchronous: true
}
DankIcon {
anchors.centerIn: parent
size: 18
name: "sports_esports"
color: Theme.surfaceText
visible: {
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
const moddedId = Paths.moddedAppId(activeWindow.appId)
return moddedId.toLowerCase().includes("steam_app")
}
}
Text {
anchors.centerIn: parent
visible: {
if (!root.isVertical || !activeWindow || !activeWindow.appId) return false
if (appIcon.status === Image.Ready) return false
const moddedId = Paths.moddedAppId(activeWindow.appId)
return !moddedId.toLowerCase().includes("steam_app")
}
text: {
if (!activeWindow || !activeWindow.appId) return "?"
if (activeDesktopEntry && activeDesktopEntry.name) {
return activeDesktopEntry.name.charAt(0).toUpperCase()
} }
return activeWindow.appId.charAt(0).toUpperCase() implicitHeight: root.widgetThickness - root.horizontalPadding * 2
} clip: true
font.pixelSize: 10
color: Theme.surfaceText
font.weight: Font.Medium
}
Row { IconImage {
id: contentRow id: appIcon
anchors.centerIn: parent
anchors.centerIn: parent width: 18
spacing: Theme.spacingS height: 18
visible: !root.isVertical visible: root.isVerticalOrientation && activeWindow && status === Image.Ready
source: {
StyledText { if (!activeWindow || !activeWindow.appId) return ""
id: appText const moddedId = Paths.moddedAppId(activeWindow.appId)
if (moddedId.toLowerCase().includes("steam_app")) return ""
text: { return Quickshell.iconPath(activeDesktopEntry?.icon, true)
if (!activeWindow || !activeWindow.appId) {
return "";
} }
smooth: true
const desktopEntry = DesktopEntries.heuristicLookup(activeWindow.appId); mipmap: true
return desktopEntry && desktopEntry.name ? desktopEntry.name : activeWindow.appId; 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 { DankIcon {
text: "•" anchors.centerIn: parent
font.pixelSize: Theme.barTextSize(barThickness) size: 18
color: Theme.outlineButton name: "sports_esports"
anchors.verticalCenter: parent.verticalCenter color: Theme.surfaceText
visible: !compactMode && appText.text && titleText.text visible: {
} if (!root.isVerticalOrientation || !activeWindow || !activeWindow.appId) return false
const moddedId = Paths.moddedAppId(activeWindow.appId)
StyledText { return moddedId.toLowerCase().includes("steam_app")
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(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 { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: root.isVertical hoverEnabled: root.isVerticalOrientation
acceptedButtons: Qt.NoButton
onEntered: { onEntered: {
if (root.isVertical && activeWindow && activeWindow.appId && root.parentScreen) { if (root.isVerticalOrientation && activeWindow && activeWindow.appId && root.parentScreen) {
tooltipLoader.active = true tooltipLoader.active = true
if (tooltipLoader.item) { if (tooltipLoader.item) {
const globalPos = mapToGlobal(width / 2, height / 2) const globalPos = mapToGlobal(width / 2, height / 2)
@@ -266,14 +253,4 @@ Rectangle {
active: false active: false
sourceComponent: DankTooltip {} sourceComponent: DankTooltip {}
} }
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
} }

View File

@@ -1,26 +1,20 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property var widgetData: null property var widgetData: null
property real barThickness: 48
property real widgetThickness: 30
property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0 property int selectedGpuIndex: (widgetData && widgetData.selectedGpuIndex !== undefined) ? widgetData.selectedGpuIndex : 0
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true 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: { property real displayTemp: {
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) { if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
return 0; return 0;
@@ -34,7 +28,6 @@ Rectangle {
} }
function updateWidgetPciId(pciId) { function updateWidgetPciId(pciId) {
// Find and update this widget's pciId in the settings
const sections = ["left", "center", "right"]; const sections = ["left", "center", "right"];
for (let s = 0; s < sections.length; s++) { for (let s = 0; s < sections.length; s++) {
const sectionId = sections[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: { Component.onCompleted: {
DgopService.addRef(["gpu"]); DgopService.addRef(["gpu"]);
if (widgetData && widgetData.pciId) { if (widgetData && widgetData.pciId) {
@@ -92,12 +74,10 @@ Rectangle {
if (widgetData && widgetData.pciId) { if (widgetData && widgetData.pciId) {
DgopService.removeGpuPciId(widgetData.pciId); DgopService.removeGpuPciId(widgetData.pciId);
} }
} }
Connections { Connections {
function onWidgetDataChanged() { function onWidgetDataChanged() {
// Force property re-evaluation by triggering change detection
root.selectedGpuIndex = Qt.binding(() => { root.selectedGpuIndex = Qt.binding(() => {
return (root.widgetData && root.widgetData.selectedGpuIndex !== undefined) ? root.widgetData.selectedGpuIndex : 0; return (root.widgetData && root.widgetData.selectedGpuIndex !== undefined) ? root.widgetData.selectedGpuIndex : 0;
}); });
@@ -106,122 +86,126 @@ Rectangle {
target: SettingsData target: SettingsData
} }
MouseArea { content: Component {
id: gpuArea 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 anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
DgopService.setSortBy("cpu"); DgopService.setSortBy("cpu");
if (root.toggleProcessList) { if (root.toggleProcessList) {
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 { Timer {
id: autoSaveTimer id: autoSaveTimer
@@ -231,13 +215,10 @@ Rectangle {
if (DgopService.availableGpus && DgopService.availableGpus.length > 0) { if (DgopService.availableGpus && DgopService.availableGpus.length > 0) {
const firstGpu = DgopService.availableGpus[0]; const firstGpu = DgopService.availableGpus[0];
if (firstGpu && firstGpu.pciId) { if (firstGpu && firstGpu.pciId) {
// Save the first GPU's PCI ID to this widget's settings
updateWidgetPciId(firstGpu.pciId); updateWidgetPciId(firstGpu.pciId);
DgopService.addGpuPciId(firstGpu.pciId); DgopService.addGpuPciId(firstGpu.pciId);
} }
} }
} }
} }
} }

View File

@@ -2,51 +2,34 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell import Quickshell
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false content: Component {
property var axis: null Item {
property string section: "right" implicitWidth: root.widgetThickness - root.horizontalPadding * 2
property var popupTarget: null implicitHeight: root.widgetThickness - root.horizontalPadding * 2
property var parentScreen: null
property real widgetThickness: 30
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
width: isVertical ? widgetThickness : (idleIcon.width + horizontalPadding * 2) DankIcon {
height: isVertical ? (idleIcon.height + horizontalPadding * 2) : widgetThickness anchors.centerIn: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
color: { size: Theme.barIconSize(root.barThickness, -4)
if (SettingsData.dankBarNoBackground) { color: Theme.surfaceText
return "transparent"; }
} }
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 { MouseArea {
id: mouseArea z: 1
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
SessionService.toggleIdleInhibit(); SessionService.toggleIdleInhibit()
} }
} }
} }

View File

@@ -3,36 +3,69 @@ import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Modules.ProcessList import qs.Modules.ProcessList
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root 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 currentLayout: ""
property string hyprlandKeyboard: "" property string hyprlandKeyboard: ""
width: isVertical ? widgetThickness : (contentRow.implicitWidth + horizontalPadding * 2) content: Component {
height: isVertical ? (contentColumn.implicitHeight + horizontalPadding * 2) : widgetThickness Item {
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth
color: { implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = mouseArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; Column {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); 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 { MouseArea {
id: mouseArea z: 1
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor 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 { Timer {
id: updateTimer id: updateTimer
interval: 1000 interval: 1000

View File

@@ -3,127 +3,95 @@ import QtQuick.Effects
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { BasePill {
id: root id: root
property bool isActive: false property bool isActive: false
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property string section: "left"
property var popupTarget: null
property var parentScreen: null
property real widgetThickness: 30
property real barThickness: 48
property var hyprlandOverviewLoader: null 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 DankIcon {
height: widgetThickness visible: SettingsData.launcherLogoMode === "apps"
anchors.centerIn: parent
MouseArea { name: "apps"
id: launcherArea size: Theme.barIconSize(root.barThickness, -4)
color: Theme.surfaceText
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
} }
root.clicked(); SystemLogo {
if (popupTarget && popupTarget.setTriggerPosition) { visible: SettingsData.launcherLogoMode === "os"
const globalPos = mapToGlobal(0, 0); anchors.centerIn: parent
const currentScreen = parentScreen || Screen; width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width); height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen); 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 { MouseArea {
id: launcherContent id: customMouseArea
anchors.fill: parent anchors.fill: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius hoverEnabled: true
color: { cursorShape: Qt.PointingHandCursor
if (SettingsData.dankBarNoBackground) { acceptedButtons: Qt.RightButton
return "transparent"; onPressed: function (mouse){
} if (CompositorService.isNiri) {
NiriService.toggleOverview()
const baseColor = launcherArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; } else if (root.hyprlandOverviewLoader?.item) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); root.hyprlandOverviewLoader.item.overviewOpen = !root.hyprlandOverviewLoader.item.overviewOpen
}
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
} }
} }
} }

View File

@@ -1,412 +1,358 @@
import QtQuick import QtQuick
import Quickshell.Services.Mpris import Quickshell.Services.Mpris
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property bool playerAvailable: activePlayer !== null readonly property bool playerAvailable: activePlayer !== null
property bool compactMode: false property bool compactMode: false
readonly property int textWidth: { readonly property int textWidth: {
switch (SettingsData.mediaSize) { switch (SettingsData.mediaSize) {
case 0: case 0:
return 0; // No text in small mode return 0;
case 2: case 2:
return 180; // Large text area return 180;
default: default:
return 120; // Medium text area return 120;
} }
} }
readonly property int currentContentWidth: { readonly property int currentContentWidth: {
if (isVertical) { if (isVerticalOrientation) {
return widgetThickness; return widgetThickness - horizontalPadding * 2;
} }
const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20; const controlsWidth = 20 + Theme.spacingXS + 24 + Theme.spacingXS + 20;
const audioVizWidth = 20; const audioVizWidth = 20;
const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth; const contentWidth = audioVizWidth + Theme.spacingXS + controlsWidth;
return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0) + horizontalPadding * 2; return contentWidth + (textWidth > 0 ? textWidth + Theme.spacingXS : 0);
} }
readonly property int currentContentHeight: { readonly property int currentContentHeight: {
if (!isVertical) { if (!isVerticalOrientation) {
return widgetThickness; return widgetThickness - horizontalPadding * 2;
} }
const audioVizHeight = 20; const audioVizHeight = 20;
const playButtonHeight = 24; const playButtonHeight = 24;
return audioVizHeight + Theme.spacingXS + playButtonHeight + horizontalPadding * 2; return audioVizHeight + Theme.spacingXS + playButtonHeight;
}
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()
}
}
}
}
} }
Row { content: Component {
id: mediaRow Item {
implicitWidth: root.playerAvailable ? root.currentContentWidth : 0
implicitHeight: root.playerAvailable ? root.currentContentHeight : 0
opacity: root.playerAvailable ? 1 : 0
visible: !root.isVertical states: [
anchors.centerIn: parent State {
spacing: Theme.spacingXS name: "shown"
when: root.playerAvailable
Row { PropertyChanges {
id: mediaInfo target: parent
opacity: 1
spacing: Theme.spacingXS implicitWidth: root.currentContentWidth
implicitHeight: root.currentContentHeight
AudioVisualization {
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
id: textContainer
property string displayText: {
if (!activePlayer || !activePlayer.trackTitle) {
return "";
} }
},
let identity = activePlayer.identity || ""; State {
let isWebMedia = identity.toLowerCase().includes("firefox") || identity.toLowerCase().includes("chrome") || identity.toLowerCase().includes("chromium") || identity.toLowerCase().includes("edge") || identity.toLowerCase().includes("safari"); name: "hidden"
let title = ""; when: !root.playerAvailable
let subtitle = ""; PropertyChanges {
if (isWebMedia && activePlayer.trackTitle) { target: parent
title = activePlayer.trackTitle; opacity: 0
subtitle = activePlayer.trackArtist || identity; implicitWidth: 0
} else { implicitHeight: 0
title = activePlayer.trackTitle || "Unknown Track";
subtitle = activePlayer.trackArtist || "";
} }
return subtitle.length > 0 ? title + " • " + subtitle : title;
} }
]
anchors.verticalCenter: parent.verticalCenter transitions: [
width: textWidth Transition {
height: 20 from: "shown"
visible: SettingsData.mediaSize > 0 to: "hidden"
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();
}
SequentialAnimation { SequentialAnimation {
id: scrollAnimation
running: mediaText.needsScrolling && textContainer.visible
loops: Animation.Infinite
PauseAnimation { PauseAnimation {
duration: 2000 duration: 500
} }
NumberAnimation { NumberAnimation {
target: mediaText properties: "opacity,implicitWidth,implicitHeight"
property: "scrollOffset" duration: Theme.shortDuration
from: 0 easing.type: Theme.standardEasing
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
}
} }
},
Transition {
from: "hidden"
to: "shown"
NumberAnimation {
properties: "opacity,implicitWidth,implicitHeight"
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
} }
]
MouseArea { Column {
anchors.fill: parent id: verticalLayout
enabled: root.playerAvailable visible: root.isVerticalOrientation
cursorShape: Qt.PointingHandCursor anchors.centerIn: parent
onPressed: { spacing: Theme.spacingXS
if (root.popupTarget && root.popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) AudioVisualization {
const currentScreen = root.parentScreen || Screen anchors.horizontalCenter: parent.horizontalCenter
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.width)
root.popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, root.section, currentScreen) 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 { Row {
spacing: Theme.spacingXS id: mediaInfo
anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS
Rectangle { AudioVisualization {
width: 20 anchors.verticalCenter: parent.verticalCenter
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 { Rectangle {
anchors.centerIn: parent id: textContainer
name: "skip_previous" property string displayText: {
size: 12 if (!activePlayer || !activePlayer.trackTitle) {
color: Theme.surfaceText return "";
} }
MouseArea { let identity = activePlayer.identity || "";
id: prevArea 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 anchors.verticalCenter: parent.verticalCenter
enabled: root.playerAvailable width: textWidth
hoverEnabled: true height: 20
cursorShape: Qt.PointingHandCursor visible: SettingsData.mediaSize > 0
onClicked: { clip: true
if (activePlayer) { color: "transparent"
activePlayer.previous();
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 { Rectangle {
width: 24 width: 20
height: 24 height: 20
radius: 12 radius: 10
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: activePlayer && activePlayer.playbackState === 1 ? Theme.primary : Theme.primaryHover color: prevArea.containsMouse ? Theme.primaryHover : "transparent"
visible: root.playerAvailable visible: root.playerAvailable
opacity: activePlayer ? 1 : 0.3 opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow" name: "skip_previous"
size: 14 size: 12
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary color: Theme.surfaceText
} }
MouseArea { MouseArea {
anchors.fill: parent id: prevArea
enabled: root.playerAvailable anchors.fill: parent
cursorShape: Qt.PointingHandCursor enabled: root.playerAvailable
onClicked: { hoverEnabled: true
if (activePlayer) { cursorShape: Qt.PointingHandCursor
activePlayer.togglePlaying(); 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
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Modules.ProcessList import qs.Modules.ProcessList
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root 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) { function formatNetworkSpeed(bytesPerSec) {
if (bytesPerSec < 1024) { if (bytesPerSec < 1024) {
return bytesPerSec.toFixed(0) + " B/s"; return bytesPerSec.toFixed(0) + " B/s"
} else if (bytesPerSec < 1024 * 1024) { } 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) { } else if (bytesPerSec < 1024 * 1024 * 1024) {
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"; return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s"
} else { } 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: { Component.onCompleted: {
DgopService.addRef(["network"]); DgopService.addRef(["network"])
} }
Component.onDestruction: { Component.onDestruction: {
DgopService.removeRef(["network"]); DgopService.removeRef(["network"])
} }
MouseArea { content: Component {
id: networkArea Item {
implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : contentRow.implicitWidth
implicitHeight: root.isVerticalOrientation ? contentColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
anchors.fill: parent Column {
hoverEnabled: true id: contentColumn
cursorShape: Qt.PointingHandCursor anchors.centerIn: parent
} spacing: 2
visible: root.isVerticalOrientation
Column { DankIcon {
id: contentColumn name: "network_check"
size: Theme.barIconSize(root.barThickness)
anchors.centerIn: parent color: Theme.surfaceText
spacing: 2 anchors.horizontalCenter: parent.horizontalCenter
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"
} }
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 { StyledText {
NumberAnimation { text: {
duration: 120 const rate = DgopService.networkTxRate
easing.type: Easing.OutCubic 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 QtQuick
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root 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: ( readonly property string focusedScreenName: (
CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") : CompositorService.isHyprland && typeof Hyprland !== "undefined" && Hyprland.focusedWorkspace && Hyprland.focusedWorkspace.monitor ? (Hyprland.focusedWorkspace.monitor.name || "") :
CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : "" CompositorService.isNiri && typeof NiriService !== "undefined" && NiriService.currentOutput ? NiriService.currentOutput : ""
@@ -43,54 +34,43 @@ Rectangle {
readonly property var notepadInstance: resolveNotepadInstance() readonly property var notepadInstance: resolveNotepadInstance()
readonly property bool isActive: notepadInstance?.isVisible ?? false readonly property bool isActive: notepadInstance?.isVisible ?? false
width: isVertical ? widgetThickness : (notepadIcon.width + horizontalPadding * 2) content: Component {
height: isVertical ? (notepadIcon.height + horizontalPadding * 2) : widgetThickness Item {
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius implicitWidth: root.widgetThickness - root.horizontalPadding * 2
color: { implicitHeight: root.widgetThickness - root.horizontalPadding * 2
if (SettingsData.dankBarNoBackground) {
return "transparent"; 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 { MouseArea {
id: notepadArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
const inst = root.notepadInstance const inst = root.notepadInstance
if (inst) { if (inst) {
inst.toggle() inst.toggle()
} }
root.clicked()
} }
} }
} }

View File

@@ -1,76 +1,37 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Widgets import qs.Widgets
Item { BasePill {
id: root id: root
property bool hasUnread: false property bool hasUnread: false
property bool isActive: 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 DankIcon {
height: widgetThickness anchors.centerIn: parent
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
MouseArea { size: Theme.barIconSize(root.barThickness, -4)
id: notificationArea color: SessionData.doNotDisturb ? Theme.error : (root.isActive ? Theme.primary : Theme.surfaceText)
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
}
root.clicked()
}
}
Rectangle {
id: notificationContent
anchors.fill: parent
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.dankBarNoBackground) {
return "transparent"
} }
const baseColor = notificationArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor Rectangle {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) width: 8
} height: 8
radius: 4
DankIcon { color: Theme.error
anchors.horizontalCenter: parent.horizontalCenter anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top
name: SessionData.doNotDisturb ? "notifications_off" : "notifications" anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6
size: Theme.barIconSize(barThickness, -4) anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6
color: SessionData.doNotDisturb ? Theme.error : (notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText) 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.Services
import qs.Widgets import qs.Widgets
Rectangle { Item {
id: root id: root
property bool isVertical: axis?.isVertical ?? false property bool isVertical: axis?.isVertical ?? false
@@ -19,26 +19,167 @@ Rectangle {
readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive readonly property int activeCount: PrivacyService.microphoneActive + PrivacyService.cameraActive + PrivacyService.screensharingActive
readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 readonly property real contentWidth: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0
readonly property real contentHeight: hasActivePrivacy ? (activeCount * 18 + (activeCount - 1) * Theme.spacingXS) : 0 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) width: isVertical ? barThickness : visualWidth
height: isVertical ? (hasActivePrivacy ? (contentHeight + horizontalPadding * 2) : 0) : (hasActivePrivacy ? widgetThickness : 0) height: isVertical ? visualHeight : barThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: hasActivePrivacy visible: hasActivePrivacy
opacity: hasActivePrivacy ? 1 : 0 opacity: hasActivePrivacy ? 1 : 0
enabled: hasActivePrivacy enabled: hasActivePrivacy
color: {
if (SettingsData.dankBarNoBackground) { Rectangle {
return "transparent"; 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 { MouseArea {
// Privacy indicator click handler
id: privacyArea id: privacyArea
z: -1
anchors.fill: parent anchors.fill: parent
hoverEnabled: hasActivePrivacy hoverEnabled: hasActivePrivacy
enabled: 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 { Rectangle {
id: tooltip id: tooltip
width: tooltipText.contentWidth + Theme.spacingM * 2 width: tooltipText.contentWidth + Theme.spacingM * 2
height: tooltipText.contentHeight + Theme.spacingS * 2 height: tooltipText.contentHeight + Theme.spacingS * 2
radius: Theme.cornerRadius radius: Theme.cornerRadius
@@ -206,7 +204,6 @@ Rectangle {
StyledText { StyledText {
id: tooltipText id: tooltipText
anchors.centerIn: parent anchors.centerIn: parent
text: PrivacyService.getPrivacySummary() text: PrivacyService.getPrivacySummary()
font.pixelSize: Theme.barTextSize(barThickness) font.pixelSize: Theme.barTextSize(barThickness)
@@ -232,9 +229,7 @@ Rectangle {
duration: Theme.shortDuration duration: Theme.shortDuration
easing.type: Theme.standardEasing easing.type: Theme.standardEasing
} }
} }
} }
Behavior on width { Behavior on width {
@@ -244,7 +239,6 @@ Rectangle {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
Behavior on height { Behavior on height {
@@ -254,7 +248,5 @@ Rectangle {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing easing.type: Theme.emphasizedEasing
} }
} }
} }

View File

@@ -1,37 +1,19 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool showPercentage: true property bool showPercentage: true
property bool showIcon: true property bool showIcon: true
property var toggleProcessList property var toggleProcessList
property string section: "right" property var popoutTarget: null
property var popupTarget: null
property var parentScreen: null
property real barThickness: 48
property real widgetThickness: 30
property var widgetData: null property var widgetData: null
property bool minimumWidth: (widgetData && widgetData.minimumWidth !== undefined) ? widgetData.minimumWidth : true 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: { Component.onCompleted: {
DgopService.addRef(["memory"]); DgopService.addRef(["memory"]);
@@ -40,120 +22,123 @@ Rectangle {
DgopService.removeRef(["memory"]); DgopService.removeRef(["memory"]);
} }
MouseArea { content: Component {
id: ramArea 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 anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
DgopService.setSortBy("memory"); DgopService.setSortBy("memory");
if (root.toggleProcessList) { if (root.toggleProcessList) {
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.Services
import qs.Widgets import qs.Widgets
Rectangle { Item {
id: root id: root
property bool isVertical: axis?.isVertical ?? false property bool isVertical: axis?.isVertical ?? false
@@ -66,22 +66,29 @@ Rectangle {
} }
} }
width: isVertical ? widgetThickness : calculatedSize width: isVertical ? barThickness : calculatedSize
height: isVertical ? calculatedSize : widgetThickness height: isVertical ? calculatedSize : barThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
visible: windowCount > 0 visible: windowCount > 0
clip: false
color: {
if (windowCount === 0) {
return "transparent";
}
if (SettingsData.dankBarNoBackground) { Rectangle {
return "transparent"; 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; if (SettingsData.dankBarNoBackground) {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); return "transparent";
}
const baseColor = Theme.widgetBaseBackgroundColor;
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency);
}
} }
MouseArea { MouseArea {
@@ -210,12 +217,16 @@ Rectangle {
} }
return appName + (windowTitle ? " • " + windowTitle : "") return appName + (windowTitle ? " • " + windowTitle : "")
} }
readonly property real visualWidth: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120)
width: SettingsData.runningAppsCompactMode ? 24 : (24 + Theme.spacingXS + 120) width: visualWidth
height: 24 height: root.barThickness
Rectangle { Rectangle {
anchors.fill: parent id: visualContent
width: delegateItem.visualWidth
height: 24
anchors.centerIn: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (isFocused) { if (isFocused) {
@@ -237,8 +248,6 @@ Rectangle {
} }
} }
}
// App icon // App icon
IconImage { IconImage {
id: iconImg id: iconImg
@@ -334,10 +343,10 @@ Rectangle {
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1
} }
}
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@@ -442,12 +451,16 @@ Rectangle {
} }
return appName + (windowTitle ? " • " + windowTitle : "") 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 height: 24
Rectangle { Rectangle {
anchors.fill: parent id: visualContent
width: delegateItem.visualWidth
height: 24
anchors.centerIn: parent
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (isFocused) { if (isFocused) {
@@ -469,8 +482,6 @@ Rectangle {
} }
} }
}
IconImage { IconImage {
id: iconImg id: iconImg
anchors.left: parent.left anchors.left: parent.left
@@ -563,10 +574,10 @@ Rectangle {
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 1 maximumLineCount: 1
} }
}
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor

View File

@@ -7,7 +7,7 @@ import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
Rectangle { Item {
id: root id: root
property bool isVertical: axis?.isVertical ?? false property bool isVertical: axis?.isVertical ?? false
@@ -15,6 +15,7 @@ Rectangle {
property var parentWindow: null property var parentWindow: null
property var parentScreen: null property var parentScreen: null
property real widgetThickness: 30 property real widgetThickness: 30
property real barThickness: 48
property bool isAtBottom: false property bool isAtBottom: false
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 2 : Theme.spacingS
readonly property var hiddenTrayIds: { readonly property var hiddenTrayIds: {
@@ -31,24 +32,33 @@ Rectangle {
}) })
} }
readonly property int calculatedSize: visibleTrayItems.length > 0 ? visibleTrayItems.length * 24 + horizontalPadding * 2 : 0 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 width: isVertical ? barThickness : visualWidth
height: isVertical ? calculatedSize : widgetThickness height: isVertical ? visualHeight : barThickness
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);
}
visible: visibleTrayItems.length > 0 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 { Loader {
id: layoutLoader id: layoutLoader
anchors.centerIn: parent anchors.centerIn: parent
@@ -64,81 +74,81 @@ Rectangle {
model: root.visibleTrayItems model: root.visibleTrayItems
delegate: Item { delegate: Item {
property var trayItem: modelData id: delegateRoot
property string iconSource: { property var trayItem: modelData
let icon = trayItem && trayItem.icon; property string iconSource: {
if (typeof icon === 'string' || icon instanceof String) { let icon = trayItem && trayItem.icon;
if (icon === "") { if (typeof icon === 'string' || icon instanceof String) {
return ""; if (icon === "") {
} return "";
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
} }
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0]; const name = split[0];
const path = split[1]; const path = split[1];
let fileName = name.substring(name.lastIndexOf("/") + 1); let fileName = name.substring(name.lastIndexOf("/") + 1);
if (fileName.startsWith("dropboxstatus")) { if (fileName.startsWith("dropboxstatus")) {
fileName = `hicolor/16x16/status/${fileName}`; 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 "";
return `file://${icon}`;
}
return icon;
} }
return "";
}
width: 24 width: 24
height: 24 height: root.barThickness
Rectangle { Rectangle {
anchors.fill: parent id: visualContent
radius: Theme.cornerRadius width: 24
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent" height: 24
anchors.centerIn: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
IconImage {
} anchors.centerIn: parent
width: 16
IconImage { height: 16
anchors.centerIn: parent source: delegateRoot.iconSource
width: 16 asynchronous: true
height: 16 smooth: true
source: parent.iconSource mipmap: true
asynchronous: true
smooth: true
mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
} }
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) { MouseArea {
trayItem.activate(); id: trayItemArea
return ;
} anchors.fill: parent
if (trayItem.hasMenu) { acceptedButtons: Qt.LeftButton | Qt.RightButton
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis); 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 model: root.visibleTrayItems
delegate: Item { delegate: Item {
property var trayItem: modelData id: delegateRoot
property string iconSource: { property var trayItem: modelData
let icon = trayItem && trayItem.icon; property string iconSource: {
if (typeof icon === 'string' || icon instanceof String) { let icon = trayItem && trayItem.icon;
if (icon === "") { if (typeof icon === 'string' || icon instanceof String) {
return ""; if (icon === "") {
} return "";
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
} }
if (icon.includes("?path=")) {
const split = icon.split("?path=");
if (split.length !== 2) {
return icon;
}
const name = split[0]; const name = split[0];
const path = split[1]; const path = split[1];
let fileName = name.substring(name.lastIndexOf("/") + 1); let fileName = name.substring(name.lastIndexOf("/") + 1);
if (fileName.startsWith("dropboxstatus")) { if (fileName.startsWith("dropboxstatus")) {
fileName = `hicolor/16x16/status/${fileName}`; 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 "";
return `file://${icon}`;
}
return icon;
} }
return "";
}
width: 24 width: root.barThickness
height: 24 height: 24
Rectangle { Rectangle {
anchors.fill: parent id: visualContent
radius: Theme.cornerRadius width: 24
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent" height: 24
} anchors.centerIn: parent
radius: Theme.cornerRadius
color: trayItemArea.containsMouse ? Theme.primaryHover : "transparent"
IconImage { IconImage {
anchors.centerIn: parent anchors.centerIn: parent
width: 16 width: 16
height: 16 height: 16
source: parent.iconSource source: delegateRoot.iconSource
asynchronous: true asynchronous: true
smooth: true smooth: true
mipmap: true mipmap: true
}
MouseArea {
id: trayItemArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (!trayItem) {
return;
} }
}
if (mouse.button === Qt.LeftButton && !trayItem.onlyMenu) { MouseArea {
trayItem.activate(); id: trayItemArea
return ;
} anchors.fill: parent
if (trayItem.hasMenu) { acceptedButtons: Qt.LeftButton | Qt.RightButton
root.showForTrayItem(trayItem, parent, parentScreen, root.isAtBottom, root.isVertical, root.axis); 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 QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
property bool isVertical: axis?.isVertical ?? false
property var axis: null
property bool isActive: false property bool isActive: false
property string section: "right"
property 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 hasUpdates: SystemUpdateService.updateCount > 0
readonly property bool isChecking: SystemUpdateService.isChecking readonly property bool isChecking: SystemUpdateService.isChecking
signal clicked()
Ref { Ref {
service: SystemUpdateService service: SystemUpdateService
} }
width: isVertical ? widgetThickness : (updaterIcon.width + horizontalPadding * 2) content: Component {
height: isVertical ? widgetThickness : widgetThickness Item {
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius implicitWidth: root.isVerticalOrientation ? (root.widgetThickness - root.horizontalPadding * 2) : updaterIcon.implicitWidth
color: { implicitHeight: root.widgetThickness - root.horizontalPadding * 2
if (SettingsData.dankBarNoBackground) {
return "transparent";
}
const baseColor = updaterArea.containsMouse ? Theme.widgetBaseHoverColor : Theme.widgetBaseBackgroundColor; DankIcon {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency); id: statusIcon
} anchors.centerIn: parent
visible: root.isVerticalOrientation
DankIcon { name: {
id: statusIcon if (root.isChecking) return "refresh"
if (SystemUpdateService.hasError) return "error"
anchors.centerIn: parent if (root.hasUpdates) return "system_update_alt"
visible: root.isVertical return "check_circle"
name: { }
if (isChecking) return "refresh"; size: Theme.barIconSize(root.barThickness, -4)
if (SystemUpdateService.hasError) return "error"; color: {
if (hasUpdates) return "system_update_alt"; if (SystemUpdateService.hasError) return Theme.error
return "check_circle"; if (root.hasUpdates) return Theme.primary
} return root.isActive ? Theme.primary : Theme.surfaceText
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
} }
}
}
}
Rectangle { RotationAnimation {
width: 8 id: rotationAnimation
height: 8 target: statusIcon
radius: 4 property: "rotation"
color: Theme.error from: 0
anchors.right: parent.right to: 360
anchors.top: parent.top duration: 1000
anchors.rightMargin: SettingsData.dankBarNoBackground ? 0 : 6 running: root.isChecking
anchors.topMargin: SettingsData.dankBarNoBackground ? 0 : 6 loops: Animation.Infinite
visible: root.isVertical && root.hasUpdates && !root.isChecking
}
Row { onRunningChanged: {
id: updaterIcon if (!running) {
statusIcon.rotation = 0
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
} }
} }
} }
}
StyledText { Rectangle {
id: countText 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 Row {
text: SystemUpdateService.updateCount.toString() id: updaterIcon
font.pixelSize: Theme.barTextSize(barThickness) anchors.centerIn: parent
font.weight: Font.Medium spacing: Theme.spacingXS
color: Theme.surfaceText visible: !root.isVerticalOrientation
visible: hasUpdates && !isChecking
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 { MouseArea {
id: updaterArea z: 1
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
root.clicked(); root.clicked()
} }
} }
} }

View File

@@ -1,46 +1,35 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root id: root
Ref { Ref {
service: VpnService service: VpnService
} }
property bool isVertical: axis?.isVertical ?? false property var popoutTarget: null
property var axis: null
property int widgetThickness: 28
property int barThickness: 32
property string section: "right"
property var popupTarget: null
property var parentScreen: null
readonly property real horizontalPadding: SettingsData.dankBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetThickness / 30))
signal toggleVpnPopup() signal toggleVpnPopup()
width: isVertical ? widgetThickness : (Theme.iconSize + horizontalPadding * 2) content: Component {
height: isVertical ? (Theme.iconSize + horizontalPadding * 2) : widgetThickness Item {
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius implicitWidth: root.widgetThickness - root.horizontalPadding * 2
color: { implicitHeight: root.widgetThickness - root.horizontalPadding * 2
if (SettingsData.dankBarNoBackground) {
return "transparent"; 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 { Loader {
@@ -55,17 +44,18 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onPressed: { onPressed: {
if (popupTarget && popupTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = root.visualContent.mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, root.visualWidth)
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
root.toggleVpnPopup(); root.toggleVpnPopup();
} }
onEntered: { onEntered: {
if (root.parentScreen && !(popupTarget && popupTarget.shouldBeVisible)) { if (root.parentScreen && !(popoutTarget && popoutTarget.shouldBeVisible)) {
tooltipLoader.active = true tooltipLoader.active = true
if (tooltipLoader.item) { if (tooltipLoader.item) {
let tooltipText = "" let tooltipText = ""
@@ -80,7 +70,7 @@ Rectangle {
} }
} }
if (root.isVertical) { if (root.isVerticalOrientation) {
const globalPos = mapToGlobal(width / 2, height / 2) const globalPos = mapToGlobal(width / 2, height / 2)
const screenX = root.parentScreen ? root.parentScreen.x : 0 const screenX = root.parentScreen ? root.parentScreen.x : 0
const screenY = root.parentScreen ? root.parentScreen.y : 0 const screenY = root.parentScreen ? root.parentScreen.y : 0
@@ -103,5 +93,4 @@ Rectangle {
tooltipLoader.active = false tooltipLoader.active = false
} }
} }
} }

View File

@@ -1,120 +1,81 @@
import QtQuick import QtQuick
import qs.Common import qs.Common
import qs.Modules.Plugins
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { BasePill {
id: root 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 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 { Ref {
service: WeatherService service: WeatherService
} }
Column { content: Component {
id: weatherColumn Item {
visible: root.isVertical implicitWidth: {
anchors.centerIn: parent if (!SettingsData.weatherEnabled) return 0
spacing: 1 if (root.isVerticalOrientation) return root.widgetThickness - root.horizontalPadding * 2
return Math.min(100 - root.horizontalPadding * 2, weatherRow.implicitWidth)
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;
} }
font.pixelSize: Theme.barTextSize(barThickness) implicitHeight: root.isVerticalOrientation ? weatherColumn.implicitHeight : (root.widgetThickness - root.horizontalPadding * 2)
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
Row { Column {
id: weatherRow id: weatherColumn
visible: root.isVerticalOrientation
anchors.centerIn: parent
spacing: 1
visible: !root.isVertical DankIcon {
anchors.centerIn: parent name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
spacing: Theme.spacingXS size: Theme.barIconSize(root.barThickness, -6)
color: Theme.primary
DankIcon { anchors.horizontalCenter: parent.horizontalCenter
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");
} }
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 { DankIcon {
id: weatherArea name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
size: Theme.barIconSize(root.barThickness, -6)
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
anchors.fill: parent StyledText {
hoverEnabled: true text: {
cursorShape: Qt.PointingHandCursor const temp = SettingsData.useFahrenheit ? WeatherService.weather.tempF : WeatherService.weather.temp;
onPressed: { if (temp === undefined || temp === null || temp === 0) {
if (popupTarget && popupTarget.setTriggerPosition) { return "--°" + (SettingsData.useFahrenheit ? "F" : "C");
const globalPos = mapToGlobal(0, 0) }
const currentScreen = parentScreen || Screen
const pos = SettingsData.getPopupTriggerPosition(globalPos, currentScreen, barThickness, width) return temp + "°" + (SettingsData.useFahrenheit ? "F" : "C");
popupTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen) }
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.Services
import qs.Widgets import qs.Widgets
Rectangle { Item {
id: root id: root
property bool isVertical: axis?.isVertical ?? false property bool isVertical: axis?.isVertical ?? false
@@ -16,6 +16,7 @@ Rectangle {
property real widgetHeight: 30 property real widgetHeight: 30
property real barThickness: 48 property real barThickness: 48
property var hyprlandOverviewLoader: null property var hyprlandOverviewLoader: null
property var parentScreen: null
readonly property var sortedToplevels: { readonly property var sortedToplevels: {
return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name); return CompositorService.filterCurrentWorkspace(CompositorService.sortedToplevels, parentScreen?.name);
} }
@@ -201,9 +202,9 @@ Rectangle {
return currentMonitor.activeWorkspace?.id ?? 1 return currentMonitor.activeWorkspace?.id ?? 1
} }
readonly property real padding: isVertical readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
? Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30)) readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
: (widgetHeight - workspaceRow.implicitHeight) / 2 readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
function getRealWorkspaces() { function getRealWorkspaces() {
return root.workspaceList.filter(ws => { return root.workspaceList.filter(ws => {
@@ -232,17 +233,24 @@ Rectangle {
} }
} }
width: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2) width: isVertical ? barThickness : visualWidth
height: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight height: isVertical ? visualHeight : barThickness
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)
}
visible: CompositorService.isNiri || CompositorService.isHyprland 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 { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
@@ -357,7 +365,7 @@ Rectangle {
Repeater { Repeater {
model: root.workspaceList model: root.workspaceList
Rectangle { Item {
id: delegateRoot id: delegateRoot
property bool isActive: { property bool isActive: {
@@ -389,6 +397,33 @@ Rectangle {
property bool loadedHasIcon: false property bool loadedHasIcon: false
property var loadedIcons: [] 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 { Timer {
id: dataUpdateTimer id: dataUpdateTimer
interval: 50 interval: 50
@@ -430,92 +465,54 @@ Rectangle {
dataUpdateTimer.restart() dataUpdateTimer.restart()
} }
width: { width: root.isVertical ? root.barThickness : visualWidth
if (root.isVertical) { height: root.isVertical ? visualHeight : root.barThickness
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
border.width: isUrgent && !isActive ? 2 : 0 Rectangle {
border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0) 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 { border.width: isUrgent && !isActive ? 2 : 0
enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) border.color: isUrgent && !isActive ? Theme.error : Theme.withAlpha(Theme.error, 0)
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on height { Behavior on width {
enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3) enabled: (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
NumberAnimation { NumberAnimation {
duration: Theme.mediumDuration duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing 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}`)
} }
} }
}
Loader { Behavior on height {
id: appIconsLoader enabled: root.isVertical && (!SettingsData.showWorkspaceApps || SettingsData.maxWorkspaceIcons <= 3)
anchors.fill: parent NumberAnimation {
active: SettingsData.showWorkspaceApps 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 { sourceComponent: Item {
Loader { Loader {
id: contentRow 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() Component.onCompleted: updateAllData()
Connections { Connections {

View File

@@ -3,7 +3,7 @@ import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Rectangle { Item {
id: root id: root
property var axis: null property var axis: null
@@ -12,44 +12,54 @@ Rectangle {
property var parentScreen: null property var parentScreen: null
property real widgetThickness: 30 property real widgetThickness: 30
property real barThickness: 48 property real barThickness: 48
property bool isVerticalOrientation: false
property alias content: contentLoader.sourceComponent 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 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() signal clicked()
width: isVerticalOrientation ? widgetThickness : contentLoader.item ? (contentLoader.item.implicitWidth + horizontalPadding * 2) : 0 width: isVerticalOrientation ? barThickness : visualWidth
height: isVerticalOrientation ? (contentLoader.item ? (contentLoader.item.implicitHeight + horizontalPadding * 2) : 0) : widgetThickness height: isVerticalOrientation ? visualHeight : barThickness
radius: SettingsData.dankBarNoBackground ? 0 : Theme.cornerRadius
color: { Rectangle {
if (SettingsData.dankBarNoBackground) { id: visualContent
return "transparent" 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 Loader {
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency) id: contentLoader
} anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Loader { }
id: contentLoader
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
} }
MouseArea { MouseArea {
id: mouseArea id: mouseArea
z: -1
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onPressed: { onClicked: {
root.clicked()
if (popoutTarget && popoutTarget.setTriggerPosition) { if (popoutTarget && popoutTarget.setTriggerPosition) {
const globalPos = mapToGlobal(0, 0) const globalPos = mapToGlobal(0, 0)
const currentScreen = parentScreen || Screen 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) popoutTarget.setTriggerPosition(pos.x, pos.y, pos.width, section, currentScreen)
} }
root.clicked()
} }
} }
} }