[libadwaita] Add AdwToast
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libadwaita] Add AdwToast
- Date: Sat, 6 Nov 2021 17:20:17 +0000 (UTC)
commit 2e6d13483f05283afd53f8ac479acf72edd6bacc
Author: Maximiliano Sandoval R <msandova gnome org>
Date: Sat Nov 6 22:06:31 2021 +0500
Add AdwToast
Co-authored-by: Alexander Mikhaylenko <alexm gnome org>
src/adw-enums.c.in | 1 +
src/adw-toast-private.h | 23 ++
src/adw-toast.c | 725 ++++++++++++++++++++++++++++++++++++++++++++++++
src/adw-toast.h | 73 +++++
src/adwaita.h | 1 +
src/meson.build | 4 +-
tests/meson.build | 1 +
tests/test-toast.c | 205 ++++++++++++++
8 files changed, 1032 insertions(+), 1 deletion(-)
---
diff --git a/src/adw-enums.c.in b/src/adw-enums.c.in
index b151b1ed..c36ff689 100644
--- a/src/adw-enums.c.in
+++ b/src/adw-enums.c.in
@@ -9,6 +9,7 @@
#include "adw-navigation-direction.h"
#include "adw-squeezer.h"
#include "adw-style-manager.h"
+#include "adw-toast.h"
#include "adw-view-switcher.h"
/*** END file-header ***/
diff --git a/src/adw-toast-private.h b/src/adw-toast-private.h
new file mode 100644
index 00000000..b9fa3907
--- /dev/null
+++ b/src/adw-toast-private.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-toast.h"
+
+G_BEGIN_DECLS
+
+gboolean adw_toast_get_added (AdwToast *self);
+void adw_toast_set_added (AdwToast *self,
+ gboolean added);
+
+G_END_DECLS
diff --git a/src/adw-toast.c b/src/adw-toast.c
new file mode 100644
index 00000000..f8f3276a
--- /dev/null
+++ b/src/adw-toast.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2021 Maximiliano Sandoval <msandova gnome org>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "adw-toast-private.h"
+
+/**
+ * AdwToastPriority:
+ * @ADW_TOAST_PRIORITY_NORMAL: the toast will be queued if another toast is
+ * already displayed.
+ * @ADW_TOAST_PRIORITY_HIGH: the toast will be displayed immediately, pushing
+ * the previous toast into the queue instead.
+ *
+ * [class@Adw.Toast] behavior when another toast is already displayed.
+ *
+ * Since: 1.0
+ */
+
+/**
+ * AdwToast:
+ *
+ * A helper object for [class@Adw.ToastOverlay].
+ *
+ * Toasts are meant to be passed into [method@Adw.ToastOverlay.add_toast] as
+ * follows:
+ *
+ * ```c
+ * adw_toast_overlay_add_toast (overlay, adw_toast_new (_("Simple Toast"));
+ * ```
+ *
+ * Toasts always have a close button and a timeout. In both cases, they emit the
+ * [signal@Adw.Toast::dismissed] signal when disappearing.
+ *
+ * [property@Adw.Toast:priority] determines how the toast behaves if another
+ * toast is already being displayed.
+ *
+ * ## Actions
+ *
+ * Toasts can have one button on them, with a label and an attached
+ * [iface@Gio.Action].
+ *
+ * ```c
+ * AdwToast *toast = adw_toast_new (_("Toast with Action"));
+ *
+ * adw_toast_set_button_label (toast, _("Example"));
+ * adw_toast_set_action_name (toast, "win.example");
+ *
+ * adw_toast_overlay_add_toast (overlay, toast);
+ * ```
+ *
+ * ## Modifying toasts
+ *
+ * Toasts can be modified after they have been shown. For this, an `AdwToast`
+ * reference must be kept around while the toast is visible.
+ *
+ * A common use case for this is using toasts as undo prompts that stack with
+ * each other, allowing to batch undo the last deleted items:
+ *
+ * ```c
+ *
+ * static void
+ * toast_undo_cb (GtkWidget *sender,
+ * const char *action,
+ * GVariant *param)
+ * {
+ * // Undo the deletion
+ * }
+ *
+ * static void
+ * dismissed_cb (MyWindow *self)
+ * {
+ * self->undo_toast = NULL;
+ *
+ * // Permanently delete the items
+ * }
+ *
+ * static void
+ * delete_item (MyWindow *self,
+ * MyItem *item)
+ * {
+ * g_autofree char *title = NULL;
+ * int n_items;
+ *
+ * // Mark the item as waiting for deletion
+ * n_items = ... // The number of waiting items
+ *
+ * if (!self->undo_toast) {
+ * title = g_strdup_printf (_("ā%sā deleted"), ...);
+ *
+ * self->undo_toast = adw_toast_new (title);
+ *
+ * adw_toast_set_priority (self->undo_toast, ADW_TOAST_PRIORITY_HIGH);
+ * adw_toast_set_button_label (self->undo_toast, _("Undo"));
+ * adw_toast_set_action_name (self->undo_toast, "toast.undo");
+ *
+ * g_signal_connect_swapped (self->undo_toast, "dismissed",
+ * G_CALLBACK (dismissed_cb), self);
+ *
+ * adw_toast_overlay_add_toast (self->toast_overlay, self->undo_toast);
+ *
+ * return;
+ * }
+ *
+ * title =
+ * g_strdup_printf (ngettext ("<span font_features='tnum=1'>%d</span> item deleted",
+ * "<span font_features='tnum=1'>%d</span> items deleted",
+ * n_items), n_items);
+ *
+ * adw_toast_set_title (self->undo_toast, title);
+ * }
+ *
+ * static void
+ * my_window_class_init (MyWindowClass *klass)
+ * {
+ * GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ *
+ * gtk_widget_class_install_action (widget_class, "toast.undo", NULL, toast_undo_cb);
+ * }
+ * ```
+ *
+ * Since: 1.0
+ */
+
+struct _AdwToast {
+ GObject parent_instance;
+
+ char *title;
+ char *button_label;
+ char *action_name;
+ GVariant *action_target;
+ AdwToastPriority priority;
+
+ gboolean added;
+};
+
+enum {
+ PROP_0,
+ PROP_TITLE,
+ PROP_BUTTON_LABEL,
+ PROP_ACTION_NAME,
+ PROP_ACTION_TARGET,
+ PROP_PRIORITY,
+ LAST_PROP,
+};
+
+static GParamSpec *props[LAST_PROP];
+
+enum {
+ SIGNAL_DISMISSED,
+ SIGNAL_LAST_SIGNAL,
+};
+
+static guint signals[SIGNAL_LAST_SIGNAL];
+
+G_DEFINE_TYPE (AdwToast, adw_toast, G_TYPE_OBJECT)
+
+static void
+dismissed_cb (AdwToast *self)
+{
+ self->added = FALSE;
+}
+
+static void
+adw_toast_finalize (GObject *object)
+{
+ AdwToast *self = ADW_TOAST (object);
+
+ g_clear_pointer (&self->title, g_free);
+ g_clear_pointer (&self->button_label, g_free);
+ g_clear_pointer (&self->action_name, g_free);
+ g_clear_pointer (&self->action_target, g_variant_unref);
+
+ G_OBJECT_CLASS (adw_toast_parent_class)->finalize (object);
+}
+
+static void
+adw_toast_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ AdwToast *self = ADW_TOAST (object);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ g_value_set_string (value, adw_toast_get_title (self));
+ break;
+ case PROP_BUTTON_LABEL:
+ g_value_set_string (value, adw_toast_get_button_label (self));
+ break;
+ case PROP_ACTION_NAME:
+ g_value_set_string (value, adw_toast_get_action_name (self));
+ break;
+ case PROP_ACTION_TARGET:
+ g_value_set_variant (value, adw_toast_get_action_target_value (self));
+ break;
+ case PROP_PRIORITY:
+ g_value_set_enum (value, adw_toast_get_priority (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+adw_toast_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ AdwToast *self = ADW_TOAST (object);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ adw_toast_set_title (self, g_value_get_string (value));
+ break;
+ case PROP_BUTTON_LABEL:
+ adw_toast_set_button_label (self, g_value_get_string (value));
+ break;
+ case PROP_ACTION_NAME:
+ adw_toast_set_action_name (self, g_value_get_string (value));
+ break;
+ case PROP_ACTION_TARGET:
+ adw_toast_set_action_target_value (self, g_value_get_variant (value));
+ break;
+ case PROP_PRIORITY:
+ adw_toast_set_priority (self, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+adw_toast_class_init (AdwToastClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = adw_toast_finalize;
+ object_class->get_property = adw_toast_get_property;
+ object_class->set_property = adw_toast_set_property;
+
+ /**
+ * AdwToast:title: (attributes org.gtk.Property.get=adw_toast_get_title
org.gtk.Property.set=adw_toast_set_title)
+ *
+ * The title of the toast.
+ *
+ * The title can be marked up with the Pango text markup language.
+ *
+ * Since: 1.0
+ */
+ props[PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title of the toast",
+ "",
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * AdwToast:button-label: (attributes org.gtk.Property.get=adw_toast_get_button_label
org.gtk.Property.set=adw_toast_set_button_label)
+ *
+ * The label to show on the button.
+ *
+ * If set to `NULL`, the button won't be shown.
+ *
+ * See [property@Adw.Toast:action-name].
+ *
+ * Since: 1.0
+ */
+ props[PROP_BUTTON_LABEL] =
+ g_param_spec_string ("button-label",
+ "Button Label",
+ "The label to show on the button",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * AdwToast:action-name: (attributes org.gtk.Property.get=adw_toast_get_action_name
org.gtk.Property.set=adw_toast_set_action_name)
+ *
+ * The name of the associated action.
+ *
+ * It will be activated when clicking the button.
+ *
+ * See [property@Adw.Toast:action-target].
+ *
+ * Since: 1.0
+ */
+ props[PROP_ACTION_NAME] =
+ g_param_spec_string ("action-name",
+ "Action Name",
+ "The name of the associated action",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * AdwToast:action-target: (attributes org.gtk.Property.get=adw_toast_get_action_target_value
org.gtk.Property.set=adw_toast_set_action_target_value)
+ *
+ * The parameter for action invocations.
+ *
+ * See [property@Adw.Toast:action-name].
+ *
+ * Since: 1.0
+ */
+ props[PROP_ACTION_TARGET] =
+ g_param_spec_variant ("action-target",
+ "Action Target Value",
+ "The parameter for action invocations",
+ G_VARIANT_TYPE_ANY,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * AdwToast:priority: (attributes org.gtk.Property.get=adw_toast_get_priority
org.gtk.Property.set=adw_toast_set_priority)
+ *
+ * The priority of the toast.
+ *
+ * Priority controls how the toast behaves when another toast is already
+ * being displayed.
+ *
+ * If the priority is `ADW_TOAST_PRIORITY_NORMAL`, the toast will be queued.
+ *
+ * If the priority is `ADW_TOAST_PRIORITY_HIGH`, the toast will be displayed
+ * immediately, pushing the previous toast into the queue instead.
+ *
+ * Since: 1.0
+ */
+ props[PROP_PRIORITY] =
+ g_param_spec_enum ("priority",
+ "Priority",
+ "The priority of the toast",
+ ADW_TYPE_TOAST_PRIORITY,
+ ADW_TOAST_PRIORITY_NORMAL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, props);
+
+ /**
+ * AdwToast::dismissed:
+ *
+ * Emitted when the toast has been dismissed.
+ *
+ * Since: 1.0
+ */
+ signals[SIGNAL_DISMISSED] =
+ g_signal_new ("dismissed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+adw_toast_init (AdwToast *self)
+{
+ self->title = g_strdup ("");
+ self->priority = ADW_TOAST_PRIORITY_NORMAL;
+
+ g_signal_connect (self, "dismissed", G_CALLBACK (dismissed_cb), self);
+}
+
+/**
+ * adw_toast_new:
+ * @title: the title to be displayed
+ *
+ * Creates a new `AdwToast`.
+ *
+ * The toast will use @title as its title.
+ *
+ * @title can be marked up with the Pango text markup language.
+ *
+ * Returns: the new created `AdwToast`
+ *
+ * Since: 1.0
+ */
+AdwToast *
+adw_toast_new (const char *title)
+{
+ g_return_val_if_fail (title != NULL, NULL);
+
+ return g_object_new (ADW_TYPE_TOAST,
+ "title", title,
+ NULL);
+}
+
+/**
+ * adw_toast_get_title: (attributes org.gtk.Method.get_property=title)
+ * @self: a `AdwToast`
+ *
+ * Gets the title that will be displayed on the toast.
+ *
+ * Returns: the title
+ *
+ * Since: 1.0
+ */
+const char *
+adw_toast_get_title (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), NULL);
+
+ return self->title;
+}
+
+/**
+ * adw_toast_set_title: (attributes org.gtk.Method.set_property=title)
+ * @self: a `AdwToast`
+ * @title: a title
+ *
+ * Sets the title that will be displayed on the toast.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_title (AdwToast *self,
+ const char *title)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+ g_return_if_fail (title != NULL);
+
+ if (!g_strcmp0 (self->title, title))
+ return;
+
+ g_clear_pointer (&self->title, g_free);
+ self->title = g_strdup (title);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TITLE]);
+}
+
+/**
+ * adw_toast_get_button_label: (attributes org.gtk.Method.get_property=button-label)
+ * @self: a `AdwToast`
+ *
+ * Gets the label to show on the button.
+ *
+ * Returns: (nullable): the button label
+ *
+ * Since: 1.0
+ */
+const char *
+adw_toast_get_button_label (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), NULL);
+
+ return self->button_label;
+}
+
+/**
+ * adw_toast_set_button_label: (attributes org.gtk.Method.set_property=button-label)
+ * @self: a `AdwToast`
+ * @button_label: (nullable): a button label
+ *
+ * Sets the label to show on the button.
+ *
+ * It set to `NULL`, the button won't be shown.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_button_label (AdwToast *self,
+ const char *button_label)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ if (!g_strcmp0 (self->button_label, button_label))
+ return;
+
+ g_clear_pointer (&self->button_label, g_free);
+ self->button_label = g_strdup (button_label);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_BUTTON_LABEL]);
+}
+
+/**
+ * adw_toast_get_action_name: (attributes org.gtk.Method.get_property=action-name)
+ * @self: a `AdwToast`
+ *
+ * Gets the name of the associated action.
+ *
+ * Returns: (nullable): the action name
+ *
+ * Since: 1.0
+ */
+const char *
+adw_toast_get_action_name (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), NULL);
+
+ return self->action_name;
+}
+
+/**
+ * adw_toast_set_action_name: (attributes org.gtk.Method.set_property=action-name)
+ * @self: a `AdwToast`
+ * @action_name: (nullable): the action name
+ *
+ * Sets the name of the associated action.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_action_name (AdwToast *self,
+ const char *action_name)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ if (!g_strcmp0 (self->action_name, action_name))
+ return;
+
+ g_clear_pointer (&self->action_name, g_free);
+ self->action_name = g_strdup (action_name);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTION_NAME]);
+}
+
+/**
+ * adw_toast_get_action_target_value: (attributes org.gtk.Method.get_property=action-target)
+ * @self: a `AdwToast`
+ *
+ * Gets the parameter for action invocations.
+ *
+ * Returns: (transfer none) (nullable): the action target
+ *
+ * Since: 1.0
+ */
+GVariant *
+adw_toast_get_action_target_value (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), NULL);
+
+ return self->action_target;
+}
+
+/**
+ * adw_toast_set_action_target_value: (attributes org.gtk.Method.get_property=action-target)
+ * @self: a `AdwToast`
+ * @action_target: (nullable): the action target
+ *
+ * Sets the parameter for action invocations.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_action_target_value (AdwToast *self,
+ GVariant *action_target)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ if (action_target == self->action_target)
+ return;
+
+ if (action_target && self->action_target &&
+ g_variant_equal (action_target, self->action_target))
+ return;
+
+ g_clear_pointer (&self->action_target, g_variant_unref);
+ self->action_target = g_variant_ref (action_target);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTION_TARGET]);
+}
+
+/**
+ * adw_toast_set_action_target: (skip)
+ * @self: a `AdwToast`
+ * @format_string: (nullable): a [struct@GLib.Variant] format string
+ * @...: arguments appropriate for @target_format
+ *
+ * Sets the parameter for action invocations.
+ *
+ * This is a convenience function that calls [ctor GLib Variant new] for
+ * @format_string and uses the result to call
+ * [method@Adw.Toast.set_action_target_value].
+ *
+ * If you are setting a string-valued target and want to set
+ * the action name at the same time, you can use
+ * [method@Adw.Toast.set_detailed_action_name].
+
+ * Since: 1.0
+ */
+void
+adw_toast_set_action_target (AdwToast *self,
+ const char *format_string,
+ ...)
+{
+ va_list args;
+
+ va_start (args, format_string);
+ adw_toast_set_action_target_value (self,
+ g_variant_new_va (format_string,
+ NULL, &args));
+ va_end (args);
+}
+
+/**
+ * adw_toast_set_detailed_action_name:
+ * @self: a `AdwToast`
+ * @detailed_action_name: (nullable): the detailed action name
+ *
+ * Sets the action name and its parameter.
+ *
+ * @detailed_action_name is a string in the format accepted by
+ * [func@Gio.Action.parse_detailed_name].
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_detailed_action_name (AdwToast *self,
+ const char *detailed_action_name)
+{
+ g_autofree char *name = NULL;
+ g_autoptr (GVariant) target = NULL;
+ g_autoptr (GError) error = NULL;
+
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ if (!detailed_action_name) {
+ adw_toast_set_action_name (self, NULL);
+ adw_toast_set_action_target_value (self, NULL);
+
+ return;
+ }
+
+ if (!g_action_parse_detailed_name (detailed_action_name, &name, &target, &error)) {
+ g_critical ("Couldn't parse detailed action name: %s", error->message);
+
+ return;
+ }
+
+ adw_toast_set_action_name (self, name);
+ adw_toast_set_action_target_value (self, target);
+}
+
+/**
+ * adw_toast_get_priority: (attributes org.gtk.Method.get_property=priority)
+ * @self: a `AdwToast`
+ *
+ * Gets the toast priority.
+ *
+ * Returns: the priority
+ *
+ * Since: 1.0
+ */
+AdwToastPriority
+adw_toast_get_priority (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), ADW_TOAST_PRIORITY_NORMAL);
+
+ return self->priority;
+}
+
+/**
+ * adw_toast_set_priority: (attributes org.gtk.Method.set_property=priority)
+ * @self: a `AdwToast`
+ * @priority: the priority
+ *
+ * Sets the toast priority.
+ *
+ * Priority controls how the toast behaves when another toast is already
+ * being displayed.
+ *
+ * If @priority is `ADW_TOAST_PRIORITY_NORMAL`, the toast will be queued.
+ *
+ * If @priority is `ADW_TOAST_PRIORITY_HIGH`, the toast will be displayed immediately,
+ * pushing the previous toast into the queue instead.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_set_priority (AdwToast *self,
+ AdwToastPriority priority)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+ g_return_if_fail (priority >= ADW_TOAST_PRIORITY_NORMAL &&
+ priority <= ADW_TOAST_PRIORITY_HIGH);
+
+ if (self->priority == priority)
+ return;
+
+ self->priority = priority;
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_PRIORITY]);
+}
+
+/**
+ * adw_toast_dismiss:
+ * @self: a `AdwToast`
+ *
+ * Dismisses @self.
+ *
+ * Since: 1.0
+ */
+void
+adw_toast_dismiss (AdwToast *self)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ if (!self->added) {
+ g_critical ("Trying to dismiss the toast '%s', but it isn't in an "
+ "AdwToastOverlay yet", self->title);
+
+ return;
+ }
+
+ g_signal_emit (self, signals[SIGNAL_DISMISSED], 0, NULL);
+}
+
+gboolean
+adw_toast_get_added (AdwToast *self)
+{
+ g_return_val_if_fail (ADW_IS_TOAST (self), FALSE);
+
+ return self->added;
+}
+
+void
+adw_toast_set_added (AdwToast *self,
+ gboolean added)
+{
+ g_return_if_fail (ADW_IS_TOAST (self));
+
+ self->added = !!added;
+}
diff --git a/src/adw-toast.h b/src/adw-toast.h
new file mode 100644
index 00000000..4111a088
--- /dev/null
+++ b/src/adw-toast.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 Maximiliano Sandoval <msandova gnome org>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#if !defined(_ADWAITA_INSIDE) && !defined(ADWAITA_COMPILATION)
+#error "Only <adwaita.h> can be included directly."
+#endif
+
+#include "adw-version.h"
+
+#include <gtk/gtk.h>
+#include "adw-enums.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ ADW_TOAST_PRIORITY_NORMAL,
+ ADW_TOAST_PRIORITY_HIGH,
+} AdwToastPriority;
+
+#define ADW_TYPE_TOAST (adw_toast_get_type())
+
+ADW_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (AdwToast, adw_toast, ADW, TOAST, GObject)
+
+ADW_AVAILABLE_IN_ALL
+AdwToast *adw_toast_new (const char *title) G_GNUC_WARN_UNUSED_RESULT;
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_toast_get_title (AdwToast *self);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_title (AdwToast *self,
+ const char *title);
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_toast_get_button_label (AdwToast *self);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_button_label (AdwToast *self,
+ const char *button_label);
+
+ADW_AVAILABLE_IN_ALL
+const char *adw_toast_get_action_name (AdwToast *self);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_action_name (AdwToast *self,
+ const char *action_name);
+
+ADW_AVAILABLE_IN_ALL
+GVariant *adw_toast_get_action_target_value (AdwToast *self);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_action_target_value (AdwToast *self,
+ GVariant *action_target);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_action_target (AdwToast *self,
+ const char *format_string,
+ ...);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_detailed_action_name (AdwToast *self,
+ const char *detailed_action_name);
+
+ADW_AVAILABLE_IN_ALL
+AdwToastPriority adw_toast_get_priority (AdwToast *self);
+ADW_AVAILABLE_IN_ALL
+void adw_toast_set_priority (AdwToast *self,
+ AdwToastPriority priority);
+
+ADW_AVAILABLE_IN_ALL
+void adw_toast_dismiss (AdwToast *self);
+
+G_END_DECLS
diff --git a/src/adwaita.h b/src/adwaita.h
index 0fca7f8d..7f7e9c36 100644
--- a/src/adwaita.h
+++ b/src/adwaita.h
@@ -56,6 +56,7 @@ G_BEGIN_DECLS
#include "adw-swipeable.h"
#include "adw-tab-bar.h"
#include "adw-tab-view.h"
+#include "adw-toast.h"
#include "adw-view-stack.h"
#include "adw-view-switcher.h"
#include "adw-view-switcher-bar.h"
diff --git a/src/meson.build b/src/meson.build
index 6d3b5e7a..48871090 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,7 +17,7 @@ adw_public_enum_headers = [
'adw-navigation-direction.h',
'adw-style-manager.h',
'adw-squeezer.h',
- 'adw-tab-bar.h',
+ 'adw-toast.h',
'adw-view-switcher.h',
]
@@ -105,6 +105,7 @@ src_headers = [
'adw-swipeable.h',
'adw-tab-bar.h',
'adw-tab-view.h',
+ 'adw-toast.h',
'adw-view-stack.h',
'adw-view-switcher.h',
'adw-view-switcher-bar.h',
@@ -168,6 +169,7 @@ src_sources = [
'adw-tab-bar.c',
'adw-tab-box.c',
'adw-tab-view.c',
+ 'adw-toast.c',
'adw-version.c',
'adw-view-stack.c',
'adw-view-switcher.c',
diff --git a/tests/meson.build b/tests/meson.build
index 83c40b69..2357f801 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -43,6 +43,7 @@ test_names = [
'test-style-manager',
'test-tab-bar',
'test-tab-view',
+ 'test-toast',
'test-view-switcher',
'test-view-switcher-bar',
'test-window',
diff --git a/tests/test-toast.c b/tests/test-toast.c
new file mode 100644
index 00000000..0ce6364b
--- /dev/null
+++ b/tests/test-toast.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Author: Alexander Mikhaylenko <alexander mikhaylenko puri sm>
+ */
+
+#include <adwaita.h>
+
+int notified;
+
+static void
+notify_cb (GtkWidget *widget, gpointer data)
+{
+ notified++;
+}
+
+static void
+test_adw_toast_title (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ g_autofree char *title = NULL;
+
+ g_assert_nonnull (toast);
+
+ notified = 0;
+ g_signal_connect (toast, "notify::title", G_CALLBACK (notify_cb), NULL);
+
+ g_object_get (toast, "title", &title, NULL);
+ g_assert_cmpstr (title, ==, "Title");
+
+ adw_toast_set_title (toast, "Another title");
+ g_assert_cmpstr (adw_toast_get_title (toast), ==, "Another title");
+ g_assert_cmpint (notified, ==, 1);
+
+ g_object_set (toast, "title", "Title", NULL);
+ g_assert_cmpstr (adw_toast_get_title (toast), ==, "Title");
+ g_assert_cmpint (notified, ==, 2);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_button_label (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ char *button_label;
+
+ g_assert_nonnull (toast);
+
+ notified = 0;
+ g_signal_connect (toast, "notify::button-label", G_CALLBACK (notify_cb), NULL);
+
+ g_object_get (toast, "button-label", &button_label, NULL);
+ g_assert_null (button_label);
+
+ adw_toast_set_button_label (toast, "Button");
+ g_assert_cmpstr (adw_toast_get_button_label (toast), ==, "Button");
+ g_assert_cmpint (notified, ==, 1);
+
+ g_object_set (toast, "button-label", "Button 2", NULL);
+ g_assert_cmpstr (adw_toast_get_button_label (toast), ==, "Button 2");
+ g_assert_cmpint (notified, ==, 2);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_action_name (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ char *action_name;
+
+ g_assert_nonnull (toast);
+
+ notified = 0;
+ g_signal_connect (toast, "notify::action-name", G_CALLBACK (notify_cb), NULL);
+
+ g_object_get (toast, "action-name", &action_name, NULL);
+ g_assert_null (action_name);
+
+ adw_toast_set_action_name (toast, "win.something");
+ g_assert_cmpstr (adw_toast_get_action_name (toast), ==, "win.something");
+ g_assert_cmpint (notified, ==, 1);
+
+ g_object_set (toast, "action-name", "win.something-else", NULL);
+ g_assert_cmpstr (adw_toast_get_action_name (toast), ==, "win.something-else");
+ g_assert_cmpint (notified, ==, 2);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_action_target (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ GVariant *action_target;
+ g_autoptr (GVariant) variant1 = g_variant_ref_sink (g_variant_new_int32 (1));
+ g_autoptr (GVariant) variant2 = g_variant_ref_sink (g_variant_new_int32 (2));
+ g_autoptr (GVariant) variant3 = g_variant_ref_sink (g_variant_new_int32 (3));
+
+ g_assert_nonnull (toast);
+
+ notified = 0;
+ g_signal_connect (toast, "notify::action-target", G_CALLBACK (notify_cb), NULL);
+
+ g_object_get (toast, "action-target", &action_target, NULL);
+ g_assert_null (action_target);
+
+ adw_toast_set_action_target_value (toast, g_variant_new_int32 (1));
+ g_assert_cmpvariant (adw_toast_get_action_target_value (toast), variant1);
+ g_assert_cmpint (notified, ==, 1);
+
+ g_object_set (toast, "action-target", g_variant_new_int32 (2), NULL);
+ g_assert_cmpvariant (adw_toast_get_action_target_value (toast), variant2);
+ g_assert_cmpint (notified, ==, 2);
+
+ adw_toast_set_action_target (toast, "i", 3);
+ g_assert_cmpvariant (adw_toast_get_action_target_value (toast), variant3);
+ g_assert_cmpint (notified, ==, 3);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_detailed_action_name (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ g_autoptr (GVariant) variant = g_variant_ref_sink (g_variant_new_int32 (2));
+
+ g_assert_nonnull (toast);
+
+ g_assert_null (adw_toast_get_action_name (toast));
+ g_assert_null (adw_toast_get_action_target_value (toast));
+
+ adw_toast_set_detailed_action_name (toast, "win.something");
+ g_assert_cmpstr (adw_toast_get_action_name (toast), ==, "win.something");
+ g_assert_null (adw_toast_get_action_target_value (toast));
+
+ adw_toast_set_detailed_action_name (toast, "win.something(2)");
+ g_assert_cmpstr (adw_toast_get_action_name (toast), ==, "win.something");
+ g_assert_cmpvariant (adw_toast_get_action_target_value (toast), variant);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_priority (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ AdwToastPriority priority;
+
+ g_assert_nonnull (toast);
+
+ notified = 0;
+ g_signal_connect (toast, "notify::priority", G_CALLBACK (notify_cb), NULL);
+
+ g_object_get (toast, "priority", &priority, NULL);
+ g_assert_cmpint (priority, ==, ADW_TOAST_PRIORITY_NORMAL);
+
+ adw_toast_set_priority (toast, ADW_TOAST_PRIORITY_HIGH);
+ g_assert_cmpint (adw_toast_get_priority (toast), ==, ADW_TOAST_PRIORITY_HIGH);
+ g_assert_cmpint (notified, ==, 1);
+
+ g_object_set (toast, "priority", ADW_TOAST_PRIORITY_NORMAL, NULL);
+ g_assert_cmpint (adw_toast_get_priority (toast), ==, ADW_TOAST_PRIORITY_NORMAL);
+ g_assert_cmpint (notified, ==, 2);
+
+ g_assert_finalize_object (toast);
+}
+
+static void
+test_adw_toast_dismiss (void)
+{
+ AdwToast *toast = adw_toast_new ("Title");
+ AdwToastOverlay *overlay = g_object_ref_sink (ADW_TOAST_OVERLAY (adw_toast_overlay_new ()));
+
+ g_assert_nonnull (overlay);
+ g_assert_nonnull (toast);
+
+ adw_toast_overlay_add_toast (overlay, g_object_ref (toast));
+ adw_toast_dismiss (toast);
+
+ g_assert_finalize_object (overlay);
+ g_assert_finalize_object (toast);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ gtk_test_init (&argc, &argv, NULL);
+ adw_init ();
+
+ g_test_add_func ("/Adwaita/Toast/title", test_adw_toast_title);
+ g_test_add_func ("/Adwaita/Toast/button_label", test_adw_toast_button_label);
+ g_test_add_func ("/Adwaita/Toast/action_name", test_adw_toast_action_name);
+ g_test_add_func ("/Adwaita/Toast/action_target", test_adw_toast_action_target);
+ g_test_add_func ("/Adwaita/Toast/detailed_action_name", test_adw_toast_detailed_action_name);
+ g_test_add_func ("/Adwaita/Toast/priority", test_adw_toast_priority);
+ g_test_add_func ("/Adwaita/Toast/dismiss", test_adw_toast_dismiss);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]