added three new modules (Crypto.qml, Battery.qml, Network.qml), improved Stats.qml, improved Tray.qml, optimized code in general, added a polkit agent, configurations change (check configs)

This commit is contained in:
SomeElse
2026-05-14 04:56:33 +00:00
parent 046a36fbec
commit e3bfa6661f
31 changed files with 5857 additions and 353 deletions

View File

@@ -13,9 +13,9 @@ Item {
property real panelRadius: Cfg.Config.radius / 2
property int borderWidth: Cfg.Config.panelBorderWidth
property bool isTop: true
property var targetScreen: null
property string barSide: "right"
property bool isTop: true
readonly property var t: Cfg.Config.theme
@@ -23,14 +23,24 @@ Item {
property bool manuallyExpanded: false
property var menuWindow: null
readonly property bool effectiveExpanded: expanded || trayMenuPopup.popupOpen
implicitWidth: bgRect.width
implicitHeight: bgRect.height
readonly property color tmBg: t.trayMenuBg
readonly property color tmBorder: t.trayMenuBorder
readonly property color tmText: t.trayMenuText
readonly property color tmDim: t.trayMenuDim
readonly property color tmHover: t.trayMenuHover
readonly property color tmCheck: t.trayMenuCheck
readonly property color tmSeparator: t.trayMenuSeparator
Rectangle {
id: bgRect
anchors.verticalCenter: parent.verticalCenter
height: Cfg.Config.trayHeight
height: parent.height
width: mainRow.width
radius: root.panelRadius
@@ -41,8 +51,8 @@ Item {
Behavior on color { ColorAnimation { duration: 150 } }
Behavior on border.color { ColorAnimation { duration: 150 } }
HoverHandler {
id: bgHover
HoverHandler {
id: bgHover
onHoveredChanged: {
if (!root.manuallyExpanded) {
root.expanded = hovered;
@@ -54,21 +64,21 @@ Item {
id: mainRow
anchors.verticalCenter: parent.verticalCenter
leftPadding: root.expanded && root.barSide === "right" ? 8 : 0
rightPadding: root.expanded && root.barSide === "left" ? 8 : 0
leftPadding: root.effectiveExpanded && root.barSide === "right" ? 8 : 0
rightPadding: root.effectiveExpanded && root.barSide === "left" ? 8 : 0
Behavior on leftPadding { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
Behavior on rightPadding { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
layoutDirection: root.barSide === "left" ? Qt.LeftToRight : Qt.RightToLeft
spacing: root.expanded ? 4 : 0
spacing: root.effectiveExpanded ? 4 : 0
Behavior on spacing { NumberAnimation { duration: 180; easing.type: Easing.OutCubic } }
Item {
id: toggleArea
width: Cfg.Config.trayHeight
height: Cfg.Config.trayHeight
width: Cfg.Config.trayToggleSize
height: Cfg.Config.trayToggleSize
z: 10
TapHandler {
@@ -80,11 +90,11 @@ Item {
Text {
anchors.centerIn: parent
text: root.expanded
text: root.effectiveExpanded
? (root.barSide === "left" ? "󰅁" : "󰅂")
: (root.barSide === "left" ? "󰅂" : "󰅁")
color: root.t.trayIcon
font.pixelSize: Cfg.Config.trayHeight * 0.55
font.pixelSize: Cfg.Config.trayToggleSize * 0.55
}
}
@@ -94,9 +104,9 @@ Item {
clip: true
anchors.verticalCenter: parent.verticalCenter
width: root.expanded ? implicitWidth : 0
opacity: root.expanded ? 1 : 0
width: root.effectiveExpanded ? implicitWidth : 0
opacity: root.effectiveExpanded ? 1 : 0
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
Behavior on opacity { NumberAnimation { duration: 150 } }
@@ -122,7 +132,9 @@ Item {
} else if (mouse.button === Qt.MiddleButton) {
item.secondaryActivate()
} else if (mouse.button === Qt.RightButton) {
menuAnchor.open()
if (item.hasMenu) {
trayMenuPopup.open(item, iconDelegate)
}
}
}
@@ -135,22 +147,462 @@ Item {
anchors.fill: parent
source: item.icon
}
}
}
}
}
}
QsMenuAnchor {
id: menuAnchor
menu: item.menu
anchor.window: root.menuWindow
anchor.adjustment: PopupAdjustment.Flip
anchor.onAnchoring: {
if (!root.menuWindow) return
const global = iconDelegate.mapToGlobal(0, iconDelegate.height)
const local = root.menuWindow.contentItem.mapFromGlobal(
global.x, global.y
)
menuAnchor.anchor.rect = Qt.rect(
local.x, local.y,
iconDelegate.width, 0
)
PanelWindow {
id: trayMenuPopup
screen: root.targetScreen
visible: false
color: "transparent"
property bool popupOpen: false
property bool _pendingOpen: false
property var activeTrayItem: null
property var activeDelegate: null
function open(item, delegate) {
if (popupOpen) close()
hideTimer.stop()
_pendingOpen = false
activeTrayItem = item
activeDelegate = delegate
rootMenuOpener.menu = item.menu
visible = true
if (menuCol.height > 0) {
popupOpen = true
} else {
_pendingOpen = true
openFallbackTimer.restart()
}
}
function close() {
_pendingOpen = false
openFallbackTimer.stop()
popupOpen = false
hideTimer.restart()
}
Connections {
target: menuCol
function onHeightChanged() {
if (trayMenuPopup._pendingOpen && menuCol.height > 0) {
trayMenuPopup._pendingOpen = false
openFallbackTimer.stop()
trayMenuPopup.popupOpen = true
}
}
}
Timer {
id: openFallbackTimer
interval: 400
onTriggered: {
if (trayMenuPopup._pendingOpen) {
trayMenuPopup._pendingOpen = false
trayMenuPopup.popupOpen = true
}
}
}
Timer {
id: hideTimer
interval: Cfg.Config.popupAnimDuration + 70
onTriggered: {
trayMenuPopup.visible = false
rootMenuOpener.menu = null
trayMenuPopup.activeTrayItem = null
trayMenuPopup.activeDelegate = null
}
}
anchors { top: true; bottom: true; left: true; right: true }
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "main-shell-tray-menu"
WlrLayershell.exclusiveZone: -1
mask: popupOpen ? null : _trayMenuNoInput
Region { id: _trayMenuNoInput }
TapHandler { onTapped: trayMenuPopup.close() }
QsMenuOpener { id: rootMenuOpener }
Item {
id: menuCardWrapper
readonly property int cardW: 220
readonly property int pad: 10
x: {
if (!trayMenuPopup.activeDelegate)
return (parent.width - cardW) / 2
var gp = trayMenuPopup.activeDelegate.mapToGlobal(0, 0)
var cx = gp.x + trayMenuPopup.activeDelegate.width / 2
return Math.max(pad,
Math.min(cx - cardW / 2, parent.width - cardW - pad))
}
width: cardW
clip: true
anchors.top: root.isTop ? parent.top : undefined
anchors.bottom: root.isTop ? undefined : parent.bottom
anchors.topMargin: root.isTop ? (Cfg.Config.barHeight + 8) : 0
anchors.bottomMargin: root.isTop ? 0 : (Cfg.Config.barHeight + 8)
height: parent.height - Cfg.Config.barHeight - 8
Rectangle {
id: menuCard
width: parent.width
height: menuCol.height + 12
anchors.top: root.isTop ? parent.top : undefined
anchors.bottom: root.isTop ? undefined : parent.bottom
transform: Translate {
y: trayMenuPopup.popupOpen ? 0
: (root.isTop ? -menuCard.height : menuCard.height)
Behavior on y {
NumberAnimation {
duration: Cfg.Config.popupAnimDuration
easing.type: Easing.OutExpo
}
}
}
opacity: trayMenuPopup.popupOpen ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200 } }
color: root.tmBg
radius: Cfg.Config.popupRadius
border.color: root.tmBorder
border.width: Cfg.Config.popupBorderWidth
Column {
id: menuCol
anchors { left: parent.left; right: parent.right; top: parent.top }
anchors.margins: 6
spacing: 0
Repeater {
model: rootMenuOpener.children
delegate: Item {
id: menuDelegate
required property var modelData
property bool isSep: modelData.isSeparator
width: menuCol.width
height: isSep ? 5 : 32
Rectangle {
visible: isSep
anchors {
left: parent.left; right: parent.right
verticalCenter: parent.verticalCenter
}
anchors.leftMargin: 8
anchors.rightMargin: 8
height: 1
color: root.tmSeparator
}
Rectangle {
id: itemBg
visible: !isSep
anchors.fill: parent
anchors.margins: 2
radius: 10
property bool active: itemMouse.containsMouse
|| (submenuCard.visible
&& trayMenuPopup._submenuParentRef === menuDelegate)
color: active ? root.tmHover : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
anchors.rightMargin: 8
spacing: 8
Item {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
visible: !isSep
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.CheckBox
&& modelData.checkState === Qt.Checked
text: "✓"
color: root.tmCheck
font.pixelSize: 14
}
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.RadioButton
&& modelData.checkState === Qt.Checked
text: "●"
color: root.tmCheck
font.pixelSize: 9
}
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.RadioButton
&& modelData.checkState !== Qt.Checked
text: "○"
color: root.tmDim
font.pixelSize: 11
}
}
Image {
visible: !isSep && modelData.icon !== ""
source: modelData.icon
width: 16; height: 16
sourceSize: Qt.size(16, 16)
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.text
color: modelData.enabled ? root.tmText : root.tmDim
font.pixelSize: 13
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
visible: !isSep && modelData.hasChildren
text: "▶"
color: root.tmDim
font.pixelSize: 9
}
}
MouseArea {
id: itemMouse
anchors.fill: parent
hoverEnabled: true
enabled: !isSep && modelData.enabled
onClicked: {
if (modelData.hasChildren) {
closeSubmenuTimer.stop()
submenuDelayTimer.itemRef = menuDelegate
submenuDelayTimer.restart()
} else {
modelData.triggered()
trayMenuPopup.close()
}
}
onEntered: {
if (modelData.hasChildren) {
closeSubmenuTimer.stop()
submenuDelayTimer.itemRef = menuDelegate
submenuDelayTimer.restart()
} else {
closeSubmenuTimer.stop()
submenuCard.visible = false
}
}
onExited: {
if (modelData.hasChildren)
closeSubmenuTimer.restart()
}
}
}
}
}
}
}
}
property var _submenuParentRef: null
QsMenuOpener { id: subMenuOpener }
Timer {
id: submenuDelayTimer
interval: 250
property var itemRef: null
onTriggered: {
if (!itemRef) return
var entry = itemRef.modelData
if (!entry || !entry.hasChildren) return
subMenuOpener.menu = entry
trayMenuPopup._submenuParentRef = itemRef
submenuCard.visible = true
}
}
Timer {
id: closeSubmenuTimer
interval: 300
onTriggered: {
if (!submenuCard.containsMouse) {
submenuCard.visible = false
subMenuOpener.menu = null
trayMenuPopup._submenuParentRef = null
}
}
}
Rectangle {
id: submenuCard
visible: false
property bool containsMouse: false
property int subW: Math.max(submenuCol.width, 180)
x: {
if (!trayMenuPopup._submenuParentRef) return 0
var pItem = trayMenuPopup._submenuParentRef
var gp = pItem.mapToGlobal(0, 0)
var lp = trayMenuPopup.contentItem.mapFromGlobal(gp.x, gp.y)
var fromLeft = lp.x + pItem.width + 4
if (fromLeft + subW + 10 > trayMenuPopup.contentItem.width)
return Math.max(2, lp.x - subW - 4)
return fromLeft
}
y: {
if (!trayMenuPopup._submenuParentRef) return 0
var pItem = trayMenuPopup._submenuParentRef
var gp = pItem.mapToGlobal(0, 0)
var lp = trayMenuPopup.contentItem.mapFromGlobal(gp.x, gp.y)
return Math.max(2, lp.y - 2)
}
width: subW
height: submenuCol.height + 12
color: root.tmBg
radius: Cfg.Config.popupRadius
border.color: root.tmBorder
border.width: Cfg.Config.popupBorderWidth
HoverHandler {
onHoveredChanged: {
submenuCard.containsMouse = hovered
if (!hovered)
closeSubmenuTimer.restart()
}
}
Column {
id: submenuCol
anchors { left: parent.left; right: parent.right; top: parent.top }
anchors.margins: 6
spacing: 0
Repeater {
model: subMenuOpener.children
delegate: Item {
required property var modelData
property bool isSep: modelData.isSeparator
width: submenuCol.width
height: isSep ? 5 : 32
Rectangle {
visible: isSep
anchors {
left: parent.left; right: parent.right
verticalCenter: parent.verticalCenter
}
anchors.leftMargin: 8; anchors.rightMargin: 8
height: 1
color: root.tmSeparator
}
Rectangle {
id: subBg
visible: !isSep
anchors.fill: parent
anchors.margins: 2
radius: 10
color: subMouse.containsMouse ? root.tmHover : "transparent"
Behavior on color { ColorAnimation { duration: 120 } }
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8; anchors.rightMargin: 8
spacing: 8
Item {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
visible: !isSep
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.CheckBox
&& modelData.checkState === Qt.Checked
text: "✓"; color: root.tmCheck
font.pixelSize: 14
}
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.RadioButton
&& modelData.checkState === Qt.Checked
text: "●"; color: root.tmCheck
font.pixelSize: 9
}
Text {
anchors.centerIn: parent
visible: modelData.buttonType === QsMenuButtonType.RadioButton
&& modelData.checkState !== Qt.Checked
text: "○"; color: root.tmDim
font.pixelSize: 11
}
}
Image {
visible: !isSep && modelData.icon !== ""
source: modelData.icon
width: 16; height: 16
sourceSize: Qt.size(16, 16)
fillMode: Image.PreserveAspectFit
}
Text {
text: modelData.text
color: modelData.enabled ? root.tmText : root.tmDim
font.pixelSize: 13
Layout.fillWidth: true
elide: Text.ElideRight
}
}
MouseArea {
id: subMouse
anchors.fill: parent
hoverEnabled: true
enabled: !isSep && modelData.enabled
onClicked: {
modelData.triggered()
trayMenuPopup.close()
}
}
}
}