improved code by removing redundancies and done some optimizations
This commit is contained in:
@@ -169,6 +169,55 @@ Rectangle {
|
||||
|
||||
MouseArea { anchors.fill: parent; propagateComposedEvents: false }
|
||||
|
||||
component MonthNavButton: Rectangle {
|
||||
id: navBtn
|
||||
required property int direction
|
||||
|
||||
width: 26; height: 26; radius: 6
|
||||
|
||||
color: btnMouse.containsMouse
|
||||
? (btnMouse.pressed ? root.t.calArrowBgPress : root.t.calArrowBgHover)
|
||||
: root.t.calArrowBg
|
||||
scale: btnMouse.pressed ? 0.88 : 1.0
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }
|
||||
|
||||
MouseArea {
|
||||
id: btnMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
function navigate() {
|
||||
if (slideAnim.running) return
|
||||
cal.slideDir = navBtn.direction
|
||||
let m = cal.displayMonth + navBtn.direction
|
||||
let y = cal.displayYear
|
||||
if (m > 11) { m = 0; y += 1 }
|
||||
if (m < 0) { m = 11; y -= 1 }
|
||||
cal.pendingMonth = m
|
||||
cal.pendingYear = y
|
||||
slideAnim.restart()
|
||||
}
|
||||
|
||||
onPressed: { navigate(); holdDelay.restart() }
|
||||
onReleased: { holdDelay.stop(); holdRepeat.stop() }
|
||||
onCanceled: { holdDelay.stop(); holdRepeat.stop() }
|
||||
|
||||
Timer { id: holdDelay; interval: 400; repeat: false; onTriggered: holdRepeat.restart() }
|
||||
Timer { id: holdRepeat; interval: 200; repeat: true; onTriggered: btnMouse.navigate() }
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: navBtn.direction < 0 ? "‹" : "›"
|
||||
color: btnMouse.containsMouse ? root.t.calArrowIconHover : root.t.calArrowIcon
|
||||
font.pixelSize: 18; font.bold: true
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: cardCol
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top }
|
||||
@@ -188,6 +237,7 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: timeManager.now.toLocaleDateString(Qt.locale(), "d MMMM yyyy")
|
||||
color: root.t.clockPopupHeader
|
||||
@@ -203,58 +253,7 @@ Rectangle {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
id: prevMonthBtn
|
||||
width: 26; height: 26; radius: 6
|
||||
color: prevBtnMouse.containsMouse
|
||||
? (prevBtnMouse.pressed ? root.t.calArrowBgPress : root.t.calArrowBgHover)
|
||||
: root.t.calArrowBg
|
||||
scale: prevBtnMouse.pressed ? 0.88 : 1.0
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }
|
||||
|
||||
MouseArea {
|
||||
id: prevBtnMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
function goToPrev() {
|
||||
if (slideAnim.running) return
|
||||
cal.slideDir = -1
|
||||
let m = cal.displayMonth - 1
|
||||
let y = cal.displayYear
|
||||
if (m < 0) { m = 11; y -= 1 }
|
||||
cal.pendingMonth = m
|
||||
cal.pendingYear = y
|
||||
slideAnim.restart()
|
||||
}
|
||||
|
||||
onPressed: { goToPrev(); prevHoldDelay.restart() }
|
||||
onReleased: { prevHoldDelay.stop(); prevHoldRepeat.stop() }
|
||||
onCanceled: { prevHoldDelay.stop(); prevHoldRepeat.stop() }
|
||||
|
||||
Timer {
|
||||
id: prevHoldDelay
|
||||
interval: 400; repeat: false
|
||||
onTriggered: prevHoldRepeat.restart()
|
||||
}
|
||||
Timer {
|
||||
id: prevHoldRepeat
|
||||
interval: 200; repeat: true
|
||||
onTriggered: prevBtnMouse.goToPrev()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "‹"
|
||||
color: prevBtnMouse.containsMouse ? root.t.calArrowIconHover : root.t.calArrowIcon
|
||||
font.pixelSize: 18; font.bold: true
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
}
|
||||
}
|
||||
MonthNavButton { direction: -1 }
|
||||
|
||||
Text {
|
||||
text: new Date(cal.displayYear, cal.displayMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy")
|
||||
@@ -263,58 +262,7 @@ Rectangle {
|
||||
Layout.fillWidth: true; horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: nextMonthBtn
|
||||
width: 26; height: 26; radius: 6
|
||||
color: nextBtnMouse.containsMouse
|
||||
? (nextBtnMouse.pressed ? root.t.calArrowBgPress : root.t.calArrowBgHover)
|
||||
: root.t.calArrowBg
|
||||
scale: nextBtnMouse.pressed ? 0.88 : 1.0
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Behavior on scale { NumberAnimation { duration: 100; easing.type: Easing.OutCubic } }
|
||||
|
||||
MouseArea {
|
||||
id: nextBtnMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
function goToNext() {
|
||||
if (slideAnim.running) return
|
||||
cal.slideDir = 1
|
||||
let m = cal.displayMonth + 1
|
||||
let y = cal.displayYear
|
||||
if (m > 11) { m = 0; y += 1 }
|
||||
cal.pendingMonth = m
|
||||
cal.pendingYear = y
|
||||
slideAnim.restart()
|
||||
}
|
||||
|
||||
onPressed: { goToNext(); nextHoldDelay.restart() }
|
||||
onReleased: { nextHoldDelay.stop(); nextHoldRepeat.stop() }
|
||||
onCanceled: { nextHoldDelay.stop(); nextHoldRepeat.stop() }
|
||||
|
||||
Timer {
|
||||
id: nextHoldDelay
|
||||
interval: 400; repeat: false
|
||||
onTriggered: nextHoldRepeat.restart()
|
||||
}
|
||||
Timer {
|
||||
id: nextHoldRepeat
|
||||
interval: 200; repeat: true
|
||||
onTriggered: nextBtnMouse.goToNext()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "›"
|
||||
color: nextBtnMouse.containsMouse ? root.t.calArrowIconHover : root.t.calArrowIcon
|
||||
font.pixelSize: 18; font.bold: true
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
}
|
||||
}
|
||||
MonthNavButton { direction: 1 }
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -363,57 +311,57 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: gridCol
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
ColumnLayout {
|
||||
id: gridCol
|
||||
width: parent.width
|
||||
spacing: 2
|
||||
|
||||
Row {
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: ["Mo","Tu","We","Th","Fr","Sa","Su"]
|
||||
Text {
|
||||
width: (card.width - 32) / 7
|
||||
text: modelData; color: root.t.clockPopupDim
|
||||
font.pixelSize: 11; horizontalAlignment: Text.AlignHCenter
|
||||
Row {
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: ["Mo","Tu","We","Th","Fr","Sa","Su"]
|
||||
Text {
|
||||
width: (card.width - 32) / 7
|
||||
text: modelData; color: root.t.clockPopupDim
|
||||
font.pixelSize: 11; horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: 6
|
||||
Row {
|
||||
id: weekRow
|
||||
property int weekIndex: index
|
||||
Repeater {
|
||||
model: 7
|
||||
delegate: Item {
|
||||
width: (card.width - 32) / 7; height: 26
|
||||
readonly property int dayNum: (weekRow.weekIndex * 7 + index) - ((new Date(cal.displayYear, cal.displayMonth, 1).getDay() + 6) % 7) + 1
|
||||
readonly property bool inMonth: dayNum >= 1 && dayNum <= new Date(cal.displayYear, cal.displayMonth + 1, 0).getDate()
|
||||
readonly property bool isToday: inMonth && dayNum === timeManager.now.getDate() && cal.displayYear === timeManager.now.getFullYear() && cal.displayMonth === timeManager.now.getMonth()
|
||||
Repeater {
|
||||
model: 6
|
||||
Row {
|
||||
id: weekRow
|
||||
property int weekIndex: index
|
||||
Repeater {
|
||||
model: 7
|
||||
delegate: Item {
|
||||
width: (card.width - 32) / 7; height: 26
|
||||
readonly property int dayNum: (weekRow.weekIndex * 7 + index) - ((new Date(cal.displayYear, cal.displayMonth, 1).getDay() + 6) % 7) + 1
|
||||
readonly property bool inMonth: dayNum >= 1 && dayNum <= new Date(cal.displayYear, cal.displayMonth + 1, 0).getDate()
|
||||
readonly property bool isToday: inMonth && dayNum === timeManager.now.getDate() && cal.displayYear === timeManager.now.getFullYear() && cal.displayMonth === timeManager.now.getMonth()
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent; width: 22; height: 22; radius: 11
|
||||
color: isToday ? root.t.accent : "transparent"
|
||||
visible: inMonth
|
||||
}
|
||||
Rectangle {
|
||||
anchors.centerIn: parent; width: 22; height: 22; radius: 11
|
||||
color: isToday ? root.t.accent : "transparent"
|
||||
visible: inMonth
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: inMonth ? dayNum : ""
|
||||
color: isToday ? root.t.todayText : (index >= 5 ? root.t.clockPopupDim : root.t.clockPopupText)
|
||||
font.pixelSize: 12; font.bold: isToday
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: inMonth ? dayNum : ""
|
||||
color: isToday ? root.t.todayText : (index >= 5 ? root.t.clockPopupDim : root.t.clockPopupText)
|
||||
font.pixelSize: 12; font.bold: isToday
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { Layout.fillWidth: true; height: 1; color: root.t.calSeparator }
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.Notifications
|
||||
|
||||
import "../../config" as Cfg
|
||||
import "../../notifications" as Notif
|
||||
import "../../config" as Cfg
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -37,6 +36,287 @@ Rectangle {
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
Behavior on width { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } }
|
||||
|
||||
component NotificationCard: Item {
|
||||
id: toastRoot
|
||||
|
||||
property string toastId: ""
|
||||
property string appName: ""
|
||||
property string summary: ""
|
||||
property string body: ""
|
||||
property int urgency: NotificationUrgency.Normal
|
||||
property string timeStr: ""
|
||||
property int timeout: 5000
|
||||
|
||||
property var t
|
||||
property var urgencyColor: function(u) { return "#ffffff" }
|
||||
property var urgencyIcon: function(u) { return "" }
|
||||
|
||||
signal dismissed(string toastId)
|
||||
|
||||
width: parent ? parent.width : 350
|
||||
height: toastCard.height
|
||||
|
||||
property real progressVal: 1.0
|
||||
property bool slideFromRight: false
|
||||
|
||||
property real slideX: slideFromRight ? (width + 20) : -(width + 20)
|
||||
|
||||
transform: Translate { id: slideTranslate; x: toastRoot.slideX }
|
||||
|
||||
Component.onCompleted: entranceSlide.start()
|
||||
|
||||
NumberAnimation {
|
||||
id: entranceSlide
|
||||
target: slideTranslate; property: "x"
|
||||
from: toastRoot.slideX; to: 0
|
||||
duration: 300; easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
function triggerDismiss() {
|
||||
if (exitSlide.running) return
|
||||
autoTimer.stop()
|
||||
exitSlide.start()
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: exitSlide
|
||||
target: slideTranslate; property: "x"
|
||||
to: toastRoot.slideX
|
||||
duration: 220; easing.type: Easing.InCubic
|
||||
onStopped: toastRoot.dismissed(toastRoot.toastId)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoTimer
|
||||
interval: toastRoot.timeout
|
||||
running: toastRoot.timeout > 0
|
||||
repeat: false
|
||||
onTriggered: toastRoot.triggerDismiss()
|
||||
}
|
||||
|
||||
NumberAnimation on progressVal {
|
||||
from: 1.0; to: 0.0
|
||||
duration: toastRoot.timeout
|
||||
running: toastRoot.urgency === NotificationUrgency.Low && toastRoot.timeout > 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: toastCard
|
||||
width: parent.width
|
||||
height: toastContent.implicitHeight + (toastRoot.urgency === NotificationUrgency.Low ? 30 : 24)
|
||||
radius: 16
|
||||
|
||||
color: cardHover.hovered ? toastRoot.t.notifToastBgHover : toastRoot.t.notifToastBg
|
||||
border.color: toastRoot.urgency === NotificationUrgency.Critical
|
||||
? toastRoot.t.notifUrgencyCritical
|
||||
: (cardHover.hovered ? toastRoot.t.notifBorderActive : toastRoot.t.notifToastBorder)
|
||||
border.width: Cfg.Config.popupBorderWidth
|
||||
layer.enabled: true
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
|
||||
HoverHandler { id: cardHover }
|
||||
TapHandler { onTapped: toastRoot.triggerDismiss() }
|
||||
|
||||
Rectangle {
|
||||
width: 4
|
||||
height: Math.max(16, toastCard.height - 24)
|
||||
radius: 2
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 10
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: toastRoot.urgencyColor(toastRoot.urgency)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: toastContent
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
leftMargin: 24
|
||||
rightMargin: 12
|
||||
topMargin: 12
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: toastRoot.urgencyIcon(toastRoot.urgency)
|
||||
color: toastRoot.urgencyColor(toastRoot.urgency)
|
||||
font.pixelSize: 12
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
Text {
|
||||
text: toastRoot.appName
|
||||
color: toastRoot.t.notifToastAppName
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
font.capitalization: Font.AllUppercase
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Text {
|
||||
text: toastRoot.timeStr
|
||||
color: toastRoot.t.notifToastDim
|
||||
font.pixelSize: 10
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
width: 20; height: 20; radius: 10
|
||||
color: closeHover.hovered ? toastRoot.t.notifHistoryHover : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
color: closeHover.hovered ? toastRoot.t.notifToastText : toastRoot.t.notifToastDim
|
||||
font.pixelSize: 10
|
||||
}
|
||||
HoverHandler { id: closeHover; cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: toastRoot.triggerDismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: toastRoot.summary
|
||||
color: toastRoot.t.notifToastText
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.PlainText
|
||||
visible: toastRoot.summary !== ""
|
||||
}
|
||||
|
||||
Text {
|
||||
text: toastRoot.body
|
||||
color: toastRoot.t.notifToastDim
|
||||
font.pixelSize: 12
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 3
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.PlainText
|
||||
visible: toastRoot.body !== ""
|
||||
Layout.bottomMargin: toastRoot.urgency === NotificationUrgency.Low ? 6 : 0
|
||||
lineHeight: 1.15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: toastRoot.urgency === NotificationUrgency.Low && toastRoot.timeout > 0
|
||||
anchors {
|
||||
left: parent.left; right: parent.right; bottom: parent.bottom
|
||||
margins: 10; bottomMargin: 10
|
||||
}
|
||||
height: 4
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 2
|
||||
color: toastRoot.t.notifProgressBg
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width * toastRoot.progressVal
|
||||
height: parent.height
|
||||
radius: 2
|
||||
color: toastRoot.t.notifProgressFill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component HistoryCard: Rectangle {
|
||||
id: histRoot
|
||||
|
||||
property int itemIndex: 0
|
||||
property string appName: ""
|
||||
property string summary: ""
|
||||
property string body: ""
|
||||
property int urgency: 0
|
||||
property string timeStr: ""
|
||||
|
||||
property var t
|
||||
property var urgencyColor: function(u) { return "#ffffff" }
|
||||
property var urgencyIcon: function(u) { return "" }
|
||||
|
||||
signal dismissed(int itemIndex)
|
||||
|
||||
height: histItemContent.implicitHeight + 24
|
||||
radius: 12
|
||||
clip: true
|
||||
color: histItemHover.hovered ? histRoot.t.notifHistoryHover : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||
|
||||
HoverHandler { id: histItemHover }
|
||||
|
||||
SequentialAnimation {
|
||||
id: dismissAnim
|
||||
ParallelAnimation {
|
||||
NumberAnimation { target: histRoot; property: "opacity"; to: 0; duration: 150; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: histRoot; property: "scale"; to: 0.95; duration: 150; easing.type: Easing.OutCubic }
|
||||
}
|
||||
NumberAnimation { target: histRoot; property: "height"; to: 0; duration: 150; easing.type: Easing.InOutQuad }
|
||||
ScriptAction { script: histRoot.dismissed(histRoot.itemIndex) }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 3
|
||||
height: Math.max(12, histRoot.height - 24)
|
||||
radius: 1.5
|
||||
anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter }
|
||||
color: histRoot.urgencyColor(histRoot.urgency)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors { right: parent.right; top: parent.top; margins: 8 }
|
||||
width: 22; height: 22; radius: 11
|
||||
color: histCloseHover.hovered ? histRoot.t.notifHistoryHover : "transparent"
|
||||
opacity: histItemHover.hovered ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
|
||||
Text { anchors.centerIn: parent; text: "✕"; color: histRoot.t.notifToastText; font.pixelSize: 10 }
|
||||
HoverHandler { id: histCloseHover; cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: dismissAnim.start() }
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: histItemContent
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top; leftMargin: 24; rightMargin: 36; topMargin: 12 }
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true; spacing: 5
|
||||
Text { text: histRoot.urgencyIcon(histRoot.urgency); color: histRoot.urgencyColor(histRoot.urgency); font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
|
||||
Text {
|
||||
text: histRoot.appName; color: histRoot.t.notifToastAppName; font.pixelSize: 11; font.bold: true
|
||||
font.capitalization: Font.AllUppercase; Layout.fillWidth: true; elide: Text.ElideRight; verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Text { text: histRoot.timeStr; color: histRoot.t.notifToastDim; font.pixelSize: 11; verticalAlignment: Text.AlignVCenter }
|
||||
}
|
||||
|
||||
Text {
|
||||
text: histRoot.summary; color: histRoot.t.notifToastText; font.pixelSize: 13; font.bold: true
|
||||
Layout.fillWidth: true; elide: Text.ElideRight; textFormat: Text.PlainText; visible: histRoot.summary !== ""
|
||||
}
|
||||
Text {
|
||||
text: histRoot.body; color: histRoot.t.notifToastDim; font.pixelSize: 12; Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap; maximumLineCount: 2; elide: Text.ElideRight; textFormat: Text.PlainText; visible: histRoot.body !== ""; lineHeight: 1.15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler { id: hoverHandler; cursorShape: Qt.PointingHandCursor; onHoveredChanged: if (hovered) bellRingAnim.start() }
|
||||
TapHandler {
|
||||
onTapped: {
|
||||
@@ -135,39 +415,31 @@ Rectangle {
|
||||
onNotification: function(notif) {
|
||||
notif.tracked = true
|
||||
bellRingAnim.start()
|
||||
var timeStr = new Date().toLocaleTimeString(Qt.locale(), "HH:mm")
|
||||
var toastId = notif.id + "_" + Date.now()
|
||||
|
||||
const timeStr = new Date().toLocaleTimeString(Qt.locale(), "HH:mm")
|
||||
const toastId = notif.id + "_" + Date.now()
|
||||
const appName = notif.appName || "Unknown"
|
||||
const summary = notif.summary || ""
|
||||
const body = notif.body || ""
|
||||
const urgency = notif.urgency
|
||||
|
||||
notifHistory.insert(0, {
|
||||
nid: notif.id,
|
||||
appName: notif.appName || "Unknown",
|
||||
summary: notif.summary || "",
|
||||
body: notif.body || "",
|
||||
urgency: notif.urgency,
|
||||
timeStr: timeStr
|
||||
nid: notif.id,
|
||||
appName, summary, body, urgency, timeStr
|
||||
})
|
||||
while (notifHistory.count > Cfg.Config.notificationHistorySize)
|
||||
notifHistory.remove(notifHistory.count - 1)
|
||||
|
||||
var cfgTimeout = notif.urgency === NotificationUrgency.Critical
|
||||
var cfgTimeout = urgency === NotificationUrgency.Critical
|
||||
? Cfg.Config.notifTimeoutCritical
|
||||
: (notif.urgency === NotificationUrgency.Low
|
||||
: (urgency === NotificationUrgency.Low
|
||||
? Cfg.Config.notifTimeoutLow
|
||||
: Cfg.Config.notifTimeoutNormal)
|
||||
|
||||
var finalTimeout = (notif.expireTimeout > 0) ? notif.expireTimeout : cfgTimeout
|
||||
|
||||
var toastEntry = {
|
||||
toastId: toastId,
|
||||
nid: notif.id,
|
||||
appName: notif.appName || "Unknown",
|
||||
summary: notif.summary || "",
|
||||
body: notif.body || "",
|
||||
urgency: notif.urgency,
|
||||
timeStr: timeStr,
|
||||
timeout: finalTimeout
|
||||
}
|
||||
if (!root.doNotDisturb) {
|
||||
var toastEntry = { toastId, nid: notif.id, appName, summary, body, urgency, timeStr, timeout: finalTimeout }
|
||||
if (toastLayer.atBottom) activeToasts.append(toastEntry)
|
||||
else activeToasts.insert(0, toastEntry)
|
||||
if (!root.historyOpen) root.unreadCount++
|
||||
@@ -197,7 +469,6 @@ Rectangle {
|
||||
|
||||
readonly property bool shareEdge: (root.isTop && !atBottom) || (!root.isTop && atBottom)
|
||||
readonly property int edgeGap: shareEdge ? root.barMargin + root.barHeight + 10 : root.barMargin + 10
|
||||
// Extra clearance so cards don't overlap the frame border stroke
|
||||
readonly property int toastPad: root.barMargin + Cfg.Config.frameBorderWidth + Cfg.Config.notifToastEdgePad
|
||||
|
||||
Column {
|
||||
@@ -209,7 +480,7 @@ Rectangle {
|
||||
|
||||
Repeater {
|
||||
model: activeToasts
|
||||
Notif.NotificationCard {
|
||||
NotificationCard {
|
||||
width: toastCol.width
|
||||
toastId: model.toastId
|
||||
appName: model.appName
|
||||
@@ -286,11 +557,11 @@ Rectangle {
|
||||
|
||||
transform: Translate {
|
||||
y: historyPopup.popupOpen ? 0 : (root.isTop ? -historyCard.height : historyCard.height)
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Cfg.Config.popupAnimDuration;
|
||||
easing.type: Easing.OutExpo
|
||||
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Cfg.Config.popupAnimDuration;
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
enabled: historyPopup.visible
|
||||
}
|
||||
@@ -319,7 +590,6 @@ Rectangle {
|
||||
Layout.fillWidth: true; Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
// ── Do Not Disturb toggle ─────────────────────────────
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
width: dndRow.implicitWidth + 16
|
||||
@@ -426,7 +696,7 @@ Rectangle {
|
||||
|
||||
Repeater {
|
||||
model: notifHistory
|
||||
Notif.HistoryCard {
|
||||
HistoryCard {
|
||||
width: histCol.width - 10; x: 2; itemIndex: index
|
||||
appName: model.appName; summary: model.summary; body: model.body; urgency: model.urgency; timeStr: model.timeStr
|
||||
t: root.t; urgencyColor: root.urgencyColor; urgencyIcon: root.urgencyIcon
|
||||
@@ -439,4 +709,3 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ Rectangle {
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.reset()
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
var cx = width / 2
|
||||
var cy = height / 2
|
||||
@@ -134,19 +133,14 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler { id: hov }
|
||||
|
||||
HoverHandler {
|
||||
id: hov
|
||||
onHoveredChanged: if (!hovered) ring.showAlt = false
|
||||
}
|
||||
|
||||
property bool showAlt: false
|
||||
onShowAltChanged: arc.requestPaint()
|
||||
|
||||
Connections {
|
||||
target: hov
|
||||
function onHoveredChanged() {
|
||||
if (!hov.hovered) ring.showAlt = false
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
enabled: !Cfg.Config.statsHideTextUntilHover || hov.hovered
|
||||
onTapped: ring.showAlt = !ring.showAlt
|
||||
@@ -155,21 +149,12 @@ Rectangle {
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
|
||||
|
||||
text: {
|
||||
if (Cfg.Config.statsHideTextUntilHover) {
|
||||
if (!hov.hovered) return ""
|
||||
var showPct = ring.showAlt
|
||||
? !Cfg.Config.statsShowPercentByDefault
|
||||
: Cfg.Config.statsShowPercentByDefault
|
||||
return showPct ? Math.round(ring.value) + "%" : ring.label
|
||||
} else {
|
||||
var baseShowPct = Cfg.Config.statsShowPercentByDefault
|
||||
? !hov.hovered
|
||||
: hov.hovered
|
||||
var finalShowPct = ring.showAlt ? !baseShowPct : baseShowPct
|
||||
return finalShowPct ? Math.round(ring.value) + "%" : ring.label
|
||||
}
|
||||
const hide = Cfg.Config.statsHideTextUntilHover
|
||||
const byDefault = Cfg.Config.statsShowPercentByDefault
|
||||
if (hide && !hov.hovered) return ""
|
||||
const showPct = ring.showAlt ? !byDefault : byDefault
|
||||
return showPct ? Math.round(ring.value) + "%" : ring.label
|
||||
}
|
||||
|
||||
color: ring.labelColor
|
||||
|
||||
@@ -356,7 +356,7 @@ Rectangle {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.preferredWidth: 44
|
||||
}
|
||||
// ── Mute toggle button ────────────────────────────────────────
|
||||
|
||||
Rectangle {
|
||||
width: 22; height: 22; radius: 6
|
||||
color: muteHover.hovered ? (vs.muted ? "#33ffffff" : "#22ffffff") : (vs.muted ? root.t.volMuteBg : "transparent")
|
||||
@@ -387,7 +387,7 @@ Rectangle {
|
||||
color: vs.muted ? vs.dimColor : vs.accentColor
|
||||
}
|
||||
}
|
||||
// ── Volume slider handle ──────────────────────────────────────
|
||||
|
||||
Rectangle {
|
||||
readonly property real ratio: vs.volume / Math.max(1, vs.maxVolume)
|
||||
x: Math.min(sliderItem.width - width, Math.max(0, sliderItem.width * ratio - width / 2))
|
||||
@@ -427,7 +427,7 @@ Rectangle {
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
// ── Device picker chevron ─────────────────────────────────────
|
||||
|
||||
Rectangle {
|
||||
width: 20; height: 20; radius: 5
|
||||
color: chevHover.hovered ? "#22ffffff" : "transparent"
|
||||
@@ -472,7 +472,6 @@ Rectangle {
|
||||
readonly property bool isActive: vs.currentNode !== null && modelData.id === vs.currentNode.id
|
||||
Layout.fillWidth: true; height: 28; radius: 7
|
||||
color: isActive ? Qt.alpha(vs.accentColor, 0.18) : (rowHover.hovered ? root.t.volPickerHover : "transparent")
|
||||
// ── Device row border ─────────────────────────────
|
||||
border.color: isActive ? Qt.alpha(vs.accentColor, 0.45) : "transparent"
|
||||
border.width: Cfg.Config.popupBorderWidth
|
||||
RowLayout {
|
||||
@@ -523,7 +522,7 @@ Rectangle {
|
||||
font.pixelSize: 11
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
// ── Decrement button ──────────────────────────────────────────────
|
||||
|
||||
Rectangle {
|
||||
width: 22; height: 22; radius: 5
|
||||
color: decHover.hovered ? "#22ffffff" : "transparent"
|
||||
@@ -544,7 +543,7 @@ Rectangle {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.preferredWidth: 44
|
||||
}
|
||||
// ── Increment button ──────────────────────────────────────────────
|
||||
|
||||
Rectangle {
|
||||
width: 22; height: 22; radius: 5
|
||||
color: incHover.hovered ? "#22ffffff" : "transparent"
|
||||
|
||||
@@ -34,7 +34,7 @@ Rectangle {
|
||||
|
||||
color: modelData.active
|
||||
? root.t.wsActiveBg
|
||||
: (wsHover.containsMouse ? root.t.wsHoverBg : root.t.wsInactiveBg)
|
||||
: (wsHover.hovered ? root.t.wsHoverBg : root.t.wsInactiveBg)
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 200 } }
|
||||
Behavior on Layout.preferredWidth { NumberAnimation { duration: 200 } }
|
||||
@@ -46,16 +46,11 @@ Rectangle {
|
||||
font.bold: true
|
||||
color: modelData.active
|
||||
? root.t.wsActiveText
|
||||
: (wsHover.containsMouse ? root.t.wsHoverText : root.t.wsInactiveText)
|
||||
: (wsHover.hovered ? root.t.wsHoverText : root.t.wsInactiveText)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wsHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: modelData.activate()
|
||||
}
|
||||
HoverHandler { id: wsHover; cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: modelData.activate() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ QtObject {
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// ── Position & Dimensions ─────────────────────────────────────────────────
|
||||
property string barPosition: "top"
|
||||
property string barPosition: "bottom"
|
||||
property int barHeight: 35
|
||||
property int margin: 15
|
||||
property int radius: 26
|
||||
|
||||
@@ -129,7 +129,7 @@ PanelWindow {
|
||||
launchProc.running = true
|
||||
|
||||
console.log("Launching: " + cleanExec)
|
||||
if (typeof close === "function") close()
|
||||
close()
|
||||
}
|
||||
|
||||
onModeChanged: if (_isOpen) loadItems()
|
||||
@@ -274,9 +274,7 @@ PanelWindow {
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
if (root.filteredItems.length > 0) {
|
||||
let idx = resultsList.currentIndex;
|
||||
if (idx === -1) idx = 0;
|
||||
root.launch(root.filteredItems[idx]);
|
||||
root.launch(root.filteredItems[Math.max(0, resultsList.currentIndex)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CACHE_DIR="/tmp/qs_launcher"
|
||||
|
||||
DRUN_CACHE="$CACHE_DIR/drun.txt"
|
||||
RUN_CACHE="$CACHE_DIR/run.txt"
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
|
||||
get_desktop_files() {
|
||||
IFS=':' read -ra DIRS <<< \
|
||||
"${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
|
||||
|
||||
for dir in "${DIRS[@]}"; do
|
||||
APP_DIR="$dir/applications"
|
||||
|
||||
[ -d "$APP_DIR" ] || continue
|
||||
|
||||
find "$APP_DIR" \
|
||||
-maxdepth 1 \
|
||||
-name "*.desktop" \
|
||||
2>/dev/null
|
||||
done | while read -r f; do
|
||||
|
||||
name=$(
|
||||
grep -m1 '^Name=' "$f" \
|
||||
| cut -d= -f2-
|
||||
)
|
||||
|
||||
exec=$(
|
||||
grep -m1 '^Exec=' "$f" \
|
||||
| cut -d= -f2- \
|
||||
| sed 's/%[fFuUik]//g'
|
||||
)
|
||||
|
||||
type=$(
|
||||
grep -m1 '^Type=' "$f" \
|
||||
| cut -d= -f2-
|
||||
)
|
||||
|
||||
nodisplay=$(
|
||||
grep -m1 '^NoDisplay=' "$f" \
|
||||
| cut -d= -f2- \
|
||||
| tr '[:upper:]' '[:lower:]'
|
||||
)
|
||||
|
||||
icon=$(
|
||||
grep -m1 '^Icon=' "$f" \
|
||||
| cut -d= -f2-
|
||||
)
|
||||
|
||||
[[ "$type" == "Application" ]] || continue
|
||||
[[ "$nodisplay" != "true" ]] || continue
|
||||
[[ -n "$name" ]] || continue
|
||||
|
||||
printf "%s\t%s\t%s\t%s\n" \
|
||||
"$name" \
|
||||
"$exec" \
|
||||
"$(basename "$f" .desktop)" \
|
||||
"$icon"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
get_binaries() {
|
||||
compgen -c \
|
||||
| sort -u \
|
||||
| head -600 \
|
||||
| awk '{
|
||||
print $1 "\t" $1 "\t" $1
|
||||
}'
|
||||
}
|
||||
|
||||
|
||||
build_drun() {
|
||||
get_desktop_files \
|
||||
| sort -f -t$'\t' -k1,1 \
|
||||
| awk -F'\t' '!seen[$1]++'
|
||||
}
|
||||
|
||||
build_run() {
|
||||
{
|
||||
get_desktop_files
|
||||
get_binaries
|
||||
} \
|
||||
| sort -f -t$'\t' -k1,1 \
|
||||
| awk -F'\t' '!seen[$1]++'
|
||||
}
|
||||
|
||||
|
||||
update_cache() {
|
||||
build_drun > "$DRUN_CACHE.tmp"
|
||||
build_run > "$RUN_CACHE.tmp"
|
||||
|
||||
mv "$DRUN_CACHE.tmp" "$DRUN_CACHE"
|
||||
mv "$RUN_CACHE.tmp" "$RUN_CACHE"
|
||||
}
|
||||
|
||||
|
||||
if [ "$1" == "--update-only" ]; then
|
||||
update_cache
|
||||
exit 0
|
||||
fi
|
||||
@@ -73,47 +73,27 @@ get_binaries() {
|
||||
}
|
||||
|
||||
|
||||
build_drun() {
|
||||
get_desktop_files \
|
||||
| sort -f -t$'\t' -k1,1 \
|
||||
sort_and_dedup() {
|
||||
sort -f -t$'\t' -k1,1 \
|
||||
| awk -F'\t' '!seen[$1]++'
|
||||
}
|
||||
|
||||
|
||||
build_drun() {
|
||||
get_desktop_files | sort_and_dedup
|
||||
}
|
||||
|
||||
build_run() {
|
||||
{
|
||||
get_desktop_files
|
||||
get_binaries
|
||||
} \
|
||||
| sort -f -t$'\t' -k1,1 \
|
||||
| awk -F'\t' '!seen[$1]++'
|
||||
{ get_desktop_files; get_binaries; } | sort_and_dedup
|
||||
}
|
||||
|
||||
|
||||
extract_proxy() {
|
||||
local conf="${PROXYCHAINS_CONF_FILE:-/etc/proxychains4.conf}"
|
||||
[ -f "$conf" ] || conf="/etc/proxychains.conf"
|
||||
[ -f "$conf" ] || return
|
||||
|
||||
local line
|
||||
line=$(awk '/^\[ProxyList\]/{found=1; next} found && NF && !/^#/{print; exit}' "$conf")
|
||||
[ -n "$line" ] || return
|
||||
|
||||
local ptype host port
|
||||
ptype=$(echo "$line" | awk '{print $1}')
|
||||
host=$(echo "$line" | awk '{print $2}')
|
||||
port=$(echo "$line" | awk '{print $3}')
|
||||
|
||||
echo "${ptype}://${host}:${port}"
|
||||
}
|
||||
|
||||
update_cache() {
|
||||
build_drun > "$DRUN_CACHE.tmp"
|
||||
build_run > "$RUN_CACHE.tmp"
|
||||
|
||||
mv "$DRUN_CACHE.tmp" "$DRUN_CACHE"
|
||||
mv "$RUN_CACHE.tmp" "$RUN_CACHE"
|
||||
|
||||
extract_proxy > "$CACHE_DIR/proxy.conf"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property int itemIndex: 0
|
||||
property string appName: ""
|
||||
property string summary: ""
|
||||
property string body: ""
|
||||
property int urgency: 0
|
||||
property string timeStr: ""
|
||||
|
||||
property var t
|
||||
property var urgencyColor: function(u) { return "#ffffff" }
|
||||
property var urgencyIcon: function(u) { return "" }
|
||||
|
||||
signal dismissed(int itemIndex)
|
||||
|
||||
height: histItemContent.implicitHeight + 24
|
||||
radius: 12
|
||||
clip: true
|
||||
color: histItemHover.hovered ? root.t.notifHistoryHover : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||
|
||||
HoverHandler { id: histItemHover }
|
||||
|
||||
SequentialAnimation {
|
||||
id: dismissAnim
|
||||
ParallelAnimation {
|
||||
NumberAnimation { target: root; property: "opacity"; to: 0; duration: 150; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: root; property: "scale"; to: 0.95; duration: 150; easing.type: Easing.OutCubic }
|
||||
}
|
||||
NumberAnimation { target: root; property: "height"; to: 0; duration: 150; easing.type: Easing.InOutQuad }
|
||||
ScriptAction { script: root.dismissed(root.itemIndex) }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 3
|
||||
height: Math.max(12, root.height - 24)
|
||||
radius: 1.5
|
||||
anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter }
|
||||
color: root.urgencyColor(root.urgency)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors { right: parent.right; top: parent.top; margins: 8 }
|
||||
width: 22; height: 22; radius: 11
|
||||
color: histCloseHover.hovered ? root.t.notifHistoryHover : "transparent"
|
||||
opacity: histItemHover.hovered ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
|
||||
Text { anchors.centerIn: parent; text: "✕"; color: root.t.notifToastText; font.pixelSize: 10 }
|
||||
HoverHandler { id: histCloseHover; cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: dismissAnim.start() }
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: histItemContent
|
||||
anchors { left: parent.left; right: parent.right; top: parent.top; leftMargin: 24; rightMargin: 36; topMargin: 12 }
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true; spacing: 5
|
||||
Text { text: root.urgencyIcon(root.urgency); color: root.urgencyColor(root.urgency); font.pixelSize: 11; Layout.alignment: Qt.AlignVCenter }
|
||||
Text {
|
||||
text: root.appName; color: root.t.notifToastAppName; font.pixelSize: 11; font.bold: true;
|
||||
font.capitalization: Font.AllUppercase; Layout.fillWidth: true; elide: Text.ElideRight; verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Text { text: root.timeStr; color: root.t.notifToastDim; font.pixelSize: 11; verticalAlignment: Text.AlignVCenter }
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.summary; color: root.t.notifToastText; font.pixelSize: 13; font.bold: true;
|
||||
Layout.fillWidth: true; elide: Text.ElideRight; textFormat: Text.PlainText; visible: root.summary !== ""
|
||||
}
|
||||
Text {
|
||||
text: root.body; color: root.t.notifToastDim; font.pixelSize: 12; Layout.fillWidth: true;
|
||||
wrapMode: Text.WordWrap; maximumLineCount: 2; elide: Text.ElideRight; textFormat: Text.PlainText; visible: root.body !== ""; lineHeight: 1.15
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Services.Notifications
|
||||
|
||||
import "../../config" as Cfg
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string toastId: ""
|
||||
property string appName: ""
|
||||
property string summary: ""
|
||||
property string body: ""
|
||||
property int urgency: NotificationUrgency.Normal
|
||||
property string timeStr: ""
|
||||
property int timeout: 5000
|
||||
|
||||
property var t
|
||||
property var urgencyColor: function(u) { return "#ffffff" }
|
||||
property var urgencyIcon: function(u) { return "" }
|
||||
|
||||
signal dismissed(string toastId)
|
||||
|
||||
width: parent ? parent.width : 350
|
||||
height: toastCard.height
|
||||
|
||||
property real progressVal: 1.0
|
||||
property bool slideFromRight: false
|
||||
|
||||
property real slideX: slideFromRight ? (width + 20) : -(width + 20)
|
||||
|
||||
transform: Translate { id: slideTranslate; x: root.slideX }
|
||||
|
||||
Component.onCompleted: entranceSlide.start()
|
||||
|
||||
NumberAnimation {
|
||||
id: entranceSlide
|
||||
target: slideTranslate; property: "x"
|
||||
from: root.slideX; to: 0
|
||||
duration: 300; easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
function triggerDismiss() {
|
||||
if (exitSlide.running) return
|
||||
autoTimer.stop()
|
||||
exitSlide.start()
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: exitSlide
|
||||
target: slideTranslate; property: "x"
|
||||
to: root.slideX
|
||||
duration: 220; easing.type: Easing.InCubic
|
||||
onStopped: root.dismissed(root.toastId)
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autoTimer
|
||||
interval: root.timeout
|
||||
running: root.timeout > 0
|
||||
repeat: false
|
||||
onTriggered: root.triggerDismiss()
|
||||
}
|
||||
|
||||
NumberAnimation on progressVal {
|
||||
from: 1.0; to: 0.0
|
||||
duration: root.timeout
|
||||
running: root.urgency === NotificationUrgency.Low && root.timeout > 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: toastCard
|
||||
width: parent.width
|
||||
height: toastContent.implicitHeight + (root.urgency === NotificationUrgency.Low ? 30 : 24)
|
||||
radius: 16
|
||||
|
||||
color: cardHover.hovered ? root.t.notifToastBgHover : root.t.notifToastBg
|
||||
border.color: root.urgency === NotificationUrgency.Critical
|
||||
? root.t.notifUrgencyCritical
|
||||
: (cardHover.hovered ? root.t.notifBorderActive : root.t.notifToastBorder)
|
||||
border.width: Cfg.Config.popupBorderWidth
|
||||
layer.enabled: true
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
|
||||
HoverHandler { id: cardHover }
|
||||
TapHandler { onTapped: root.triggerDismiss() }
|
||||
|
||||
Rectangle {
|
||||
width: 4
|
||||
height: Math.max(16, toastCard.height - 24)
|
||||
radius: 2
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 10
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
color: root.urgencyColor(root.urgency)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: toastContent
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
leftMargin: 24
|
||||
rightMargin: 12
|
||||
topMargin: 12
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: root.urgencyIcon(root.urgency)
|
||||
color: root.urgencyColor(root.urgency)
|
||||
font.pixelSize: 12
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
Text {
|
||||
text: root.appName
|
||||
color: root.t.notifToastAppName
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
font.capitalization: Font.AllUppercase
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
Text {
|
||||
text: root.timeStr
|
||||
color: root.t.notifToastDim
|
||||
font.pixelSize: 10
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
width: 20; height: 20; radius: 10
|
||||
color: closeHover.hovered ? root.t.notifHistoryHover : "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "✕"
|
||||
color: closeHover.hovered ? root.t.notifToastText : root.t.notifToastDim
|
||||
font.pixelSize: 10
|
||||
}
|
||||
HoverHandler { id: closeHover; cursorShape: Qt.PointingHandCursor }
|
||||
TapHandler { onTapped: root.triggerDismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.summary
|
||||
color: root.t.notifToastText
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.PlainText
|
||||
visible: root.summary !== ""
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.body
|
||||
color: root.t.notifToastDim
|
||||
font.pixelSize: 12
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 3
|
||||
elide: Text.ElideRight
|
||||
textFormat: Text.PlainText
|
||||
visible: root.body !== ""
|
||||
Layout.bottomMargin: root.urgency === NotificationUrgency.Low ? 6 : 0
|
||||
lineHeight: 1.15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: root.urgency === NotificationUrgency.Low && root.timeout > 0
|
||||
anchors {
|
||||
left: parent.left; right: parent.right; bottom: parent.bottom
|
||||
margins: 10; bottomMargin: 10
|
||||
}
|
||||
height: 4
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: 2
|
||||
color: root.t.notifProgressBg
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width * root.progressVal
|
||||
height: parent.height
|
||||
radius: 2
|
||||
color: root.t.notifProgressFill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,25 +19,21 @@ PanelWindow {
|
||||
property string secondsString: ""
|
||||
|
||||
function startTyping() {
|
||||
typedChars = 0;
|
||||
secondsText.opacity = 0;
|
||||
typingTimer.start();
|
||||
typedChars = 0
|
||||
secondsText.opacity = 0
|
||||
typingTimer.start()
|
||||
}
|
||||
|
||||
function updateDate() {
|
||||
let d = new Date();
|
||||
fullDateString = d.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy");
|
||||
startTyping();
|
||||
}
|
||||
|
||||
function updateSeconds() {
|
||||
let d = new Date();
|
||||
secondsString = Qt.formatTime(d, "hh:mm:ss");
|
||||
function updateDate(d) {
|
||||
let now = d || new Date()
|
||||
fullDateString = now.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy")
|
||||
startTyping()
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
updateSeconds();
|
||||
updateDate();
|
||||
let d = new Date()
|
||||
secondsString = Qt.formatTime(d, "hh:mm:ss")
|
||||
updateDate(d)
|
||||
}
|
||||
|
||||
Column {
|
||||
@@ -53,7 +49,7 @@ PanelWindow {
|
||||
font.weight: Font.ExtraLight
|
||||
font.letterSpacing: 4
|
||||
opacity: 0.55
|
||||
style: Text.DropShadow
|
||||
style: Text.Raised
|
||||
text: fullDateString.substring(0, typedChars)
|
||||
}
|
||||
|
||||
@@ -66,7 +62,7 @@ PanelWindow {
|
||||
font.weight: Font.ExtraLight
|
||||
font.letterSpacing: 6
|
||||
opacity: 0
|
||||
style: Text.DropShadow
|
||||
style: Text.Raised
|
||||
text: secondsString
|
||||
|
||||
Behavior on opacity {
|
||||
@@ -84,10 +80,10 @@ PanelWindow {
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (typedChars < fullDateString.length) {
|
||||
typedChars++;
|
||||
typedChars++
|
||||
} else {
|
||||
stop();
|
||||
secondsText.opacity = 0.55;
|
||||
stop()
|
||||
secondsText.opacity = 0.55
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,10 +93,10 @@ PanelWindow {
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
updateSeconds();
|
||||
let d = new Date();
|
||||
let d = new Date()
|
||||
secondsString = Qt.formatTime(d, "hh:mm:ss")
|
||||
if (d.getSeconds() === 0 && d.getMinutes() === 0 && d.getHours() === 0)
|
||||
updateDate();
|
||||
updateDate(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user