[gtk/wip/otte/listview: 95/152] gtk-demo: Introduce awards



commit 72b96cb92def3b60b71f7684500dbe9407fadb5f
Author: Benjamin Otte <otte redhat com>
Date:   Fri May 31 04:52:13 2019 +0200

    gtk-demo: Introduce awards
    
    We need a way to get a useful listbox, so here we go!

 demos/gtk-demo/award.c            | 247 ++++++++++++++++++++++++++++++++++++++
 demos/gtk-demo/award.h            |  18 +++
 demos/gtk-demo/awardlistitem.ui   |  11 ++
 demos/gtk-demo/awards.ui          |  89 ++++++++++++++
 demos/gtk-demo/awardview.c        |  49 ++++++++
 demos/gtk-demo/demo.gresource.xml |   7 ++
 demos/gtk-demo/listbox.c          |  10 +-
 demos/gtk-demo/main.c             |   4 +
 demos/gtk-demo/meson.build        |   2 +
 demos/gtk-demo/password_entry.c   |  16 ++-
 demos/gtk-demo/sliding_puzzle.c   |  25 +++-
 11 files changed, 473 insertions(+), 5 deletions(-)
---
diff --git a/demos/gtk-demo/award.c b/demos/gtk-demo/award.c
new file mode 100644
index 0000000000..389bfc6790
--- /dev/null
+++ b/demos/gtk-demo/award.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright © 2018 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "award.h"
+
+struct _GtkAward
+{
+  GObject parent;
+
+  char *explanation;
+  char *name;
+  char *title;
+  GDateTime *granted; /* or NULL if not granted */
+};
+
+enum {
+  PROP_0,
+  PROP_EXPLANATION,
+  PROP_NAME,
+  PROP_TITLE,
+  PROP_GRANTED,
+
+  N_PROPS,
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+G_DEFINE_TYPE (GtkAward, gtk_award, G_TYPE_OBJECT)
+
+static void
+gtk_award_set_property (GObject      *object,
+                        guint         prop_id,
+                        const GValue *value,
+                        GParamSpec   *pspec)
+
+{
+  GtkAward *self = GTK_AWARD (object);
+
+  switch (prop_id)
+    {
+    case PROP_EXPLANATION:
+      self->explanation = g_value_dup_string (value);
+      break;
+
+    case PROP_NAME:
+      self->name = g_value_dup_string (value);
+      break;
+
+    case PROP_TITLE:
+      self->title = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_award_get_property (GObject    *object,
+                        guint       prop_id,
+                        GValue     *value,
+                        GParamSpec *pspec)
+{
+  GtkAward *self = GTK_AWARD (object);
+
+  switch (prop_id)
+    {
+    case PROP_EXPLANATION:
+      g_value_set_string (value, self->explanation);
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    case PROP_TITLE:
+      g_value_set_string (value, self->title);
+      break;
+
+    case PROP_GRANTED:
+      g_value_set_boxed (value, self->granted);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_award_dispose (GObject *object)
+{
+  GtkAward *self = GTK_AWARD (object);
+
+  g_clear_pointer (&self->name, g_free);
+  g_clear_pointer (&self->title, g_free);
+  g_clear_pointer (&self->granted, g_date_time_unref);
+
+  G_OBJECT_CLASS (gtk_award_parent_class)->dispose (object);
+}
+
+static void
+gtk_award_class_init (GtkAwardClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  gobject_class->set_property = gtk_award_set_property;
+  gobject_class->get_property = gtk_award_get_property;
+  gobject_class->dispose = gtk_award_dispose;
+
+  properties[PROP_EXPLANATION] =
+    g_param_spec_string ("explanation",
+                         "Explanation",
+                         "How to get the title",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "internal name of the award",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_TITLE] =
+    g_param_spec_string ("title",
+                         "Title",
+                         "user-visible title",
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | 
G_PARAM_STATIC_STRINGS);
+
+  properties[PROP_GRANTED] =
+    g_param_spec_boxed ("granted",
+                        "Granted",
+                        "Timestamp the award was granted or NULL if not granted yet",
+                        G_TYPE_DATE_TIME,
+                        G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gtk_award_init (GtkAward *self)
+{
+}
+
+GListModel *
+gtk_award_get_list (void)
+{
+  static GListModel *list = NULL;
+
+  if (list == NULL)
+    {
+      GtkBuilder *builder;
+
+      g_type_ensure (GTK_TYPE_AWARD);
+      builder = gtk_builder_new_from_resource ("/awards.ui");
+      list = G_LIST_MODEL (gtk_builder_get_object (builder, "list"));
+      g_object_ref (list);
+      g_object_unref (builder);
+    }
+
+  return g_object_ref (list);
+}
+
+const char *
+gtk_award_get_name (GtkAward *award)
+{
+  return award->name;
+}
+
+const char *
+gtk_award_get_title (GtkAward *award)
+{
+  return award->title;
+}
+
+GDateTime *
+gtk_award_get_granted (GtkAward *award)
+{
+  return award->granted;
+}
+
+GtkAward *
+award_find (const char *name)
+{
+  GListModel *list;
+  GtkAward *self;
+  guint i;
+
+  list = gtk_award_get_list ();
+  g_object_unref (list);
+
+  for (i = 0; i < g_list_model_get_n_items (list); i++)
+    {
+      self = g_list_model_get_item (list, i);
+      g_object_unref (self);
+
+      if (g_ascii_strcasecmp (name, self->name) == 0)
+        return self;
+    }
+
+  return NULL;
+}
+
+void
+award (const char *name)
+{
+  GtkAward *self;
+  GNotification *notification;
+
+  self = award_find (name);
+  if (self == NULL)
+    {
+      g_warning ("Did not find award \"%s\"", name);
+      return;
+    }
+
+  if (self->granted)
+    return;
+
+  self->granted = g_date_time_new_now_utc ();
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_GRANTED]);
+
+  notification = g_notification_new ("You won an award!");
+  g_notification_set_body (notification, self->title);
+  g_application_send_notification (g_application_get_default (), NULL, notification);
+  g_object_unref (notification);
+}
+
diff --git a/demos/gtk-demo/award.h b/demos/gtk-demo/award.h
new file mode 100644
index 0000000000..0c4cca01f2
--- /dev/null
+++ b/demos/gtk-demo/award.h
@@ -0,0 +1,18 @@
+#ifndef __AWARD_H__
+#define __AWARD_H__
+
+#include <gtk/gtk.h>
+
+#define GTK_TYPE_AWARD (gtk_award_get_type ())
+
+G_DECLARE_FINAL_TYPE (GtkAward, gtk_award, GTK, AWARD, GObject)
+
+GListModel *    gtk_award_get_list                      (void);
+
+const char *    gtk_award_get_name                      (GtkAward               *award);
+const char *    gtk_award_get_title                     (GtkAward               *award);
+GDateTime *     gtk_award_get_granted                   (GtkAward               *award);
+
+void            award                                   (const char             *name);
+
+#endif /* __AWARD_H__ */
diff --git a/demos/gtk-demo/awardlistitem.ui b/demos/gtk-demo/awardlistitem.ui
new file mode 100644
index 0000000000..8e2a1415fd
--- /dev/null
+++ b/demos/gtk-demo/awardlistitem.ui
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk40">
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkLabel">
+        <property name="label" bind-source="GtkListItem" bind-property="position"></property>
+        <property name="margin">6</property>
+      </object>
+    </property>
+  </template>
+</interface>
diff --git a/demos/gtk-demo/awards.ui b/demos/gtk-demo/awards.ui
new file mode 100644
index 0000000000..5979c9253a
--- /dev/null
+++ b/demos/gtk-demo/awards.ui
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <object class="GListStore" id="list">
+    <property name="item-type">GtkAward</property>
+    <child>
+      <object class="GtkAward">
+        <property name="name">demo-inspector</property>
+        <!-- Transformers -->
+        <property name="title" translatable="yes">You got a high-rise double-pump carburetor.</property>
+        <property name="explanation" translatable="yes">Launch the inspector</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">demo-start</property>
+        <!-- The Matrix -->
+        <property name="title" translatable="yes">After this, there is no turning back.</property>
+        <property name="explanation" translatable="yes">Start gtk-demo</property>
+      </object>
+    </child>
+
+    <child>
+      <object class="GtkAward">
+        <property name="name">listbox-reshare</property>
+        <!-- Mean Girls -->
+        <property name="title" translatable="yes">Trying to make fetch happen</property>
+        <property name="explanation" translatable="yes">Reshare a tweet</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">listbox-100th-row</property>
+        <!-- Aladdin -->
+        <property name="title" translatable="yes">The ever impressive, long contained, often imitated, but 
never duplicated Genie of the lamp.</property>
+        <property name="explanation" translatable="yes">Select a 100th row in a list</property>
+      </object>
+    </child>
+
+    <child>
+      <object class="GtkAward">
+        <property name="name">password-best</property>
+        <!-- Spaceballs -->
+        <property name="title" translatable="yes">I've got the same combination on my luggage!</property>
+        <property name="explanation" translatable="yes">Use "12345" as the password</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">password-correct</property>
+        <!-- Stanley Parable -->
+        <property name="title" translatable="yes">Night Shark 1-1-5</property>
+        <property name="explanation" translatable="yes">Correctly enter a password</property>
+      </object>
+    </child>
+
+    <child>
+      <object class="GtkAward">
+        <property name="name">puzzle-give-up</property>
+        <!-- Pretty Woman -->
+        <property name="title" translatable="yes">Big Mistake. Big. Huge!</property>
+        <property name="explanation" translatable="yes">Close the puzzle without finishing it</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">puzzle-solve</property>
+        <!-- The Incredibles -->
+        <property name="title" translatable="yes">That was totally wicked!</property>
+        <property name="explanation" translatable="yes">Solve a puzzle</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">puzzle-solve-animated</property>
+        <!-- The Phantom Menace -->
+        <property name="title" translatable="yes">A surprise to be sure but a welcome one.</property>
+        <property name="explanation" translatable="yes">Solve an animated puzzle</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkAward">
+        <property name="name">puzzle-solve-large</property>
+        <!-- Portal -->
+        <property name="title" translatable="yes">Science isn't about WHY. It's about WHY NOT?!</property>
+        <property name="explanation" translatable="yes">Solve a puzzle with at least 20 pieces</property>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/demos/gtk-demo/awardview.c b/demos/gtk-demo/awardview.c
new file mode 100644
index 0000000000..f532344a59
--- /dev/null
+++ b/demos/gtk-demo/awardview.c
@@ -0,0 +1,49 @@
+/* Awards
+ *
+ * This demo demonstrates how to use lists to show the awards you have collected
+ * while exploring this demo.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+/* Include the header for accessing the awards */
+#include "award.h"
+
+static GtkWidget *window = NULL;
+
+GtkWidget *
+do_awardview (GtkWidget *do_widget)
+{
+  if (!window)
+    {
+      GtkWidget *sw, *listview;
+      GListModel *list;
+
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_display (GTK_WINDOW (window),
+                              gtk_widget_get_display (do_widget));
+      gtk_window_set_title (GTK_WINDOW (window), "Awards");
+      gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
+      g_signal_connect (window, "destroy",
+                        G_CALLBACK (gtk_widget_destroyed), &window);
+
+      sw = gtk_scrolled_window_new (NULL, NULL);
+      gtk_container_add (GTK_CONTAINER (window), sw);
+
+      listview = gtk_list_view_new_with_factory (
+          gtk_builder_list_item_factory_new_from_resource (NULL, "/awardview/awardlistitem.ui"));
+      list = gtk_award_get_list ();
+      gtk_list_view_set_model (GTK_LIST_VIEW (listview), list);
+      g_object_unref (list);
+      gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
+      gtk_container_add (GTK_CONTAINER (sw), listview);
+    }
+
+  if (!gtk_widget_get_visible (window))
+    gtk_widget_show (window);
+  else
+    gtk_widget_destroy (window);
+
+  return window;
+}
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 57ec059727..acd6ed7543 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
+  <gresource prefix="/">
+    <file>awards.ui</file>
+  </gresource>
   <gresource prefix="/ui">
     <file preprocess="xml-stripblanks">main.ui</file>
     <file preprocess="xml-stripblanks">appmenu.ui</file>
@@ -9,6 +12,9 @@
     <file>application.ui</file>
     <file>menus.ui</file>
   </gresource>
+  <gresource prefix="/awardview">
+    <file>awardlistitem.ui</file>
+  </gresource>
   <gresource prefix="/builder">
     <file>demo.ui</file>
   </gresource>
@@ -153,6 +159,7 @@
   </gresource>
   <gresource prefix="/sources">
     <file>application_demo.c</file>
+    <file>awardview.c</file>
     <file>assistant.c</file>
     <file>builder.c</file>
     <file>changedisplay.c</file>
diff --git a/demos/gtk-demo/listbox.c b/demos/gtk-demo/listbox.c
index 4e70c2f979..00e24f5b88 100644
--- a/demos/gtk-demo/listbox.c
+++ b/demos/gtk-demo/listbox.c
@@ -8,6 +8,7 @@
 #include <gtk/gtk.h>
 #include <stdlib.h>
 #include <string.h>
+#include "award.h"
 
 static GdkPixbuf *avatar_pixbuf_other;
 static GtkWidget *window = NULL;
@@ -234,9 +235,9 @@ reshare_clicked (GtkMessageRow *row,
 {
   GtkMessageRowPrivate *priv = row->priv;
 
+  award ("listbox-reshare");
   priv->message->n_reshares++;
   gtk_message_row_update (row);
-
 }
 
 static void
@@ -255,11 +256,14 @@ gtk_message_row_state_flags_changed (GtkWidget    *widget,
 {
   GtkMessageRowPrivate *priv = GTK_MESSAGE_ROW (widget)->priv;
   GtkStateFlags flags;
+  gboolean visible;
 
   flags = gtk_widget_get_state_flags (widget);
 
-  gtk_widget_set_visible (priv->extra_buttons_box,
-                          flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED));
+  visible = flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED) ? TRUE : FALSE;
+  gtk_widget_set_visible (priv->extra_buttons_box, visible);
+  if (visible && gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (widget)) % 100 == 99)
+    award ("listbox-100th-row");
 
   GTK_WIDGET_CLASS (gtk_message_row_parent_class)->state_flags_changed (widget, previous_state_flags);
 }
diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c
index 6dc59da639..53f0dfc605 100644
--- a/demos/gtk-demo/main.c
+++ b/demos/gtk-demo/main.c
@@ -8,6 +8,7 @@
 #include <gtk/gtk.h>
 #include <glib/gstdio.h>
 
+#include "award.h"
 #include "demos.h"
 
 static GtkWidget *info_view;
@@ -111,6 +112,7 @@ activate_inspector (GSimpleAction *action,
                     gpointer       user_data)
 {
   gtk_window_set_interactive_debugging (TRUE);
+  award ("demo-inspector");
 }
 
 static void
@@ -1091,6 +1093,8 @@ activate (GApplication *app)
 
   gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview));
 
+  award ("demo-start");
+
   gtk_widget_show (GTK_WIDGET (window));
 
   g_object_unref (builder);
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index abf5371c87..f1f1ad11df 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -3,6 +3,7 @@
 demos = files([
   'application_demo.c',
   'assistant.c',
+  'awardview.c',
   'builder.c',
   'changedisplay.c',
   'clipboard.c',
@@ -88,6 +89,7 @@ demos = files([
 gtkdemo_deps = [ libgtk_dep, ]
 
 extra_demo_sources = files(['main.c',
+                            'award.c',
                             'gtkfishbowl.c',
                             'fontplane.c',
                             'gtkgears.c',
diff --git a/demos/gtk-demo/password_entry.c b/demos/gtk-demo/password_entry.c
index e92e625876..313c6ee927 100644
--- a/demos/gtk-demo/password_entry.c
+++ b/demos/gtk-demo/password_entry.c
@@ -11,6 +11,8 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
+#include "award.h"
+
 static GtkWidget *entry;
 static GtkWidget *entry2;
 static GtkWidget *button;
@@ -25,6 +27,18 @@ update_button (GObject    *object,
 
   gtk_widget_set_sensitive (button,
                             text[0] != '\0' && g_str_equal (text, text2));
+
+  if (g_str_equal (text, text2) &&
+      g_ascii_strcasecmp (text, "12345") == 0)
+    award ("password-best");
+}
+
+static void
+button_pressed (GtkButton *button,
+                GtkWidget *window)
+{
+  award ("password-correct");
+  gtk_widget_destroy (window);
 }
 
 GtkWidget *
@@ -72,7 +86,7 @@ do_password_entry (GtkWidget *do_widget)
 
       button = gtk_button_new_with_mnemonic ("_Done");
       gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
-      g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
+      g_signal_connect (button, "clicked", G_CALLBACK (button_pressed), window);
       gtk_widget_set_sensitive (button, FALSE);
       gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
 
diff --git a/demos/gtk-demo/sliding_puzzle.c b/demos/gtk-demo/sliding_puzzle.c
index 9486004605..2b3e962bdd 100644
--- a/demos/gtk-demo/sliding_puzzle.c
+++ b/demos/gtk-demo/sliding_puzzle.c
@@ -11,6 +11,9 @@
 #include "puzzlepiece.h"
 #include "paintable.h"
 
+/* Give out awards */
+#include "award.h"
+
 static GtkWidget *window = NULL;
 static GtkWidget *frame = NULL;
 static GtkWidget *choices = NULL;
@@ -156,6 +159,14 @@ check_solved (GtkWidget *grid)
   picture = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
   gtk_picture_set_paintable (GTK_PICTURE (picture), piece);
 
+  /* Hand out a bunch of awards
+   */
+  award ("puzzle-solve");
+  if ((gdk_paintable_get_flags (piece) & GDK_PAINTABLE_STATIC_CONTENTS) == 0)
+    award ("puzzle-solve-animated");
+  if (height * width > 20)
+    award ("puzzle-solve-large");
+
   return TRUE;
 }
 
@@ -401,6 +412,18 @@ add_choice (GtkWidget    *choices,
   gtk_container_add (GTK_CONTAINER (choices), icon);
 }
 
+static void
+widget_destroyed (GtkWidget      *widget,
+                 GtkWidget     **widget_pointer)
+{
+  if (widget_pointer)
+    *widget_pointer = NULL;
+
+  if (!solved)
+    award ("puzzle-give-up");
+}
+
+
 GtkWidget *
 do_sliding_puzzle (GtkWidget *do_widget)
 {
@@ -469,7 +492,7 @@ do_sliding_puzzle (GtkWidget *do_widget)
       gtk_window_set_titlebar (GTK_WINDOW (window), header);
       gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
       g_signal_connect (window, "destroy",
-                        G_CALLBACK (gtk_widget_destroyed), &window);
+                        G_CALLBACK (widget_destroyed), &window);
 
       frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio 
(puzzle), FALSE);
       gtk_container_add (GTK_CONTAINER (window), frame);


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