tray menu refresh
This commit is contained in:
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,7 +49,7 @@ Item {
|
|||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
font.family: "Material Symbols Rounded"
|
font.family: "Material Symbols Rounded"
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
text: ""
|
text: "\ue30f"
|
||||||
color: "#ffffff"
|
color: "#ffffff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -48,7 +48,7 @@ Item {
|
|||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: swayncProcess
|
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
|
running: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-18
@@ -3,8 +3,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Caelestia
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.DBusMenu
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
@@ -38,35 +38,30 @@ MouseArea {
|
|||||||
smooth: false
|
smooth: false
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
ImageAnalyser {
|
|
||||||
id: analyser
|
|
||||||
sourceItem: icon
|
|
||||||
rescaleSize: 22
|
|
||||||
}
|
|
||||||
|
|
||||||
TrayMenu {
|
TrayMenu {
|
||||||
id: trayMenu
|
id: trayMenu
|
||||||
menu: menuOpener
|
trayMenu: root.item?.menu
|
||||||
trayItemRect: root.globalPos
|
trayItemRect: root.globalPos
|
||||||
bar: root.bar
|
bar: root.bar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: trayMenu
|
||||||
|
function onVisibleChanged() {
|
||||||
|
if ( !trayMenu.visible ) {
|
||||||
|
trayMenu.trayMenu = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if ( mouse.button === Qt.LeftButton ) {
|
if ( mouse.button === Qt.LeftButton ) {
|
||||||
root.item.activate();
|
root.item.activate();
|
||||||
} else if ( mouse.button === Qt.RightButton ) {
|
} else if ( mouse.button === Qt.RightButton ) {
|
||||||
if ( trayMenu.menu != menuOpener ) {
|
trayMenu.trayMenu = root.item?.menu;
|
||||||
trayMenu.menu = menuOpener;
|
|
||||||
}
|
|
||||||
trayMenu.visible = !trayMenu.visible;
|
trayMenu.visible = !trayMenu.visible;
|
||||||
console.log(root.x);
|
trayMenu.focusGrab = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QsMenuOpener {
|
|
||||||
id: menuOpener
|
|
||||||
|
|
||||||
menu: root.item?.menu
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-97
@@ -1,24 +1,34 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.DBusMenu
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
import QtQml
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
signal menuActionTriggered()
|
signal menuActionTriggered()
|
||||||
required property QsMenuOpener menu
|
required property QsMenuHandle trayMenu
|
||||||
required property point trayItemRect
|
required property point trayItemRect
|
||||||
required property PanelWindow bar
|
required property PanelWindow bar
|
||||||
property var menuStack: []
|
property var menuStack: []
|
||||||
property real scaleValue: 0
|
property real scaleValue: 0
|
||||||
|
property alias focusGrab: grab.active
|
||||||
property int height: calcSize("h")
|
|
||||||
property int entryHeight: 30
|
property int entryHeight: 30
|
||||||
property int maxWidth: calcSize("w")
|
property int biggestWidth: 0
|
||||||
|
|
||||||
|
QsMenuOpener {
|
||||||
|
id: menuOpener
|
||||||
|
menu: root.trayMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
// onTrayMenuChanged: {
|
||||||
|
// listLayout.forceLayout();
|
||||||
|
// }
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
@@ -29,61 +39,29 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcSize(String) {
|
mask: Region { id: mask; item: menuRect }
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
if ( root.menuStack.length > 0 ) {
|
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;
|
backEntry.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateMask() {
|
||||||
|
root.mask.changed();
|
||||||
|
}
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if ( !visible ) {
|
if ( visible ) {
|
||||||
goBack();
|
|
||||||
} else {
|
|
||||||
scaleValue = 0;
|
scaleValue = 0;
|
||||||
scaleAnimation.start();
|
scaleAnimation.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: tempMetrics
|
|
||||||
text: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
id: scaleAnimation
|
id: scaleAnimation
|
||||||
target: root
|
target: root
|
||||||
@@ -92,18 +70,22 @@ PanelWindow {
|
|||||||
to: 1
|
to: 1
|
||||||
duration: 150
|
duration: 150
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
|
onStopped: {
|
||||||
|
root.updateMask();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
HyprlandFocusGrab {
|
||||||
anchors.fill: parent
|
id: grab
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
windows: [ root ]
|
||||||
onClicked: {
|
active: false
|
||||||
|
onCleared: {
|
||||||
root.visible = false;
|
root.visible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on menu {
|
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
|
id: menuChangeAnimation
|
||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: MaterialEasing.standardTime / 2
|
duration: MaterialEasing.standardTime / 2
|
||||||
@@ -149,33 +131,24 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMenuActionTriggered: {
|
||||||
|
if ( root.menuStack.length > 0 ) {
|
||||||
|
backEntry.visible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: menuRect
|
id: menuRect
|
||||||
x: root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11
|
x: Math.round( root.trayItemRect.x - ( menuRect.implicitWidth / 2 ) + 11 )
|
||||||
y: root.trayItemRect.y - 5
|
y: Math.round( root.trayItemRect.y - 5 )
|
||||||
implicitHeight: root.height
|
implicitWidth: listLayout.contentWidth + 10
|
||||||
implicitWidth: root.maxWidth + 20
|
implicitHeight: listLayout.contentHeight + ( root.menuStack.length > 0 ? root.entryHeight + 10 : 10 )
|
||||||
color: "#80151515"
|
color: "#80151515"
|
||||||
radius: 8
|
radius: 8
|
||||||
border.color: "#40FFFFFF"
|
border.color: "#40FFFFFF"
|
||||||
clip: true
|
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: [
|
transform: [
|
||||||
Scale {
|
Scale {
|
||||||
origin.x: menuRect.width / 2
|
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 {
|
ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
@@ -201,31 +187,11 @@ PanelWindow {
|
|||||||
ListView {
|
ListView {
|
||||||
id: listLayout
|
id: listLayout
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: root.height - ( root.menuStack.length > 0 ? root.entryHeight + 10 : 0 )
|
Layout.preferredHeight: contentHeight
|
||||||
spacing: 2
|
spacing: 2
|
||||||
model: ScriptModel {
|
contentWidth: root.biggestWidth
|
||||||
values: [...root.menu?.children.values]
|
contentHeight: contentItem.childrenRect.height
|
||||||
}
|
model: menuOpener.children
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
id: menuItem
|
id: menuItem
|
||||||
@@ -235,7 +201,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
property bool containsMouseAndEnabled: mouseArea.containsMouse && menuItem.modelData.enabled
|
||||||
property bool containsMouseAndNotEnabled: 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.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
|
height: menuItem.modelData.isSeparator ? 1 : root.entryHeight
|
||||||
@@ -243,6 +209,19 @@ PanelWindow {
|
|||||||
radius: 4
|
radius: 4
|
||||||
visible: true
|
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 {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -254,12 +233,15 @@ PanelWindow {
|
|||||||
if ( !menuItem.modelData.hasChildren ) {
|
if ( !menuItem.modelData.hasChildren ) {
|
||||||
if ( menuItem.modelData.enabled ) {
|
if ( menuItem.modelData.enabled ) {
|
||||||
menuItem.modelData.triggered();
|
menuItem.modelData.triggered();
|
||||||
root.menuActionTriggered();
|
|
||||||
root.visible = false;
|
root.visible = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
root.menuStack.push(root.menu);
|
root.menuStack.push(root.trayMenu);
|
||||||
root.menu = menuItem.child;
|
menuChangeAnimation.start();
|
||||||
|
root.biggestWidth = 0;
|
||||||
|
root.trayMenu = menuItem.modelData;
|
||||||
|
listLayout.positionViewAtBeginning();
|
||||||
|
root.menuActionTriggered();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user