[libadwaita] Add AdwToastWidget



commit 61a1dccccd7925b2524bd75f7dc7335b876fbfaf
Author: Maximiliano Sandoval R <msandova gnome org>
Date:   Sat Nov 6 22:09:29 2021 +0500

    Add AdwToastWidget
    
    Co-authored-by: Alexander Mikhaylenko <alexm gnome org>

 src/adw-toast-widget-private.h    |  26 +++++
 src/adw-toast-widget.c            | 206 ++++++++++++++++++++++++++++++++++++++
 src/adw-toast-widget.ui           |  80 +++++++++++++++
 src/adwaita.gresources.xml        |   1 +
 src/meson.build                   |   1 +
 src/stylesheet/widgets/_misc.scss |  26 +++++
 6 files changed, 340 insertions(+)
---
diff --git a/src/adw-toast-widget-private.h b/src/adw-toast-widget-private.h
new file mode 100644
index 00000000..ff523f71
--- /dev/null
+++ b/src/adw-toast-widget-private.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ *
+ * 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 "adw-toast.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ADW_TYPE_TOAST_WIDGET (adw_toast_widget_get_type())
+
+G_DECLARE_FINAL_TYPE (AdwToastWidget, adw_toast_widget, ADW, TOAST_WIDGET, GtkWidget)
+
+GtkWidget *adw_toast_widget_new (AdwToast *toast) G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
diff --git a/src/adw-toast-widget.c b/src/adw-toast-widget.c
new file mode 100644
index 00000000..a9fcb018
--- /dev/null
+++ b/src/adw-toast-widget.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2021 Maximiliano Sandoval <msandova gnome org>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "adw-toast-widget-private.h"
+
+#define TOAST_DURATION 5000
+
+struct _AdwToastWidget {
+  GtkWidget parent_instance;
+
+  AdwToast *toast;
+
+  guint hide_timeout_id;
+  gint inhibit_count;
+};
+
+enum {
+  PROP_0,
+  PROP_TOAST,
+  LAST_PROP,
+};
+
+static GParamSpec *props[LAST_PROP];
+
+G_DEFINE_TYPE (AdwToastWidget, adw_toast_widget, GTK_TYPE_WIDGET)
+
+static gboolean
+string_is_not_empty (gpointer    user_data,
+                     const char *string)
+{
+  return string && string[0];
+}
+
+static gboolean
+timeout_cb (AdwToastWidget *self)
+{
+  self->hide_timeout_id = 0;
+
+  adw_toast_dismiss (self->toast);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+start_timeout (AdwToastWidget *self)
+{
+  if (!self->hide_timeout_id)
+    self->hide_timeout_id =
+      g_timeout_add (TOAST_DURATION,
+                     G_SOURCE_FUNC (timeout_cb),
+                     self);
+}
+
+static void
+end_timeout (AdwToastWidget *self)
+{
+  g_clear_handle_id (&self->hide_timeout_id, g_source_remove);
+}
+
+static void
+inhibit_hide (AdwToastWidget *self)
+{
+  if (self->inhibit_count++ == 0)
+    end_timeout (self);
+}
+
+static void
+uninhibit_hide (AdwToastWidget  *self)
+{
+  g_assert (self->inhibit_count);
+
+  if (--self->inhibit_count == 0)
+    start_timeout (self);
+}
+
+static void
+dismiss (AdwToastWidget *self)
+{
+  end_timeout (self);
+
+  adw_toast_dismiss (self->toast);
+}
+
+static gboolean
+close_idle_cb (AdwToastWidget *self)
+{
+  dismiss (self);
+  g_object_unref (self);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+action_clicked_cb (AdwToastWidget *self)
+{
+  end_timeout (self);
+
+  /* Keep the widget alive through the idle. Otherwise it may be immediately
+   * destroyed if animations are disabled */
+  g_idle_add (G_SOURCE_FUNC (close_idle_cb), g_object_ref (self));
+}
+
+static void
+adw_toast_widget_dispose (GObject *object)
+{
+  AdwToastWidget *self = ADW_TOAST_WIDGET (object);
+  GtkWidget *child;
+
+  end_timeout (self);
+
+  while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
+    gtk_widget_unparent (child);
+
+  g_clear_pointer (&self->toast, g_object_unref);
+
+  G_OBJECT_CLASS (adw_toast_widget_parent_class)->dispose (object);
+}
+
+static void
+adw_toast_widget_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  AdwToastWidget *self = ADW_TOAST_WIDGET (object);
+
+  switch (prop_id) {
+  case PROP_TOAST:
+    g_value_set_object (value, self->toast);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_toast_widget_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  AdwToastWidget *self = ADW_TOAST_WIDGET (object);
+
+  switch (prop_id) {
+  case PROP_TOAST:
+    g_set_object (&self->toast, g_value_get_object (value));
+    end_timeout (self);
+    start_timeout (self);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+adw_toast_widget_class_init (AdwToastWidgetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->dispose = adw_toast_widget_dispose;
+  object_class->get_property = adw_toast_widget_get_property;
+  object_class->set_property = adw_toast_widget_set_property;
+
+  props[PROP_TOAST] =
+      g_param_spec_object ("toast",
+                           "Toast",
+                           "The displayed toast",
+                           ADW_TYPE_TOAST,
+                           G_PARAM_READWRITE);
+
+  g_object_class_install_properties (object_class, LAST_PROP, props);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/Adwaita/ui/adw-toast-widget.ui");
+
+  gtk_widget_class_bind_template_callback (widget_class, string_is_not_empty);
+  gtk_widget_class_bind_template_callback (widget_class, action_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, dismiss);
+  gtk_widget_class_bind_template_callback (widget_class, inhibit_hide);
+  gtk_widget_class_bind_template_callback (widget_class, uninhibit_hide);
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
+  gtk_widget_class_set_css_name (widget_class, "toast");
+}
+
+static void
+adw_toast_widget_init (AdwToastWidget *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget *
+adw_toast_widget_new (AdwToast *toast)
+{
+  g_assert (ADW_IS_TOAST (toast));
+
+  return g_object_new (ADW_TYPE_TOAST_WIDGET,
+                       "toast", toast,
+                       NULL);
+}
diff --git a/src/adw-toast-widget.ui b/src/adw-toast-widget.ui
new file mode 100644
index 00000000..bb69ba3b
--- /dev/null
+++ b/src/adw-toast-widget.ui
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="AdwToastWidget">
+    <property name="hexpand">False</property>
+    <child>
+      <object class="GtkEventControllerFocus">
+        <signal name="enter" handler="inhibit_hide" swapped="yes"/>
+        <signal name="leave" handler="uninhibit_hide" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkEventControllerMotion">
+        <signal name="enter" handler="inhibit_hide" swapped="yes"/>
+        <signal name="leave" handler="uninhibit_hide" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGestureClick">
+        <signal name="pressed" handler="inhibit_hide" swapped="yes"/>
+        <signal name="released" handler="uninhibit_hide" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="title">
+        <property name="ellipsize">end</property>
+        <property name="xalign">0</property>
+        <property name="hexpand">True</property>
+        <property name="use-markup">True</property>
+        <binding name="label">
+          <lookup name="title" type="AdwToast">
+            <lookup name="toast">AdwToastWidget</lookup>
+          </lookup>
+        </binding>
+        <style>
+          <class name="heading"/>
+        </style>
+      </object>
+    </child>
+    <child>
+      <object class="GtkButton" id="action_button">
+        <property name="valign">center</property>
+        <binding name="visible">
+          <closure type="gboolean" function="string_is_not_empty">
+            <lookup name="button-label" type="AdwToast">
+              <lookup name="toast">AdwToastWidget</lookup>
+            </lookup>
+          </closure>
+        </binding>
+        <binding name="label">
+          <lookup name="button-label" type="AdwToast">
+            <lookup name="toast">AdwToastWidget</lookup>
+          </lookup>
+        </binding>
+        <binding name="action-name">
+          <lookup name="action-name" type="AdwToast">
+            <lookup name="toast">AdwToastWidget</lookup>
+          </lookup>
+        </binding>
+        <binding name="action-target">
+          <lookup name="action-target" type="AdwToast">
+            <lookup name="toast">AdwToastWidget</lookup>
+          </lookup>
+        </binding>
+        <signal name="clicked" handler="action_clicked_cb" swapped="yes"/>
+      </object>
+    </child>
+    <child>
+      <object class="GtkButton" id="close_button">
+        <property name="valign">center</property>
+        <property name="icon-name">window-close-symbolic</property>
+        <signal name="clicked" handler="dismiss" swapped="yes"/>
+        <style>
+          <class name="circular"/>
+          <class name="flat"/>
+        </style>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/adwaita.gresources.xml b/src/adwaita.gresources.xml
index 69f5f483..ea354366 100644
--- a/src/adwaita.gresources.xml
+++ b/src/adwaita.gresources.xml
@@ -17,6 +17,7 @@
     <file preprocess="xml-stripblanks">adw-status-page.ui</file>
     <file preprocess="xml-stripblanks">adw-tab.ui</file>
     <file preprocess="xml-stripblanks">adw-tab-bar.ui</file>
+    <file preprocess="xml-stripblanks">adw-toast-widget.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-bar.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-button.ui</file>
     <file preprocess="xml-stripblanks">adw-view-switcher-title.ui</file>
diff --git a/src/meson.build b/src/meson.build
index 48871090..d54208ef 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -170,6 +170,7 @@ src_sources = [
   'adw-tab-box.c',
   'adw-tab-view.c',
   'adw-toast.c',
+  'adw-toast-widget.c',
   'adw-version.c',
   'adw-view-stack.c',
   'adw-view-switcher.c',
diff --git a/src/stylesheet/widgets/_misc.scss b/src/stylesheet/widgets/_misc.scss
index fbf5fdd6..51f10bb0 100644
--- a/src/stylesheet/widgets/_misc.scss
+++ b/src/stylesheet/widgets/_misc.scss
@@ -49,6 +49,32 @@ separator {
   border { border: none; }
 }
 
+/**********
+ * Toasts *
+ **********/
+
+toast {
+  @extend %osd;
+
+  margin: 12px;
+  margin-bottom: 24px;
+
+  border-radius: 150px;
+  border-spacing: 6px;
+  padding: 6px;
+
+  &:dir(ltr) { padding-left: 12px; }
+  &:dir(rtl) { padding-right: 12px; }
+
+  > label {
+    margin: 0 6px;
+  }
+
+  @if $contrast == 'high' {
+    box-shadow: 0 0 0 1px $border_color;
+  }
+}
+
 /**************
  * GtkVideo *
  **************/


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]