tray menu refresh

This commit is contained in:
Zacharias-Brohn
2025-11-08 14:42:20 +01:00
parent 70916e4755
commit 6d52325d89
12 changed files with 748 additions and 454 deletions
-47
View File
@@ -1,47 +0,0 @@
// CustomTrayMenu.qml
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import QtQuick.Window // for Window, flags
import qs.Modules
PopupWindow {
id: popup
color: "#00202020"
required property QsMenuOpener trayMenu
Column {
id: contentColumn
anchors.fill: parent
spacing: 4
Repeater {
id: repeater
model: popup.trayMenu.children
Row {
id: entryRow
anchors.fill: parent
property var entry: modelData
MouseArea {
anchors.fill: parent
onClicked: {
if (entryRow.entry.triggered) {
entryRow.entry.triggered()
}
popup.visible = false
}
}
Image {
source: entryRow.entry.icon
width: 20; height: 20
visible: entryRow.entry.icon !== ""
}
Text {
text: entryRow.entry.text
color: "black"
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ Item {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 16
text: "󰢮"
text: "\ue30f"
color: "#ffffff"
}
-91
View File
@@ -1,91 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
import Quickshell.Hyprland
PopupWindow {
id: root
required property QsMenuHandle menu
property int height: entryCount * ( entryHeight + 3 )
property int entryHeight: 30
property int entryCount: 0
implicitWidth: 300
implicitHeight: height
color: "transparent"
QsMenuOpener {
id: menuOpener
menu: root.menu
}
HyprlandFocusGrab {
id: grab
windows: [ root ]
onCleared: {
root.visible = false;
}
}
onVisibleChanged: {
grab.active = root.visible;
}
Rectangle {
id: menuRect
anchors.fill: parent
color: "#90000000"
radius: 8
border.color: "#10FFFFFF"
ColumnLayout {
id: columnLayout
anchors.fill: parent
anchors.margins: 5
spacing: 2
Repeater {
id: repeater
model: menuOpener.children
Rectangle {
id: menuItem
width: root.implicitWidth
Layout.maximumWidth: parent.width
Layout.fillHeight: true
height: root.entryHeight
color: mouseArea.containsMouse && !modelData.isSeparator ? "#15FFFFFF" : "transparent"
radius: 4
visible: modelData.isSeparator ? false : true
required property QsMenuEntry modelData
Component.onCompleted: {
if ( !modelData.isSeparator ) {
root.entryCount += 1;
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
preventStealing: true
propagateComposedEvents: true
acceptedButtons: Qt.LeftButton
onClicked: {
if ( !menuItem.modelData.hasChildren ) {
menuItem.modelData.triggered();
root.visible = false;
}
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
text: menuItem.modelData.text
color: "white"
}
}
}
}
}
}
+1 -1
View File
@@ -48,7 +48,7 @@ Item {
Process {
id: swayncProcess
command: ["swaync-client", "-t", "-sw"]
command: ["sh", "-c", "qs -p /home/zach/GitProjects/z-bar-qt/notification-test/shell.qml ipc call root showCenter"]
running: false
}
+13 -18
View File
@@ -3,8 +3,8 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import Caelestia
import Quickshell
import Quickshell.DBusMenu
import Quickshell.Widgets
import Quickshell.Hyprland
import Quickshell.Services.SystemTray
@@ -38,35 +38,30 @@ MouseArea {
smooth: false
asynchronous: true
ImageAnalyser {
id: analyser
sourceItem: icon
rescaleSize: 22
}
TrayMenu {
id: trayMenu
menu: menuOpener
trayMenu: root.item?.menu
trayItemRect: root.globalPos
bar: root.bar
}
}
Connections {
target: trayMenu
function onVisibleChanged() {
if ( !trayMenu.visible ) {
trayMenu.trayMenu = null;
}
}
}
onClicked: {
if ( mouse.button === Qt.LeftButton ) {
root.item.activate();
} else if ( mouse.button === Qt.RightButton ) {
if ( trayMenu.menu != menuOpener ) {
trayMenu.menu = menuOpener;
}
trayMenu.trayMenu = root.item?.menu;
trayMenu.visible = !trayMenu.visible;
console.log(root.x);
trayMenu.focusGrab = true;
}
}
QsMenuOpener {
id: menuOpener
menu: root.item?.menu
}
}
+79 -97
View File
@@ -1,24 +1,34 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.DBusMenu
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell.Hyprland
import QtQml
PanelWindow {
id: root
signal menuActionTriggered()
required property QsMenuOpener menu
required property QsMenuHandle trayMenu
required property point trayItemRect
required property PanelWindow bar
property var menuStack: []
property real scaleValue: 0
property int height: calcSize("h")
property alias focusGrab: grab.active
property int entryHeight: 30
property int maxWidth: calcSize("w")
property int biggestWidth: 0
QsMenuOpener {
id: menuOpener
menu: root.trayMenu
}
// onTrayMenuChanged: {
// listLayout.forceLayout();
// }
visible: false
color: "transparent"
@@ -29,61 +39,29 @@ PanelWindow {
bottom: true
}
function calcSize(String) {
if ( String === "w" ) {
let menuWidth = 0;
for ( let i = 0; i < listLayout.count; i++ ) {
if ( !listLayout.model.values[i].isSeparator ) {
let entry = listLayout.model.values[i];
tempMetrics.text = entry.text;
let textWidth = tempMetrics.width + 20 + (entry.icon ?? "" ? 30 : 0) + (entry.hasChildren ? 30 : 0);
if ( textWidth > menuWidth ) {
menuWidth = textWidth;
}
}
}
return menuWidth;
}
if ( String === "h" ) {
let count = 0;
let separatorCount = 0;
for (let i = 0; i < listLayout.count; i++) {
if (!listLayout.model.values[i].isSeparator) {
count++;
} else {
separatorCount++;
}
}
if ( root.menuStack.length > 0 ) {
backEntry.visible = true;
count++;
}
return (count * entryHeight) + ((count - 1) * 2) + (separatorCount * 3) + 10;
}
}
mask: Region { id: mask; item: menuRect }
function goBack() {
if ( root.menuStack.length > 0 ) {
root.menu = root.menuStack.pop();
menuChangeAnimation.start();
root.biggestWidth = 0;
root.trayMenu = root.menuStack.pop();
listLayout.positionViewAtBeginning();
backEntry.visible = false;
}
}
function updateMask() {
root.mask.changed();
}
onVisibleChanged: {
if ( !visible ) {
goBack();
} else {
if ( visible ) {
scaleValue = 0;
scaleAnimation.start();
}
}
TextMetrics {
id: tempMetrics
text: ""
}
NumberAnimation {
id: scaleAnimation
target: root
@@ -92,18 +70,22 @@ PanelWindow {
to: 1
duration: 150
easing.type: Easing.OutCubic
onStopped: {
root.updateMask();
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
HyprlandFocusGrab {
id: grab
windows: [ root ]
active: false
onCleared: {
root.visible = false;
}
}
Behavior on menu {
SequentialAnimation {
id: menuChangeAnimation
ParallelAnimation {
NumberAnimation {
duration: MaterialEasing.standardTime / 2
@@ -149,33 +131,24 @@ PanelWindow {
}
}
}
onMenuActionTriggered: {
if ( root.menuStack.length > 0 ) {
backEntry.visible = true;
}
}
Rectangle {
id: menuRect
x: root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11
y: root.trayItemRect.y - 5
implicitHeight: root.height
implicitWidth: root.maxWidth + 20
x: Math.round( root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11 )
y: Math.round( root.trayItemRect.y - 5 )
implicitWidth: listLayout.contentWidth + 10
implicitHeight: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
color: "#80151515"
radius: 8
border.color: "#40FFFFFF"
clip: true
Behavior on implicitHeight {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
Behavior on implicitWidth {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
transform: [
Scale {
origin.x: menuRect.width / 2
@@ -185,6 +158,19 @@ PanelWindow {
}
]
Behavior on implicitWidth {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
Behavior on implicitHeight {
NumberAnimation {
duration: MaterialEasing.standardTime
easing.bezierCurve: MaterialEasing.standard
}
}
ColumnLayout {
id: columnLayout
@@ -201,31 +187,11 @@ PanelWindow {
ListView {
id: listLayout
Layout.fillWidth: true
Layout.preferredHeight: root.height - ( root.menuStack.length > 0 ? root.entryHeight + 10 : 0 )
Layout.preferredHeight: contentHeight
spacing: 2
model: ScriptModel {
values: [...root.menu?.children.values]
}
add: Transition {
NumberAnimation {
property: "x"
from: listLayout.width
to: 0
duration: 200
easing.type: Easing.OutCubic
}
}
move: Transition {
NumberAnimation {
property: "x"
from: 0
to: -listLayout.width
duration: 200
easing.type: Easing.InCubic
}
}
contentWidth: root.biggestWidth
contentHeight: contentItem.childrenRect.height
model: menuOpener.children
delegate: Rectangle {
id: menuItem
@@ -235,7 +201,7 @@ PanelWindow {
}
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
property bool containsMouseAndNotEnabled: mouseArea.containsMouse && !menuItem.modelData.enabled
width: root.implicitWidth
width: widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20
anchors.left: parent.left
anchors.right: parent.right
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
@@ -243,6 +209,19 @@ PanelWindow {
radius: 4
visible: true
Component.onCompleted: {
var biggestWidth = root.biggestWidth;
var currentWidth = widthMetrics.width + (menuItem.modelData.icon ?? "" ? 30 : 0) + (menuItem.modelData.hasChildren ? 30 : 0) + 20;
if ( currentWidth > biggestWidth ) {
root.biggestWidth = currentWidth;
}
}
TextMetrics {
id: widthMetrics
text: menuItem.modelData.text
}
MouseArea {
id: mouseArea
anchors.fill: parent
@@ -254,12 +233,15 @@ PanelWindow {
if ( !menuItem.modelData.hasChildren ) {
if ( menuItem.modelData.enabled ) {
menuItem.modelData.triggered();
root.menuActionTriggered();
root.visible = false;
}
} else {
root.menuStack.push(root.menu);
root.menu = menuItem.child;
root.menuStack.push(root.trayMenu);
menuChangeAnimation.start();
root.biggestWidth = 0;
root.trayMenu = menuItem.modelData;
listLayout.positionViewAtBeginning();
root.menuActionTriggered();
}
}
}
+155
View File
@@ -0,0 +1,155 @@
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import QtQuick.Layouts
import QtQuick
import Quickshell.Services.Notifications
PanelWindow {
id: root
color: "transparent"
anchors {
top: true
right: true
left: true
bottom: true
}
mask: Region { item: backgroundRect }
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay
required property Notification notif
required property int centerX
required property list<int> notifIndex
property int index: notifIndex.indexOf(notif.id)
signal notifDestroy()
Component.onCompleted: {
openAnim.start();
}
Timer {
id: timeout
interval: 5000
onTriggered: {
closeAnim.start();
}
}
NumberAnimation {
id: openAnim
target: backgroundRect
property: "x"
from: root.centerX
to: root.centerX - backgroundRect.implicitWidth - 20
duration: 200
easing.type: Easing.InOutQuad
onStopped: { timeout.start(); }
}
NumberAnimation {
id: closeAnim
target: backgroundRect
property: "x"
from: root.centerX - backgroundRect.implicitWidth - 20
to: root.centerX
duration: 200
easing.type: Easing.InOutQuad
onStopped: {
root.destroy();
root.notifDestroy();
}
}
Rectangle {
id: backgroundRect
implicitWidth: 400
implicitHeight: 80
x: root.centerX - implicitWidth - 20
y: 34 + 20 + ( root.index * ( implicitHeight + 10 ))
color: "#801a1a1a"
border.color: "#555555"
radius: 8
Behavior on y {
NumberAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
IconImage {
source: root.notif.image
Layout.preferredWidth: 64
Layout.preferredHeight: 64
Layout.alignment: Qt.AlignHCenter | Qt.AlignLeft
visible: root.notif.image !== ""
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: root.notif.image !== "" ? 0 : 16
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Text {
text: root.notif.appName
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
wrapMode: Text.NoWrap
Layout.fillWidth: true
}
Text {
text: root.notif.summary
color: "white"
font.pointSize: 10
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
Text {
text: root.notif.body
color: "#dddddd"
font.pointSize: 8
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 6
anchors.topMargin: 6
width: 18
height: 18
color: closeArea.containsMouse ? "#FF6077" : "transparent"
radius: 9
Text {
anchors.centerIn: parent
text: "✕"
color: closeArea.containsMouse ? "white" : "#888888"
font.pointSize: 12
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.notif.dismiss();
root.visible = false;
}
}
}
}
}
+420
View File
@@ -0,0 +1,420 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Hyprland
import Quickshell.Widgets
import Quickshell.Io
import QtQuick.Layouts
import QtQuick.Controls.FluentWinUI3
import QtQuick.Effects
import QtQuick
import Quickshell.Services.Notifications
PanelWindow {
id: root
color: "transparent"
anchors {
top: true
right: true
left: true
bottom: true
}
required property list<Notification> notifications
property bool centerShown: false
property alias posX: backgroundRect.x
property alias doNotDisturb: dndSwitch.checked
property alias groupedData: groupedData
visible: false
IpcHandler {
id: ipcHandler
target: "root"
function showCenter(): void { root.centerShown = !root.centerShown; }
}
onVisibleChanged: {
if ( root.visible ) {
showAnimation.start();
}
}
onCenterShownChanged: {
if ( !root.centerShown ) {
closeAnimation.start();
closeTimer.start();
} else {
root.visible = true;
}
}
Timer {
id: closeTimer
interval: 300
onTriggered: {
root.visible = false;
}
}
NumberAnimation {
id: showAnimation
target: backgroundRect
property: "x"
from: Screen.width
to: Screen.width - backgroundRect.implicitWidth - 10
duration: 300
easing.type: Easing.OutCubic
}
NumberAnimation {
id: closeAnimation
target: backgroundRect
property: "x"
from: Screen.width - backgroundRect.implicitWidth - 10
to: Screen.width
duration: 300
easing.type: Easing.OutCubic
}
ListModel {
id: groupedData
property int totalCount: 0
property var groupMap: ({})
function mapGroups() {
groupedData.groupMap = {};
for ( var i = 0; i < groupedData.count; i++ ) {
var name = get(i).name;
groupMap[ name ] = i;
}
}
function updateCount() {
var count = 0;
for ( var i = 0; i < groupedData.count; i++ ) {
count += get(i).notifications.count;
}
totalCount = count;
}
function ensureGroup(appName) {
for ( var i = 0; i < count; i++ ) {
if ( get(i).name === appName ) {
return get(i).notifications;
}
}
var model = Qt.createQmlObject('import QtQuick 2.0; ListModel {}', root);
append({ name: appName, notifications: model });
mapGroups();
return model;
}
function addNotification(notif) {
var appName = notif.appName
var model = ensureGroup(appName);
model.insert(0, notif);
updateCount();
}
function removeNotification(notif) {
var appName = notif.appName || "Unknown";
var group = get( groupMap[ appName ]);
if ( group.name === appName ) {
root.notifications[ idMap[ notif.id ]].dismiss();
if ( group.notifications.count === 0 ) {
remove( groupMap[ appName ], 1 );
mapGroups();
}
}
updateCount();
}
function removeGroup(notif) {
var appName = notif.appName || "Unknown";
var group = get(groupMap[ appName ]);
if ( group.name === appName ) {
for ( var i = 0; i < group.notifications.count; i++ ) {
var item = group.notifications.get( i );
item.dismiss();
}
remove( groupMap[ appName ], 1 );
updateCount();
mapGroups();
}
}
function resetGroups(notifications) {
groupedData.clear();
for (var i = 0; i < root.notifications.length; i++) {
addNotification(root.notifications[i]);
}
updateCount();
}
Component.onCompleted: {
resetGroups(root.notifications)
}
}
Connections {
target: root
function onNotificationsChanged() {
if ( root.notifications.length > groupedData.totalCount ) {
groupedData.addNotification( root.notifications[ root.notifications.length - 1 ] );
groupedData.mapGroups();
console.log(root.notifications)
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: {
if ( root.centerShown ) {
root.centerShown = false;
console.log("groups", groupedData.count);
console.log(root.notifications)
}
}
}
Rectangle {
id: backgroundRect
y: 10
x: Screen.width
implicitWidth: 400
implicitHeight: root.height - 20
color: "#801a1a1a"
radius: 8
border.color: "#555555"
border.width: 1
clip: true
ColumnLayout {
id: mainLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Layout.preferredHeight: 30
Layout.fillWidth: true
Text {
text: "Notifications"
color: "white"
font.bold: true
font.pointSize: 16
Layout.fillWidth: true
}
Switch {
id: dndSwitch
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: "Do Not Disturb"
}
}
Rectangle {
color: "#333333"
Layout.preferredHeight: 1
Layout.fillWidth: true
}
Column {
id: notificationColumn
Layout.fillHeight: true
Layout.fillWidth: true
// width: mainLayout.width
spacing: 10
clip: true
move: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
add: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
ListView {
id: groupListView
model: groupedData
spacing: 10
width: 400
height: Math.min(contentHeight, notificationColumn.height)
// Layout.fillWidth: true
// Layout.fillHeight: true
delegate: ListView {
required property var modelData
required property int index
property bool isExpanded: false
property bool isExpandedAnim: false
id: listView
visible: true
property ListModel notificationsModel: modelData.notifications
model: notificationsModel
width: notificationColumn.width
implicitHeight: listView.isExpandedAnim ? contentHeight : 80
clip: true
spacing: 10
onIsExpandedChanged: {
if ( !isExpanded ) {
collapseAnim.start();
} else {
expandAnim.start();
}
}
NumberAnimation {
id: collapseAnim
target: listView
property: "implicitHeight"
to: 80
from: listView.contentHeight
duration: 80
easing.type: Easing.InOutQuad
onStopped: {
listView.isExpandedAnim = listView.isExpanded;
}
}
NumberAnimation {
id: expandAnim
target: listView
property: "implicitHeight"
from: 80
to: listView.contentHeight
duration: 80
easing.type: Easing.InOutQuad
onStopped: {
listView.isExpandedAnim = listView.isExpanded;
}
}
Behavior on y {
NumberAnimation {
duration: 200
easing.type: Easing.InOutQuad
}
}
displaced: Transition {
NumberAnimation {
properties: "y,x"
duration: 200
easing.type: Easing.InOutQuad
}
}
delegate: Rectangle {
id: notificationItem
required property var modelData
required property int index
width: listView.width
height: 80
color: "#801a1a1a"
border.color: "#555555"
border.width: 1
radius: 8
clip: true
visible: true
opacity: 1
MouseArea {
anchors.fill: parent
onClicked: {
listView.isExpanded ? ( notificationItem.index === 0 ? listView.isExpanded = false : null ) : listView.isExpanded = true;
}
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 10
IconImage {
source: notificationItem.modelData.image
Layout.preferredWidth: 48
Layout.preferredHeight: 48
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 4
Text {
text: notificationItem.modelData.appName
color: "white"
font.bold: true
font.pointSize: 12
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: notificationItem.modelData.summary
color: "white"
font.pointSize: 10
elide: Text.ElideRight
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
Text {
text: notificationItem.modelData.body
color: "#dddddd"
font.pointSize: 8
elide: Text.ElideRight
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter
}
}
}
Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 6
anchors.topMargin: 6
width: 18
height: 18
color: closeArea.containsMouse ? "#FF6077" : "transparent"
radius: 9
Text {
anchors.centerIn: parent
text: "✕"
color: closeArea.containsMouse ? "white" : "#888888"
font.pointSize: 12
}
MouseArea {
id: closeArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
root.notifications[modelData].dismiss();
}
}
}
}
}
}
}
}
}
}
+40
View File
@@ -0,0 +1,40 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
Scope {
id: root
property list<int> notifIds: []
NotificationServer {
id: notificationServer
imageSupported: true
actionsSupported: true
persistenceSupported: true
bodyImagesSupported: true
bodySupported: true
onNotification: {
notification.tracked = true;
notification.receivedTime = Date.now();
root.notifIds.push(notification.id);
notificationCenter.groupedData.addNotification(notification);
notificationComponent.createObject(root, { notif: notification, visible: !notificationCenter.doNotDisturb });
}
}
Component {
id: notificationComponent
Notif {
centerX: notificationCenter.posX
notifIndex: root.notifIds
onNotifDestroy: {
root.notifIds.shift();
}
}
}
NotificationCenter {
id: notificationCenter
notifications: notificationServer.trackedNotifications.values
}
}
-16
View File
@@ -1,16 +0,0 @@
pragma Singleton
import Quickshell
import Quickshell.Services.Notifications
Singleton {
id: root
function getTrayIcon(id: string, icon: string): string {
if (icon.includes("?path=")) {
const [name, path] = icon.split("?path=");
icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`);
}
return icon;
}
}
-50
View File
@@ -1,50 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import QtQuick.Layouts
PopupWindow {
id: root
required property QsMenuHandle menu
property int height: menuOpener.children.values.length * entryHeight
property int entryHeight: 25
implicitWidth: 300
implicitHeight: height
QsMenuOpener {
id: menuOpener
menu: root.menu
}
ColumnLayout {
anchors.fill: parent
spacing: 0
Repeater {
model: menuOpener.children
Rectangle {
id: menuItem
width: root.implicitWidth
height: modelData.isSeparator ? 5 : root.entryHeight
color: mouseArea.containsMouse ? "#22000000" : "transparent"
required property QsMenuEntry modelData
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton
onClicked: {
menuItem.modelData.triggered();
root.visible = false;
}
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
text: menuItem.modelData.text
}
}
}
}
}
}
-94
View File
@@ -1,94 +0,0 @@
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Services.SystemTray
import Quickshell.Widgets
import Caelestia
import QtQuick
import QtQuick.Layouts
import qs.modules
Variants {
model: Quickshell.screens
Scope {
id: scope
required property ShellScreen modelData
PanelWindow {
screen: scope.modelData
color: "#99000000"
anchors {
top: true
left: true
right: true
}
implicitHeight: 34
Rectangle {
anchors.centerIn: parent
implicitHeight: parent.height
implicitWidth: rowL.implicitWidth + 10
color: "transparent"
RowLayout {
spacing: 8
id: rowL
anchors.centerIn: parent
Repeater {
model: SystemTray.items
MouseArea {
id: trayItem
required property SystemTrayItem modelData
property SystemTrayItem item: modelData
implicitWidth: 20
implicitHeight: 20
hoverEnabled: true
acceptedButtons: Qt.RightButton | Qt.LeftButton
IconImage {
id: icon
anchors.fill: parent
layer.enabled: true
layer.onEnabledChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
onStatusChanged: {
if (layer.enabled && status === Image.Ready)
analyser.requestUpdate();
}
source: GetIcons.getTrayIcon(trayItem.item.id, trayItem.item.icon)
mipmap: false
asynchronous: true
ImageAnalyser {
id: analyser
sourceItem: icon
}
}
Popout {
id: popout
menu: trayItem.item.menu
anchor.item: trayItem
anchor.edges: Edges.Bottom | Edges.Left
}
onClicked: {
if ( mouse.button === Qt.LeftButton ) {
trayItem.item.activate();
} else if ( mouse.button === Qt.RightButton ) {
popout.visible = !popout.visible;
}
}
}
}
}
}
}
}
}