[gnome-builder] egg: add EggEmptyState widget



commit 80bd1fbe3424d505699f7620da005e2878b02bd2
Author: Christian Hergert <chergert redhat com>
Date:   Tue Mar 1 18:29:43 2016 -0800

    egg: add EggEmptyState widget
    
    This is a convenience widget for creating empty-state/initial-state
    screens. You can embed a link in the subtitle by escaping the markup
    in your UI file. To activate an action, set the uri to
    
      action://group.name::param
    
    The ::param is optional, but can be used to pass a gvariant parameter to
    the action.
    
    For example, to activate the "new-project" action of "app", use:
    
      action://app.new-project
    
    as the URI for an <a href=""></a> link in the subtitle markup.

 contrib/egg/Makefile.am        |    2 +
 contrib/egg/egg-empty-state.c  |  373 ++++++++++++++++++++++++++++++++++++++++
 contrib/egg/egg-empty-state.h  |   50 ++++++
 contrib/egg/egg-empty-state.ui |   45 +++++
 contrib/egg/egg.gresource.xml  |    1 +
 5 files changed, 471 insertions(+), 0 deletions(-)
---
diff --git a/contrib/egg/Makefile.am b/contrib/egg/Makefile.am
index 06f1bdf..dcfe4f5 100644
--- a/contrib/egg/Makefile.am
+++ b/contrib/egg/Makefile.am
@@ -15,6 +15,7 @@ headers_DATA = \
        egg-column-layout.h \
        egg-counter.h \
        egg-date-time.h \
+       egg-empty-state.h \
        egg-frame-source.h \
        egg-heap.h \
        egg-menu-manager.h \
@@ -43,6 +44,7 @@ libegg_private_la_SOURCES = \
        egg-column-layout.c \
        egg-counter.c \
        egg-date-time.c \
+       egg-empty-state.c \
        egg-frame-source.c \
        egg-heap.c \
        egg-menu-manager.c \
diff --git a/contrib/egg/egg-empty-state.c b/contrib/egg/egg-empty-state.c
new file mode 100644
index 0000000..ed76899
--- /dev/null
+++ b/contrib/egg/egg-empty-state.c
@@ -0,0 +1,373 @@
+/* egg-empty-state.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "egg-empty-state.h"
+
+typedef struct
+{
+  GtkBox   *box;
+  GtkImage *image;
+  GtkLabel *subtitle;
+  GtkLabel *title;
+} EggEmptyStatePrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (EggEmptyState, egg_empty_state, GTK_TYPE_BIN)
+
+enum {
+  PROP_0,
+  PROP_ICON_NAME,
+  PROP_RESOURCE,
+  PROP_SUBTITLE,
+  PROP_TITLE,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gboolean
+egg_empty_state_action (GtkWidget   *widget,
+                        const gchar *prefix,
+                        const gchar *action_name,
+                        GVariant    *parameter)
+{
+  GtkWidget *toplevel;
+  GApplication *app;
+  GActionGroup *group = NULL;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (prefix, FALSE);
+  g_return_val_if_fail (action_name, FALSE);
+
+  app = g_application_get_default ();
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  while ((group == NULL) && (widget != NULL))
+    {
+      group = gtk_widget_get_action_group (widget, prefix);
+      widget = gtk_widget_get_parent (widget);
+    }
+
+  if (!group && g_str_equal (prefix, "win") && G_IS_ACTION_GROUP (toplevel))
+    group = G_ACTION_GROUP (toplevel);
+
+  if (!group && g_str_equal (prefix, "app") && G_IS_ACTION_GROUP (app))
+    group = G_ACTION_GROUP (app);
+
+  if (group && g_action_group_has_action (group, action_name))
+    {
+      g_action_group_activate_action (group, action_name, parameter);
+      return TRUE;
+    }
+
+  if (parameter && g_variant_is_floating (parameter))
+    {
+      parameter = g_variant_ref_sink (parameter);
+      g_variant_unref (parameter);
+    }
+
+  g_warning ("Failed to locate action %s.%s", prefix, action_name);
+
+  return FALSE;
+}
+
+static gboolean
+egg_empty_state_activate_link (EggEmptyState *self,
+                               const gchar   *uri,
+                               GtkLabel      *label)
+{
+  g_assert (EGG_IS_EMPTY_STATE (self));
+  g_assert (uri != NULL);
+  g_assert (GTK_IS_LABEL (label));
+
+  if (g_str_has_prefix (uri, "action://"))
+    {
+      g_autofree gchar *full_name = NULL;
+      g_autofree gchar *action_name = NULL;
+      g_autofree gchar *group_name = NULL;
+      g_autoptr(GVariant) param = NULL;
+      g_autoptr(GError) error = NULL;
+
+      uri += strlen ("action://");
+
+      if (g_action_parse_detailed_name (uri, &full_name, &param, &error))
+        {
+          const gchar *dot = strchr (full_name, '.');
+
+          if (param != NULL && g_variant_is_floating (param))
+            param = g_variant_ref_sink (param);
+
+          if (dot == NULL)
+            return FALSE;
+
+          group_name = g_strndup (full_name, dot - full_name);
+          action_name = g_strdup (++dot);
+
+          egg_empty_state_action (GTK_WIDGET (self),
+                                  group_name,
+                                  action_name,
+                                  param);
+
+          return TRUE;
+        }
+      else
+        g_warning ("%s", error->message);
+    }
+
+  return FALSE;
+}
+
+static void
+egg_empty_state_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  EggEmptyState *self = EGG_EMPTY_STATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ICON_NAME:
+      g_value_set_string (value, egg_empty_state_get_icon_name (self));
+      break;
+
+    case PROP_SUBTITLE:
+      g_value_set_string (value, egg_empty_state_get_subtitle (self));
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, egg_empty_state_get_title (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+egg_empty_state_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  EggEmptyState *self = EGG_EMPTY_STATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ICON_NAME:
+      egg_empty_state_set_icon_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_RESOURCE:
+      egg_empty_state_set_resource (self, g_value_get_string (value));
+      break;
+
+    case PROP_SUBTITLE:
+      egg_empty_state_set_subtitle (self, g_value_get_string (value));
+      break;
+
+    case PROP_TITLE:
+      egg_empty_state_set_title (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+egg_empty_state_class_init (EggEmptyStateClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->get_property = egg_empty_state_get_property;
+  object_class->set_property = egg_empty_state_set_property;
+
+  properties [PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name",
+                         "Icon Name",
+                         "The name of the icon to display",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_RESOURCE] =
+    g_param_spec_string ("resource",
+                         "Resource",
+                         "A resource path to use for the icon",
+                         NULL,
+                         (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SUBTITLE] =
+    g_param_spec_string ("subtitle",
+                         "Subtitle",
+                         "The subtitle of the empty state",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "The title of the empty state",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/libegg-private/egg-empty-state.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, EggEmptyState, box);
+  gtk_widget_class_bind_template_child_private (widget_class, EggEmptyState, image);
+  gtk_widget_class_bind_template_child_private (widget_class, EggEmptyState, title);
+  gtk_widget_class_bind_template_child_private (widget_class, EggEmptyState, subtitle);
+}
+
+static void
+egg_empty_state_init (EggEmptyState *self)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (priv->subtitle,
+                           "activate-link",
+                           G_CALLBACK (egg_empty_state_activate_link),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+const gchar *
+egg_empty_state_get_icon_name (EggEmptyState *self)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+  const gchar *icon_name = NULL;
+
+  g_return_val_if_fail (EGG_IS_EMPTY_STATE (self), NULL);
+
+  gtk_image_get_icon_name (priv->image, &icon_name, NULL);
+
+  return icon_name;
+}
+
+void
+egg_empty_state_set_icon_name (EggEmptyState *self,
+                               const gchar   *icon_name)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_if_fail (EGG_IS_EMPTY_STATE (self));
+
+  if (g_strcmp0 (icon_name, egg_empty_state_get_icon_name (self)) != 0)
+    {
+      g_object_set (priv->image, "icon-name", icon_name, NULL);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
+    }
+}
+
+const gchar *
+egg_empty_state_get_subtitle (EggEmptyState *self)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_val_if_fail (EGG_IS_EMPTY_STATE (self), NULL);
+
+  return gtk_label_get_label (priv->subtitle);
+}
+
+void
+egg_empty_state_set_subtitle (EggEmptyState *self,
+                              const gchar   *subtitle)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_if_fail (EGG_IS_EMPTY_STATE (self));
+
+  if (g_strcmp0 (subtitle, egg_empty_state_get_subtitle (self)) != 0)
+    {
+      gtk_label_set_label (priv->subtitle, subtitle);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SUBTITLE]);
+    }
+}
+
+const gchar *
+egg_empty_state_get_title (EggEmptyState *self)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_val_if_fail (EGG_IS_EMPTY_STATE (self), NULL);
+
+  return gtk_label_get_label (priv->title);
+}
+
+void
+egg_empty_state_set_title (EggEmptyState *self,
+                           const gchar   *title)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_if_fail (EGG_IS_EMPTY_STATE (self));
+
+  if (g_strcmp0 (title, egg_empty_state_get_title (self)) != 0)
+    {
+      gtk_label_set_label (priv->title, title);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+    }
+}
+
+void
+egg_empty_state_set_resource (EggEmptyState *self,
+                              const gchar   *resource)
+{
+  EggEmptyStatePrivate *priv = egg_empty_state_get_instance_private (self);
+
+  g_return_if_fail (EGG_IS_EMPTY_STATE (self));
+
+  if (resource != NULL)
+    {
+      GdkPixbuf *pixbuf;
+      GError *error = NULL;
+      gint scale_factor;
+
+      scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+
+      pixbuf = gdk_pixbuf_new_from_resource_at_scale (resource,
+                                                      128 * scale_factor,
+                                                      128 * scale_factor,
+                                                      TRUE,
+                                                      &error);
+
+      if (pixbuf == NULL)
+        {
+          g_warning ("%s", error->message);
+          g_clear_error (&error);
+          return;
+        }
+
+      g_object_set (priv->image,
+                    "pixbuf", pixbuf,
+                    NULL);
+
+      g_clear_object (&pixbuf);
+    }
+}
diff --git a/contrib/egg/egg-empty-state.h b/contrib/egg/egg-empty-state.h
new file mode 100644
index 0000000..ebef03e
--- /dev/null
+++ b/contrib/egg/egg-empty-state.h
@@ -0,0 +1,50 @@
+/* egg-empty-state.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EGG_EMPTY_STATE_H
+#define EGG_EMPTY_STATE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_EMPTY_STATE (egg_empty_state_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (EggEmptyState, egg_empty_state, EGG, EMPTY_STATE, GtkBin)
+
+struct _EggEmptyStateClass
+{
+  GtkBinClass parent_class;
+};
+
+GtkWidget   *egg_empty_state_new           (void);
+const gchar *egg_empty_state_get_icon_name (EggEmptyState *self);
+void         egg_empty_state_set_icon_name (EggEmptyState *self,
+                                            const gchar   *icon_name);
+void         egg_empty_state_set_resource  (EggEmptyState *self,
+                                            const gchar   *resource);
+const gchar *egg_empty_state_get_title     (EggEmptyState *self);
+void         egg_empty_state_set_title     (EggEmptyState *self,
+                                            const gchar   *title);
+const gchar *egg_empty_state_get_subtitle  (EggEmptyState *self);
+void         egg_empty_state_set_subtitle  (EggEmptyState *self,
+                                            const gchar   *title);
+
+G_END_DECLS
+
+#endif /* EGG_EMPTY_STATE_H */
diff --git a/contrib/egg/egg-empty-state.ui b/contrib/egg/egg-empty-state.ui
new file mode 100644
index 0000000..a84a1dd
--- /dev/null
+++ b/contrib/egg/egg-empty-state.ui
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="EggEmptyState" parent="GtkBin">
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="halign">center</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="valign">center</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkImage" id="image">
+            <property name="pixel-size">128</property>
+            <property name="visible">true</property>
+            <property name="xalign">0.5</property>
+            <property name="yalign">0.5</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="title">
+            <property name="visible">true</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+            <attributes>
+              <attribute name="scale" value="2.0"/>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="subtitle">
+            <property name="visible">true</property>
+            <property name="track-visited-links">false</property>
+            <property name="use-markup">true</property>
+            <property name="wrap">true</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/contrib/egg/egg.gresource.xml b/contrib/egg/egg.gresource.xml
index 8d1cf87..702e887 100644
--- a/contrib/egg/egg.gresource.xml
+++ b/contrib/egg/egg.gresource.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/gnome/libegg-private">
+    <file>egg-empty-state.ui</file>
     <file>egg-pill-box.ui</file>
     <file>egg-simple-popover.ui</file>
   </gresource>


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