Notifications WIP
This commit is contained in:
@@ -9,9 +9,9 @@ Rectangle {
|
|||||||
|
|
||||||
property string monitor: ""
|
property string monitor: ""
|
||||||
property string icon: " "
|
property string icon: " "
|
||||||
property bool audioPipewireActive: Pipewire.defaultAudioSink? true:false
|
property bool audioPipewireActive: Pipewire.defaultAudioSink ? true : false
|
||||||
property int volume: audioPipewireActive? Math.round(Pipewire.defaultAudioSink.audio.volume * 100): 30
|
property var sink: audioPipewireActive ? Pipewire.defaultAudioSink : null
|
||||||
property var sink: audioPipewireActive? Pipewire.defaultAudioSink : null
|
property int volume: audioPipewireActive ? Math.round(sink.audio.volume * 100) : 30
|
||||||
|
|
||||||
implicitWidth: audioText.implicitWidth * 1.6
|
implicitWidth: audioText.implicitWidth * 1.6
|
||||||
implicitHeight: Theme.heightGaps
|
implicitHeight: Theme.heightGaps
|
||||||
@@ -20,12 +20,11 @@ Rectangle {
|
|||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "Mute"
|
name: "Mute"
|
||||||
when: audioWidget.volume == 0
|
when: audioWidget.volume == 0 || audioWidget.sink.audio.muted
|
||||||
|
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
audioWidget.icon: " "
|
audioWidget.icon: " "
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "low volume"
|
name: "low volume"
|
||||||
@@ -34,7 +33,6 @@ Rectangle {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
audioWidget.icon: " "
|
audioWidget.icon: " "
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
name: "high volume"
|
name: "high volume"
|
||||||
@@ -43,7 +41,6 @@ Rectangle {
|
|||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
audioWidget.icon: " "
|
audioWidget.icon: " "
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -56,13 +53,13 @@ Rectangle {
|
|||||||
audioWidget.volume = Math.round(Pipewire.defaultAudioSink.audio.volume * 100);
|
audioWidget.volume = Math.round(Pipewire.defaultAudioSink.audio.volume * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: audioWidget.audioPipewireActive? Pipewire.defaultAudioSink.audio: null
|
target: audioWidget.audioPipewireActive ? Pipewire.defaultAudioSink.audio : null
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: audioText
|
id: audioText
|
||||||
|
|
||||||
property string audioTextText: audioWidget.audioPipewireActive? audioWidget.icon + " " + Math.round(Pipewire.defaultAudioSink.audio.volume * 100).toString() + "%" : ""
|
property string audioTextText: audioWidget.audioPipewireActive ? audioWidget.icon + " " + Math.round(Pipewire.defaultAudioSink.audio.volume * 100).toString() + "%" : ""
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: audioTextText
|
text: audioTextText
|
||||||
@@ -81,26 +78,31 @@ Rectangle {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: {
|
onEntered: {
|
||||||
PopUpHover.start(audioWidget,"audio")
|
PopUpHover.start(audioWidget, "audio");
|
||||||
}
|
}
|
||||||
onExited: {
|
onExited: {
|
||||||
// PopUpHover.exit()
|
// PopUpHover.exit()
|
||||||
PopUpHover.exit()
|
PopUpHover.exit();
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: mouse => {
|
||||||
audioWidgetProcess.startDetached();
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
parent.sink.audio.muted = !parent.sink.audio.muted;
|
||||||
|
} else if (mouse.button === Qt.LeftButton) {
|
||||||
|
audioWidgetProcess.startDetached();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onWheel: (wheel) => {
|
onWheel: wheel => {
|
||||||
if (wheel.angleDelta.y > 0) {
|
if (wheel.angleDelta.y > 0) {
|
||||||
if (0)
|
if (0)
|
||||||
return ;
|
return;
|
||||||
|
|
||||||
Pipewire.defaultAudioSink.audio.volume = (audioWidget.volume + 5) / 100;
|
Pipewire.defaultAudioSink.audio.volume = (audioWidget.volume + 5) / 100;
|
||||||
} else if (wheel.angleDelta.y < 0) {
|
} else if (wheel.angleDelta.y < 0) {
|
||||||
if (0)
|
if (0)
|
||||||
return ;
|
return;
|
||||||
|
|
||||||
Pipewire.defaultAudioSink.audio.volume = (audioWidget.volume - 5) / 100;
|
Pipewire.defaultAudioSink.audio.volume = (audioWidget.volume - 5) / 100;
|
||||||
}
|
}
|
||||||
|
|||||||
54
Bar.qml
54
Bar.qml
@@ -5,15 +5,15 @@ PanelWindow {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var modelData: null
|
property var modelData: null
|
||||||
property var panelMonitor: modelData? modelData.name:"DP-1"
|
property var panelMonitor: modelData ? modelData.name : "DP-1"
|
||||||
property var barComponentsLeft: []
|
property var barComponentsLeft: []
|
||||||
property var barComponentsCenter: []
|
property var barComponentsCenter: []
|
||||||
property var barComponentsRight: []
|
property var barComponentsRight: []
|
||||||
|
|
||||||
screen:modelData
|
screen: modelData
|
||||||
implicitHeight: Theme.barSize
|
implicitHeight: Theme.barSize
|
||||||
|
|
||||||
color: Qt.rgba(0.68, 0.75, 0.88,0)
|
color: Qt.rgba(0.68, 0.75, 0.88, 0)
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: true
|
top: true
|
||||||
@@ -21,31 +21,31 @@ PanelWindow {
|
|||||||
right: true
|
right: true
|
||||||
}
|
}
|
||||||
|
|
||||||
BarArea{
|
BarArea {
|
||||||
anchors{
|
anchors {
|
||||||
left:parent.left
|
left: parent.left
|
||||||
verticalCenter:parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
|
||||||
monitor:root.panelMonitor
|
|
||||||
components:root.barComponentsLeft
|
|
||||||
}
|
|
||||||
|
|
||||||
BarArea{
|
|
||||||
anchors{
|
|
||||||
horizontalCenter:parent.horizontalCenter
|
|
||||||
verticalCenter:parent.verticalCenter
|
|
||||||
}
|
|
||||||
monitor:root.panelMonitor
|
|
||||||
components:root.barComponentsCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
BarArea{
|
|
||||||
anchors{
|
|
||||||
right:parent.right
|
|
||||||
verticalCenter:parent.verticalCenter
|
|
||||||
}
|
}
|
||||||
monitor: root.panelMonitor
|
monitor: root.panelMonitor
|
||||||
components:root.barComponentsRight
|
components: root.barComponentsLeft
|
||||||
}
|
}
|
||||||
// Helper function to set monitor property on loaded items
|
|
||||||
|
BarArea {
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
monitor: root.panelMonitor
|
||||||
|
components: root.barComponentsCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
BarArea {
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
monitor: root.panelMonitor
|
||||||
|
components: root.barComponentsRight
|
||||||
|
}
|
||||||
|
// Helper function to set monitor property on loaded items
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
import Quickshell.Services.Notifications
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
property var monitor: ""
|
property var monitor: ""
|
||||||
|
property bool createWindow: false
|
||||||
|
|
||||||
MarginWrapperManager {
|
MarginWrapperManager {
|
||||||
rightMargin: Theme.gaps
|
rightMargin: Theme.gaps
|
||||||
@@ -22,19 +27,30 @@ Item {
|
|||||||
id: clockText
|
id: clockText
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: "OPA!"
|
text: {
|
||||||
|
NotificationService.notificationsNumber > 0 ? "\udb80\udc9a "+NotificationService.notificationsNumber : "\ueaa2";
|
||||||
|
}
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pixelSize: Theme.pixelSize
|
font.pixelSize: Theme.pixelSize
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
color: Theme.textColor
|
color: Theme.textColor
|
||||||
}
|
}
|
||||||
MouseArea{
|
|
||||||
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log(Notifications.tracked)
|
console.log(NotificationService.currentNotification.image);
|
||||||
|
root.createWindow = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
LazyLoader {
|
||||||
|
id: windowLoader
|
||||||
|
|
||||||
|
active: root.createWindow
|
||||||
|
|
||||||
|
component: NotificationWindow {
|
||||||
|
anchor.item:root
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
Services/NotificationService.qml
Normal file
66
Services/NotificationService.qml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: notificationRoot
|
||||||
|
|
||||||
|
|
||||||
|
readonly property var notificationServer: notificationServer
|
||||||
|
readonly property var trackedNotifications: notificationServer.trackedNotifications
|
||||||
|
readonly property var notificationsNumber: notificationServer.trackedNotifications.values.length
|
||||||
|
property bool shouldShowOsd: false
|
||||||
|
property var currentNotification: null
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notificationServer
|
||||||
|
imageSupported: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onNotification(notif) {
|
||||||
|
if (notif.body == "MediaOngoingActivity") {
|
||||||
|
notif.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notif.tracked = true;
|
||||||
|
notificationRoot.currentNotification = notif;
|
||||||
|
notificationRoot.shouldShowOsd = true;
|
||||||
|
notificationTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: notificationServer
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: notificationTimer
|
||||||
|
|
||||||
|
interval: 5000
|
||||||
|
onTriggered: parent.shouldShowOsd = false
|
||||||
|
}
|
||||||
|
|
||||||
|
LazyLoader {
|
||||||
|
id: popupLoader
|
||||||
|
active: currentNotification && shouldShowOsd
|
||||||
|
|
||||||
|
component: NotificationPopup {
|
||||||
|
notification: currentNotification
|
||||||
|
// No signal handler here!
|
||||||
|
}
|
||||||
|
|
||||||
|
onItemChanged: {
|
||||||
|
if (item) {
|
||||||
|
item.dismissed.connect(function() {
|
||||||
|
notificationRoot.shouldShowOsd = false
|
||||||
|
currentNotification.dismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Services.Notifications
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: notificationsRoot
|
|
||||||
|
|
||||||
NotificationServer {
|
|
||||||
id: nServer
|
|
||||||
imageSupported: true
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onNotification(rapazinho) {
|
|
||||||
console.log(rapazinho.id)
|
|
||||||
console.log(nServer.trackedNotifications)
|
|
||||||
}
|
|
||||||
|
|
||||||
target: nServer
|
|
||||||
}
|
|
||||||
|
|
||||||
property var tracked: nServer.trackedNotifications
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
149
Widgets/NotificationPopup.qml
Normal file
149
Widgets/NotificationPopup.qml
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import Quickshell.Widgets
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: notificationRoot
|
||||||
|
|
||||||
|
// Since the panel's screen is unset, it will be picked by the compositor
|
||||||
|
// when the window is created. Most compositors pick the current active monitor.
|
||||||
|
|
||||||
|
property bool hasImage: notification?.image ? true : false
|
||||||
|
|
||||||
|
property string image: {
|
||||||
|
if (hasImage) {
|
||||||
|
return notification.image;
|
||||||
|
}
|
||||||
|
if (notification?.appIcon === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return Quickshell.iconPath(notification?.appIcon);
|
||||||
|
}
|
||||||
|
property var notification: null
|
||||||
|
|
||||||
|
signal dismissed()
|
||||||
|
|
||||||
|
anchors.top: true
|
||||||
|
margins.top: screen.height / 100
|
||||||
|
exclusiveZone: 0
|
||||||
|
|
||||||
|
implicitWidth: 400
|
||||||
|
implicitHeight: 100
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
// An empty click mask prevents the window from blocking mouse events.
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
topLeftRadius: 100
|
||||||
|
bottomLeftRadius: 100
|
||||||
|
topRightRadius: 20
|
||||||
|
bottomRightRadius: 20
|
||||||
|
color: "#80000000"
|
||||||
|
|
||||||
|
MouseArea{
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
notificationRoot.dismissed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
ClippingRectangle {
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
|
||||||
|
implicitWidth: 100
|
||||||
|
implicitHeight: 100
|
||||||
|
visible: image? true : false
|
||||||
|
|
||||||
|
color: "grey"
|
||||||
|
|
||||||
|
radius: 100
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitSize: 100
|
||||||
|
source: notificationRoot.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
Layout.rightMargin: 10
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
implicitHeight: 30
|
||||||
|
radius: 20
|
||||||
|
color: "#50ffffff"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: parent.width
|
||||||
|
radius: parent.radius
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
|
||||||
|
text: notificationRoot.notification.summary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
implicitHeight: 55
|
||||||
|
radius: 20
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: parent.width
|
||||||
|
radius: parent.radius
|
||||||
|
color: "white"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font.pixelSize: 24
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
fontSizeMode: Text.Fit
|
||||||
|
minimumPixelSize: 10
|
||||||
|
elide: Text.ElideRight
|
||||||
|
|
||||||
|
text: notificationRoot.notification.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Widgets/NotificationWindow.qml
Normal file
42
Widgets/NotificationWindow.qml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
PopupWindow {
|
||||||
|
|
||||||
|
anchor.item:root
|
||||||
|
anchor.rect.y: parentWindow.height
|
||||||
|
implicitWidth: 400
|
||||||
|
implicitHeight: 1000
|
||||||
|
color: "white"
|
||||||
|
visible:true
|
||||||
|
|
||||||
|
id: notificationRoot
|
||||||
|
|
||||||
|
// Since the panel's screen is unset, it will be picked by the compositor
|
||||||
|
// when the window is created. Most compositors pick the current active monitor.
|
||||||
|
|
||||||
|
|
||||||
|
// An empty click mask prevents the window from blocking mouse events.
|
||||||
|
Component {
|
||||||
|
id: contactDelegate
|
||||||
|
NotificationPopup {
|
||||||
|
id: myItem
|
||||||
|
required property string notification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
model: NotificationService.trackedNotifications.values
|
||||||
|
delegate: contactDelegate
|
||||||
|
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
|
||||||
|
focus: true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -105,7 +105,7 @@ WrapperMouseArea {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (parent.hasTopLevel) {
|
if (parent.hasTopLevel && !parent.workspaceActive) {
|
||||||
PopUpHover.start(workspacesRectangle, "workspace")
|
PopUpHover.start(workspacesRectangle, "workspace")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ ShellRoot {
|
|||||||
|
|
||||||
Variants {
|
Variants {
|
||||||
model: Quickshell.screens
|
model: Quickshell.screens
|
||||||
delegate: Bar{
|
delegate: Bar {
|
||||||
modelData: item
|
modelData: item
|
||||||
barComponentsLeft: ["NotificationsWidget.qml"]
|
barComponentsLeft: ["NotificationsWidget.qml"]
|
||||||
barComponentsCenter: ["Workspaces.qml"]
|
barComponentsCenter: ["Workspaces.qml"]
|
||||||
|
|||||||
Reference in New Issue
Block a user