summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/Shortcuts.qml21
-rw-r--r--modules/drawers/Panels.qml26
-rw-r--r--modules/utilities/toasts/ToastItem.qml132
-rw-r--r--modules/utilities/toasts/Toasts.qml135
4 files changed, 306 insertions, 8 deletions
diff --git a/modules/Shortcuts.qml b/modules/Shortcuts.qml
index bf1cded..a62b827 100644
--- a/modules/Shortcuts.qml
+++ b/modules/Shortcuts.qml
@@ -1,6 +1,7 @@
import qs.components.misc
import qs.modules.controlcenter
import qs.services
+import Caelestia
import Quickshell
import Quickshell.Io
@@ -95,4 +96,24 @@ Scope {
WindowFactory.create();
}
}
+
+ IpcHandler {
+ target: "toaster"
+
+ function info(title: string, message: string, icon: string): void {
+ Toaster.toast(title, message, icon, Toast.Info);
+ }
+
+ function success(title: string, message: string, icon: string): void {
+ Toaster.toast(title, message, icon, Toast.Success);
+ }
+
+ function warn(title: string, message: string, icon: string): void {
+ Toaster.toast(title, message, icon, Toast.Warning);
+ }
+
+ function error(title: string, message: string, icon: string): void {
+ Toaster.toast(title, message, icon, Toast.Error);
+ }
+ }
}
diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml
index 7896ddb..4ce1182 100644
--- a/modules/drawers/Panels.qml
+++ b/modules/drawers/Panels.qml
@@ -6,6 +6,7 @@ import qs.modules.launcher as Launcher
import qs.modules.dashboard as Dashboard
import qs.modules.bar.popouts as BarPopouts
import qs.modules.utilities as Utilities
+import qs.modules.utilities.toasts as Toasts
import qs.modules.sidebar as Sidebar
import Quickshell
import QtQuick
@@ -17,14 +18,15 @@ Item {
required property PersistentProperties visibilities
required property Item bar
- readonly property Osd.Wrapper osd: osd
- readonly property Notifications.Wrapper notifications: notifications
- readonly property Session.Wrapper session: session
- readonly property Launcher.Wrapper launcher: launcher
- readonly property Dashboard.Wrapper dashboard: dashboard
- readonly property BarPopouts.Wrapper popouts: popouts
- readonly property Utilities.Wrapper utilities: utilities
- readonly property Sidebar.Wrapper sidebar: sidebar
+ readonly property alias osd: osd
+ readonly property alias notifications: notifications
+ readonly property alias session: session
+ readonly property alias launcher: launcher
+ readonly property alias dashboard: dashboard
+ readonly property alias popouts: popouts
+ readonly property alias utilities: utilities
+ readonly property alias toasts: toasts
+ readonly property alias sidebar: sidebar
anchors.fill: parent
anchors.margins: Config.border.thickness
@@ -112,6 +114,14 @@ Item {
anchors.right: parent.right
}
+ Toasts.Toasts {
+ id: toasts
+
+ anchors.bottom: sidebar.visible ? parent.bottom : utilities.top
+ anchors.right: sidebar.left
+ anchors.margins: Appearance.padding.normal
+ }
+
Sidebar.Wrapper {
id: sidebar
diff --git a/modules/utilities/toasts/ToastItem.qml b/modules/utilities/toasts/ToastItem.qml
new file mode 100644
index 0000000..fa3aa18
--- /dev/null
+++ b/modules/utilities/toasts/ToastItem.qml
@@ -0,0 +1,132 @@
+import qs.components
+import qs.components.effects
+import qs.services
+import qs.config
+import Caelestia
+import QtQuick
+import QtQuick.Layouts
+
+StyledRect {
+ id: root
+
+ required property Toast modelData
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2
+
+ radius: Appearance.rounding.normal
+ color: {
+ if (root.modelData.type === Toast.Success)
+ return Colours.palette.m3successContainer;
+ if (root.modelData.type === Toast.Warning)
+ return Colours.palette.m3secondary;
+ if (root.modelData.type === Toast.Error)
+ return Colours.palette.m3errorContainer;
+ return Colours.palette.m3surface;
+ }
+
+ border.width: 1
+ border.color: {
+ let colour = Colours.palette.m3outlineVariant;
+ if (root.modelData.type === Toast.Success)
+ colour = Colours.palette.m3success;
+ if (root.modelData.type === Toast.Warning)
+ colour = Colours.palette.m3secondaryContainer;
+ if (root.modelData.type === Toast.Error)
+ colour = Colours.palette.m3error;
+ return Qt.alpha(colour, 0.3);
+ }
+
+ Elevation {
+ anchors.fill: parent
+ radius: parent.radius
+ opacity: parent.opacity
+ z: -1
+ level: 3
+ }
+
+ RowLayout {
+ id: layout
+
+ anchors.fill: parent
+ anchors.margins: Appearance.padding.smaller
+ anchors.leftMargin: Appearance.padding.normal
+ anchors.rightMargin: Appearance.padding.normal
+ spacing: Appearance.spacing.normal
+
+ StyledRect {
+ radius: Appearance.rounding.normal
+ color: {
+ if (root.modelData.type === Toast.Success)
+ return Colours.palette.m3success;
+ if (root.modelData.type === Toast.Warning)
+ return Colours.palette.m3secondaryContainer;
+ if (root.modelData.type === Toast.Error)
+ return Colours.palette.m3error;
+ return Colours.palette.m3surfaceContainerHigh;
+ }
+
+ implicitWidth: implicitHeight
+ implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2
+
+ MaterialIcon {
+ id: icon
+
+ anchors.centerIn: parent
+ text: root.modelData.icon
+ color: {
+ if (root.modelData.type === Toast.Success)
+ return Colours.palette.m3onSuccess;
+ if (root.modelData.type === Toast.Warning)
+ return Colours.palette.m3onSecondaryContainer;
+ if (root.modelData.type === Toast.Error)
+ return Colours.palette.m3onError;
+ return Colours.palette.m3onSurfaceVariant;
+ }
+ font.pointSize: Appearance.font.size.large
+ }
+ }
+
+ ColumnLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ StyledText {
+ id: title
+
+ Layout.fillWidth: true
+ text: root.modelData.title
+ color: {
+ if (root.modelData.type === Toast.Success)
+ return Colours.palette.m3onSuccessContainer;
+ if (root.modelData.type === Toast.Warning)
+ return Colours.palette.m3onSecondary;
+ if (root.modelData.type === Toast.Error)
+ return Colours.palette.m3onErrorContainer;
+ return Colours.palette.m3onSurface;
+ }
+ font.pointSize: Appearance.font.size.normal
+ }
+
+ StyledText {
+ Layout.fillWidth: true
+ text: root.modelData.message
+ color: {
+ if (root.modelData.type === Toast.Success)
+ return Colours.palette.m3onSuccessContainer;
+ if (root.modelData.type === Toast.Warning)
+ return Colours.palette.m3onSecondary;
+ if (root.modelData.type === Toast.Error)
+ return Colours.palette.m3onErrorContainer;
+ return Colours.palette.m3onSurface;
+ }
+ opacity: 0.8
+ }
+ }
+ }
+
+ Behavior on border.color {
+ CAnim {}
+ }
+}
diff --git a/modules/utilities/toasts/Toasts.qml b/modules/utilities/toasts/Toasts.qml
new file mode 100644
index 0000000..c23790d
--- /dev/null
+++ b/modules/utilities/toasts/Toasts.qml
@@ -0,0 +1,135 @@
+pragma ComponentBehavior: Bound
+
+import qs.components
+import qs.config
+import Caelestia
+import Quickshell
+import QtQuick
+import QtQuick.Layouts
+
+Item {
+ id: root
+
+ readonly property int spacing: Appearance.spacing.small
+ property bool flag
+
+ implicitWidth: Config.utilities.sizes.width - Appearance.padding.normal * 2
+ implicitHeight: {
+ let h = -spacing;
+ for (let i = 0; i < repeater.count; i++) {
+ const item = repeater.itemAt(i);
+ if (!item.modelData.closed && !item.previewHidden)
+ h += item.implicitHeight + spacing;
+ }
+ return h;
+ }
+
+ Repeater {
+ id: repeater
+
+ model: ScriptModel {
+ values: {
+ const toasts = [];
+ let count = 0;
+ for (const toast of Toaster.toasts) {
+ toasts.push(toast);
+ if (!toast.closed) {
+ count++;
+ if (count > Config.utilities.maxToasts)
+ break;
+ }
+ }
+ return toasts;
+ }
+ onValuesChanged: root.flagChanged()
+ }
+
+ MouseArea {
+ id: toast
+
+ required property int index
+ required property Toast modelData
+
+ readonly property bool previewHidden: {
+ let extraHidden = 0;
+ for (let i = 0; i < index; i++)
+ if (Toaster.toasts[i].closed)
+ extraHidden++;
+ return index >= Config.utilities.maxToasts + extraHidden;
+ }
+
+ opacity: modelData.closed || previewHidden ? 0 : 1
+ scale: modelData.closed || previewHidden ? 0.7 : 1
+
+ anchors.bottomMargin: {
+ root.flag; // Force update
+ let y = 0;
+ for (let i = 0; i < index; i++) {
+ const item = repeater.itemAt(i);
+ if (item && !item.modelData.closed && !item.previewHidden)
+ y += item.implicitHeight + root.spacing;
+ }
+ return y;
+ }
+
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ implicitHeight: toastInner.implicitHeight
+
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
+ onClicked: modelData.close()
+
+ Component.onCompleted: modelData.lock(this)
+
+ Anim {
+ Component.onCompleted: running = !toast.previewHidden
+
+ target: toast
+ properties: "opacity,scale"
+ from: 0
+ to: 1
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+
+ ParallelAnimation {
+ running: toast.modelData.closed
+ onStarted: toast.anchors.bottomMargin = toast.anchors.bottomMargin
+ onFinished: toast.modelData.unlock(toast)
+
+ Anim {
+ target: toast
+ property: "opacity"
+ to: 0
+ }
+ Anim {
+ target: toast
+ property: "scale"
+ to: 0.7
+ }
+ }
+
+ ToastItem {
+ id: toastInner
+
+ modelData: toast.modelData
+ }
+
+ Behavior on opacity {
+ Anim {}
+ }
+
+ Behavior on scale {
+ Anim {}
+ }
+
+ Behavior on anchors.bottomMargin {
+ Anim {
+ duration: Appearance.anim.durations.expressiveDefaultSpatial
+ easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
+ }
+ }
+ }
+ }
+}