[gtk/gtk-3-22] fishbowl: Port version from GTK 4



commit f5482e6954cb2aca70d14064ec28a3a38d1fd831
Author: Benjamin Otte <otte redhat com>
Date:   Wed Apr 25 23:57:44 2018 +0200

    fishbowl: Port version from GTK 4
    
    This version also merges widgetbowl into fishbowl.

 demos/gtk-demo/Makefile.am        |   1 -
 demos/gtk-demo/demo.gresource.xml |   1 -
 demos/gtk-demo/fishbowl.c         | 307 +++++++++++++++++----------
 demos/gtk-demo/fishbowl.ui        |  68 +++++-
 demos/gtk-demo/gtkfishbowl.c      | 425 +++++++++++++++++++++++++-------------
 demos/gtk-demo/gtkfishbowl.h      |  13 +-
 demos/gtk-demo/widgetbowl.c       | 365 --------------------------------
 7 files changed, 549 insertions(+), 631 deletions(-)
---
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index decba7d0e2..261d555ae5 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -28,7 +28,6 @@ demos_base =                                  \
        expander.c                              \
         filtermodel.c                          \
        fishbowl.c                              \
-       widgetbowl.c                            \
        foreigndrawing.c                        \
        gestures.c                              \
        glarea.c                                \
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 7ab207c692..a02c4843f9 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -156,7 +156,6 @@
     <file>expander.c</file>
     <file>filtermodel.c</file>
     <file>fishbowl.c</file>
-    <file>widgetbowl.c</file>
     <file>flowbox.c</file>
     <file>foreigndrawing.c</file>
     <file>font_features.c</file>
diff --git a/demos/gtk-demo/fishbowl.c b/demos/gtk-demo/fishbowl.c
index db49822fc5..549d09d54c 100644
--- a/demos/gtk-demo/fishbowl.c
+++ b/demos/gtk-demo/fishbowl.c
@@ -9,163 +9,254 @@
 
 #include "gtkfishbowl.h"
 
-GtkWidget *allow_changes;
+const char *const css =
+".blurred-button {"
+"  box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);"
+"}"
+"";
 
-#define N_STATS 5
+char **icon_names = NULL;
+gsize n_icon_names = 0;
 
-#define STATS_UPDATE_TIME G_USEC_PER_SEC
+static void
+init_icon_names (GtkIconTheme *theme)
+{
+  GPtrArray *icons;
+  GList *l, *icon_list;
 
-typedef struct _Stats Stats;
-struct _Stats {
-  gint64 last_stats;
-  gint64 last_frame;
-  gint last_suggestion;
-  guint frame_counter_max;
+  if (icon_names)
+    return;
 
-  guint stats_index;
-  guint frame_counter[N_STATS];
-  guint item_counter[N_STATS];
-};
+  icon_list = gtk_icon_theme_list_icons (theme, NULL);
+  icons = g_ptr_array_new ();
+
+  for (l = icon_list; l; l = l->next)
+    {
+      if (g_str_has_suffix (l->data, "symbolic"))
+        continue;
+
+      g_ptr_array_add (icons, g_strdup (l->data));
+    }
+
+  n_icon_names = icons->len;
+  g_ptr_array_add (icons, NULL); /* NULL-terminate the array */
+  icon_names = (char **) g_ptr_array_free (icons, FALSE);
+
+  /* don't free strings, we assigned them to the array */
+  g_list_free_full (icon_list, g_free);
+}
 
-static Stats *
-get_stats (GtkWidget *widget)
+static const char *
+get_random_icon_name (GtkIconTheme *theme)
 {
-  static GQuark stats_quark = 0;
-  Stats *stats;
+  init_icon_names (theme);
 
-  if (G_UNLIKELY (stats_quark == 0))
-    stats_quark = g_quark_from_static_string ("stats");
+  return icon_names[g_random_int_range(0, n_icon_names)];
+}
 
-  stats = g_object_get_qdata (G_OBJECT (widget), stats_quark);
-  if (stats == NULL)
-    {
-      stats = g_new0 (Stats, 1);
-      g_object_set_qdata_full (G_OBJECT (widget), stats_quark, stats, g_free);
-      stats->last_frame = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
-      stats->last_stats = stats->last_frame;
-    }
+GtkWidget *
+create_icon (void)
+{
+  GtkWidget *image;
 
-  return stats;
+  image = gtk_image_new_from_icon_name (get_random_icon_name (gtk_icon_theme_get_default ()), 
GTK_ICON_SIZE_DND);
+
+  return image;
 }
 
-static void
-do_stats (GtkWidget *widget,
-          GtkWidget *info_label,
-          gint      *suggested_change)
+static GtkWidget *
+create_button (void)
 {
-  Stats *stats;
-  gint64 frame_time;
+  return gtk_button_new_with_label ("Button");
+}
 
-  stats = get_stats (widget);
-  frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
+static GtkWidget *
+create_blurred_button (void)
+{
+  GtkWidget *w = gtk_button_new ();
 
-  if (stats->last_stats + STATS_UPDATE_TIME < frame_time)
-    {
-      char *new_label;
-      guint i, n_frames;
-
-      n_frames = 0;
-      for (i = 0; i < N_STATS; i++)
-        {
-          n_frames += stats->frame_counter[i];
-        }
-      
-      new_label = g_strdup_printf ("icons - %.1f fps",
-                                   (double) G_USEC_PER_SEC * n_frames
-                                       / (N_STATS * STATS_UPDATE_TIME));
-      gtk_label_set_label (GTK_LABEL (info_label), new_label);
-      g_free (new_label);
-
-      if (stats->frame_counter[stats->stats_index] >= 19 * stats->frame_counter_max / 20)
-        {
-          if (stats->last_suggestion > 0)
-            stats->last_suggestion *= 2;
-          else
-            stats->last_suggestion = 1;
-        }
-      else
-        {
-          if (stats->last_suggestion < 0)
-            stats->last_suggestion--;
-          else
-            stats->last_suggestion = -1;
-          stats->last_suggestion = MAX (stats->last_suggestion, 1 - (int) 
stats->item_counter[stats->stats_index]);
-        }
-
-      stats->stats_index = (stats->stats_index + 1) % N_STATS;
-      stats->frame_counter[stats->stats_index] = 0;
-      stats->item_counter[stats->stats_index] = stats->item_counter[(stats->stats_index + N_STATS - 1) % 
N_STATS];
-      stats->last_stats = frame_time;
-      
-      if (suggested_change)
-        *suggested_change = stats->last_suggestion;
-      else
-        stats->last_suggestion = 0;
-    }
-  else
-    {
-      if (suggested_change)
-        *suggested_change = 0;
-    }
+  gtk_style_context_add_class (gtk_widget_get_style_context (w), "blurred-button");
+
+  return w;
+}
+
+static GtkWidget *
+create_font_button (void)
+{
+  return gtk_font_button_new ();
+}
+
+static GtkWidget *
+create_level_bar (void)
+{
+  GtkWidget *w = gtk_level_bar_new_for_interval (0, 100);
+
+  gtk_level_bar_set_value (GTK_LEVEL_BAR (w), 50);
+
+  /* Force them to be a bit larger */
+  gtk_widget_set_size_request (w, 200, -1);
 
-  stats->last_frame = frame_time;
-  stats->frame_counter[stats->stats_index]++;
-  stats->frame_counter_max = MAX (stats->frame_counter_max, stats->frame_counter[stats->stats_index]);
+  return w;
 }
 
+static GtkWidget *
+create_spinner (void)
+{
+  GtkWidget *w = gtk_spinner_new ();
+
+  gtk_spinner_start (GTK_SPINNER (w));
+
+  return w;
+}
+
+static GtkWidget *
+create_spinbutton (void)
+{
+  GtkWidget *w = gtk_spin_button_new_with_range (0, 10, 1);
+
+  return w;
+}
+
+static GtkWidget *
+create_label (void)
+{
+  GtkWidget *w = gtk_label_new ("pLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy 
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.");
+
+  gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
+  gtk_label_set_max_width_chars (GTK_LABEL (w), 100);
+
+  return w;
+}
+
+#if 0
+static GtkWidget *
+create_gears (void)
+{
+  GtkWidget *w = gtk_gears_new ();
+
+  gtk_widget_set_size_request (w, 100, 100);
+
+  return w;
+}
+#endif
+
+static GtkWidget *
+create_switch (void)
+{
+  GtkWidget *w = gtk_switch_new ();
+
+  gtk_switch_set_state (GTK_SWITCH (w), TRUE);
+
+  return w;
+}
+
+static const struct {
+  const char *name;
+  GtkWidget * (*create_func) (void);
+} widget_types[] = {
+  { "Icon",       create_icon           },
+  { "Button",     create_button         },
+  { "Blurbutton", create_blurred_button },
+  { "Fontbutton", create_font_button    },
+  { "Levelbar",   create_level_bar      },
+  { "Label",      create_label          },
+  { "Spinner",    create_spinner        },
+  { "Spinbutton", create_spinbutton     },
+ // { "Gears",      create_gears          },
+  { "Switch",     create_switch         },
+};
+
+static int selected_widget_type = -1;
+static const int N_WIDGET_TYPES = G_N_ELEMENTS (widget_types);
+
 static void
-stats_update (GtkWidget *widget)
+set_widget_type (GtkFishbowl *fishbowl,
+                 int          widget_type_index)
+{
+  GtkWidget *window, *headerbar;
+
+  if (widget_type_index == selected_widget_type)
+    return;
+
+  selected_widget_type = widget_type_index;
+
+  gtk_fishbowl_set_creation_func (fishbowl,
+                                  widget_types[selected_widget_type].create_func);
+
+  window = gtk_widget_get_toplevel (GTK_WIDGET (fishbowl));
+  headerbar = gtk_window_get_titlebar (GTK_WINDOW (window));
+  gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar),
+                            widget_types[selected_widget_type].name);
+}
+
+void
+next_button_clicked_cb (GtkButton *source,
+                        gpointer   user_data)
 {
-  Stats *stats;
+  GtkFishbowl *fishbowl = user_data;
+  int new_index;
 
-  stats = get_stats (widget);
+  if (selected_widget_type + 1 >= N_WIDGET_TYPES)
+    new_index = 0;
+  else
+    new_index = selected_widget_type + 1;
 
-  stats->item_counter[stats->stats_index] = gtk_fishbowl_get_count (GTK_FISHBOWL (widget));
+  set_widget_type (fishbowl, new_index);
 }
 
-static gboolean
-move_fish (GtkWidget     *bowl,
-           GdkFrameClock *frame_clock,
-           gpointer       info_label)
+void
+prev_button_clicked_cb (GtkButton *source,
+                        gpointer   user_data)
 {
-  gint suggested_change = 0;
-  
-  do_stats (bowl,
-            info_label,
-            !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (allow_changes)) ? &suggested_change : NULL);
+  GtkFishbowl *fishbowl = user_data;
+  int new_index;
 
-  gtk_fishbowl_set_count (GTK_FISHBOWL (bowl),
-                          gtk_fishbowl_get_count (GTK_FISHBOWL (bowl)) + suggested_change);
-  stats_update (bowl);
+  if (selected_widget_type - 1 < 0)
+    new_index = N_WIDGET_TYPES - 1;
+  else
+    new_index = selected_widget_type - 1;
 
-  return G_SOURCE_CONTINUE;
+  set_widget_type (fishbowl, new_index);
 }
 
+
 GtkWidget *
 do_fishbowl (GtkWidget *do_widget)
 {
   static GtkWidget *window = NULL;
+  static GtkCssProvider *provider = NULL;
+
+  if (provider == NULL)
+    {
+      provider = gtk_css_provider_new ();
+      gtk_css_provider_load_from_data (provider, css, -1, NULL);
+      gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+                                                 GTK_STYLE_PROVIDER (provider),
+                                                 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+    }
 
   if (!window)
     {
       GtkBuilder *builder;
-      GtkWidget *bowl, *info_label;
+      GtkWidget *bowl;
 
       g_type_ensure (GTK_TYPE_FISHBOWL);
 
       builder = gtk_builder_new_from_resource ("/fishbowl/fishbowl.ui");
+      gtk_builder_add_callback_symbols (builder,
+                                        "next_button_clicked_cb", G_CALLBACK (next_button_clicked_cb),
+                                        "prev_button_clicked_cb", G_CALLBACK (prev_button_clicked_cb),
+                                        NULL);
       gtk_builder_connect_signals (builder, NULL);
       window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
       bowl = GTK_WIDGET (gtk_builder_get_object (builder, "bowl"));
-      info_label = GTK_WIDGET (gtk_builder_get_object (builder, "info_label"));
-      allow_changes = GTK_WIDGET (gtk_builder_get_object (builder, "changes_allow"));
+      set_widget_type (GTK_FISHBOWL (bowl), 0);
       gtk_window_set_screen (GTK_WINDOW (window),
                              gtk_widget_get_screen (do_widget));
       g_signal_connect (window, "destroy",
                         G_CALLBACK (gtk_widget_destroyed), &window);
 
       gtk_widget_realize (window);
-      gtk_widget_add_tick_callback (bowl, move_fish, info_label, NULL);
     }
 
   if (!gtk_widget_get_visible (window))
diff --git a/demos/gtk-demo/fishbowl.ui b/demos/gtk-demo/fishbowl.ui
index 403a03bc88..6f1f985aea 100644
--- a/demos/gtk-demo/fishbowl.ui
+++ b/demos/gtk-demo/fishbowl.ui
@@ -8,27 +8,76 @@
         <property name="visible">True</property>
         <property name="show-close-button">True</property>
         <child>
-          <object class="GtkLabel" id="info_label">
+          <object class="GtkBox">
             <property name="visible">True</property>
-            <property name="label">icons - 0 fps</property>
+            <style>
+              <class name="linked"/>
+            </style>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon-name">pan-start-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+                <signal name="clicked" handler="prev_button_clicked_cb" object="bowl" swapped="no"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="icon-name">pan-end-symbolic</property>
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+                <signal name="clicked" handler="next_button_clicked_cb" object="bowl" swapped="no"/>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="label">fps</property>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="label" bind-source="bowl" bind-property="framerate"/>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="label">Icons, </property>
           </object>
           <packing>
-            <property name="pack_type">end</property>
+            <property name="pack-type">end</property>
           </packing>
         </child>
         <child>
           <object class="GtkLabel">
             <property name="visible">True</property>
-            <property name="label" bind-source="bowl" bind-property="count">0</property>
+            <property name="label" bind-source="bowl" bind-property="count"/>
           </object>
           <packing>
-            <property name="pack_type">end</property>
+            <property name="pack-type">end</property>
           </packing>
         </child>
         <child>
           <object class="GtkToggleButton" id="changes_allow">
-            <property name="active">False</property>
-            <property name="visible" bind-source="changes_allow" bind-property="active" 
bind-flags="invert-boolean">True</property>
+            <property name="visible" bind-source="changes_allow" bind-property="active" 
bind-flags="invert-boolean"/>
             <property name="relief">none</property>
             <child>
               <object class="GtkImage">
@@ -38,7 +87,7 @@
             </child>
           </object>
           <packing>
-            <property name="pack_type">end</property>
+            <property name="pack-type">end</property>
           </packing>
         </child>
         <child>
@@ -54,7 +103,7 @@
             </child>
           </object>
           <packing>
-            <property name="pack_type">end</property>
+            <property name="pack-type">end</property>
           </packing>
         </child>
       </object>
@@ -63,6 +112,7 @@
       <object class="GtkFishbowl" id="bowl">
         <property name="visible">True</property>
         <property name="animating">True</property>
+        <property name="benchmark" bind-source="changes_allow" bind-property="active" 
bind-flags="invert-boolean">True</property>
       </object>
     </child>
   </object>
diff --git a/demos/gtk-demo/gtkfishbowl.c b/demos/gtk-demo/gtkfishbowl.c
index 4f2a2d571f..5101210fd1 100644
--- a/demos/gtk-demo/gtkfishbowl.c
+++ b/demos/gtk-demo/gtkfishbowl.c
@@ -19,18 +19,25 @@
 
 #include "gtkfishbowl.h"
 
-#include "gtk/fallback-c89.c"
+#include <math.h>
 
 typedef struct _GtkFishbowlPrivate       GtkFishbowlPrivate;
 typedef struct _GtkFishbowlChild         GtkFishbowlChild;
 
 struct _GtkFishbowlPrivate
 {
+  GtkFishCreationFunc creation_func;
   GList *children;
   guint count;
 
   gint64 last_frame_time;
+  gint64 update_delay;
   guint tick_id;
+
+  double framerate;
+  int last_benchmark_change;
+
+  guint benchmark : 1;
 };
 
 struct _GtkFishbowlChild
@@ -45,7 +52,10 @@ struct _GtkFishbowlChild
 enum {
    PROP_0,
    PROP_ANIMATING,
+   PROP_BENCHMARK,
    PROP_COUNT,
+   PROP_FRAMERATE,
+   PROP_UPDATE_DELAY,
    NUM_PROPERTIES
 };
 
@@ -56,7 +66,11 @@ G_DEFINE_TYPE_WITH_PRIVATE (GtkFishbowl, gtk_fishbowl, GTK_TYPE_CONTAINER)
 static void
 gtk_fishbowl_init (GtkFishbowl *fishbowl)
 {
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
   gtk_widget_set_has_window (GTK_WIDGET (fishbowl), FALSE);
+
+  priv->update_delay = G_USEC_PER_SEC;
 }
 
 /**
@@ -73,48 +87,9 @@ gtk_fishbowl_new (void)
 }
 
 static void
-gtk_widget_measure (GtkWidget      *widget,
-                    GtkOrientation  orientation,
-                    gint            size,
-                    gint           *minimum,
-                    gint           *natural,
-                    gint           *minimum_baseline,
-                    gint           *natural_baseline)
-{
-  g_return_if_fail (GTK_IS_WIDGET (widget));
-  g_return_if_fail (size >= -1);
-
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      if (size < 0)
-        gtk_widget_get_preferred_width (widget, minimum, natural);
-      else
-        gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
-
-      if (minimum_baseline)
-        *minimum_baseline = -1;
-      if (natural_baseline)
-        *natural_baseline = -1;
-    }
-  else
-    {
-      gtk_widget_get_preferred_height_and_baseline_for_width (widget,
-                                                              size,
-                                                              minimum,
-                                                              natural,
-                                                              minimum_baseline,
-                                                              natural_baseline);
-    }
-}
-
-static void
-gtk_fishbowl_measure (GtkWidget      *widget,
-                      GtkOrientation  orientation,
-                      int             for_size,
-                      int            *minimum,
-                      int            *natural,
-                      int            *minimum_baseline,
-                      int            *natural_baseline)
+gtk_fishbowl_get_preferred_width (GtkWidget *widget,
+                                  int       *minimum,
+                                  int       *natural)
 {
   GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
   GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
@@ -132,47 +107,42 @@ gtk_fishbowl_measure (GtkWidget      *widget,
       if (!gtk_widget_get_visible (child->widget))
         continue;
 
-      gtk_widget_measure (child->widget, orientation, -1, &child_min, &child_nat, NULL, NULL);
+      gtk_widget_get_preferred_width (child->widget, &child_min, &child_nat);
 
       *minimum = MAX (*minimum, child_min);
       *natural = MAX (*natural, child_nat);
     }
 }
 
-static void
-gtk_fishbowl_get_preferred_width (GtkWidget *widget,
-                                  int       *minimum,
-                                  int       *natural)
-{
-  gtk_fishbowl_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural, NULL, NULL);
-}
-
 static void
 gtk_fishbowl_get_preferred_height (GtkWidget *widget,
                                    int       *minimum,
                                    int       *natural)
 {
-  gtk_fishbowl_measure (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural, NULL, NULL);
-}
+  GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+  GtkFishbowlChild *child;
+  GList *children;
+  gint child_min, child_nat;
 
-static void
-gtk_fishbowl_get_preferred_width_for_height (GtkWidget *widget,
-                                             int        for_size,
-                                             int       *minimum,
-                                             int       *natural)
-{
-  gtk_fishbowl_measure (widget, GTK_ORIENTATION_HORIZONTAL, for_size, minimum, natural, NULL, NULL);
-}
+  *minimum = 0;
+  *natural = 0;
 
-static void
-gtk_fishbowl_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
-                                                          int        for_size,
-                                                          int       *minimum,
-                                                          int       *natural,
-                                                          int       *minimum_baseline,
-                                                          int       *natural_baseline)
-{
-  gtk_fishbowl_measure (widget, GTK_ORIENTATION_VERTICAL, for_size, minimum, natural, minimum_baseline, 
natural_baseline);
+  for (children = priv->children; children; children = children->next)
+    {
+      int min_width;
+
+      child = children->data;
+
+      if (!gtk_widget_get_visible (child->widget))
+        continue;
+
+      gtk_widget_get_preferred_width (child->widget, &min_width, NULL);
+      gtk_widget_get_preferred_height_for_width (child->widget, min_width, &child_min, &child_nat);
+
+      *minimum = MAX (*minimum, child_min);
+      *natural = MAX (*natural, child_nat);
+    }
 }
 
 static void
@@ -186,8 +156,6 @@ gtk_fishbowl_size_allocate (GtkWidget     *widget,
   GtkRequisition child_requisition;
   GList *children;
 
-  gtk_widget_set_allocation (widget, allocation);
-
   for (children = priv->children; children; children = children->next)
     {
       child = children->data;
@@ -244,7 +212,7 @@ gtk_fishbowl_remove (GtkContainer *container,
   GtkFishbowl *fishbowl = GTK_FISHBOWL (container);
   GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
   GtkFishbowlChild *child;
-  GtkWidget *widget_container = GTK_WIDGET (container);
+  GtkWidget *widget_bowl = GTK_WIDGET (fishbowl);
   GList *children;
 
   for (children = priv->children; children; children = children->next)
@@ -261,8 +229,8 @@ gtk_fishbowl_remove (GtkContainer *container,
           g_list_free (children);
           g_free (child);
 
-          if (was_visible && gtk_widget_get_visible (widget_container))
-            gtk_widget_queue_resize (widget_container);
+          if (was_visible && gtk_widget_get_visible (widget_bowl))
+            gtk_widget_queue_resize (widget_bowl);
 
           priv->count--;
           g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_COUNT]);
@@ -271,6 +239,7 @@ gtk_fishbowl_remove (GtkContainer *container,
     }
 }
 
+
 static void
 gtk_fishbowl_forall (GtkContainer *container,
                      gboolean      include_internals,
@@ -295,29 +264,6 @@ gtk_fishbowl_forall (GtkContainer *container,
     }
 }
 
-static gboolean
-gtk_fishbowl_draw (GtkWidget *widget,
-                   cairo_t   *cr)
-{
-  GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
-  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
-  GtkFishbowlChild *child;
-  GList *list;
-
-  for (list = priv->children;
-       list;
-       list = list->next)
-    {
-      child = list->data;
-
-      gtk_container_propagate_draw (GTK_CONTAINER (fishbowl),
-                                    child->widget,
-                                    cr);
-    }
-
-  return FALSE;
-}
-
 static void
 gtk_fishbowl_dispose (GObject *object)
 {
@@ -343,10 +289,18 @@ gtk_fishbowl_set_property (GObject         *object,
       gtk_fishbowl_set_animating (fishbowl, g_value_get_boolean (value));
       break;
 
+    case PROP_BENCHMARK:
+      gtk_fishbowl_set_benchmark (fishbowl, g_value_get_boolean (value));
+      break;
+
     case PROP_COUNT:
       gtk_fishbowl_set_count (fishbowl, g_value_get_uint (value));
       break;
 
+    case PROP_UPDATE_DELAY:
+      gtk_fishbowl_set_update_delay (fishbowl, g_value_get_int64 (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -367,10 +321,22 @@ gtk_fishbowl_get_property (GObject         *object,
       g_value_set_boolean (value, gtk_fishbowl_get_animating (fishbowl));
       break;
 
+    case PROP_BENCHMARK:
+      g_value_set_boolean (value, gtk_fishbowl_get_benchmark (fishbowl));
+      break;
+
     case PROP_COUNT:
       g_value_set_uint (value, gtk_fishbowl_get_count (fishbowl));
       break;
 
+    case PROP_FRAMERATE:
+      g_value_set_double (value, gtk_fishbowl_get_framerate (fishbowl));
+      break;
+
+    case PROP_UPDATE_DELAY:
+      g_value_set_int64 (value, gtk_fishbowl_get_update_delay (fishbowl));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -390,10 +356,7 @@ gtk_fishbowl_class_init (GtkFishbowlClass *klass)
 
   widget_class->get_preferred_width = gtk_fishbowl_get_preferred_width;
   widget_class->get_preferred_height = gtk_fishbowl_get_preferred_height;
-  widget_class->get_preferred_width_for_height = gtk_fishbowl_get_preferred_width_for_height;
-  widget_class->get_preferred_height_and_baseline_for_width = 
gtk_fishbowl_get_preferred_height_and_baseline_for_width;
   widget_class->size_allocate = gtk_fishbowl_size_allocate;
-  widget_class->draw = gtk_fishbowl_draw;
 
   container_class->add = gtk_fishbowl_add;
   container_class->remove = gtk_fishbowl_remove;
@@ -406,13 +369,36 @@ gtk_fishbowl_class_init (GtkFishbowlClass *klass)
                             FALSE,
                             G_PARAM_READWRITE);
 
+  props[PROP_BENCHMARK] =
+      g_param_spec_boolean ("benchmark",
+                            "Benchmark",
+                            "Adapt the count property to hit the maximum framerate",
+                            FALSE,
+                            G_PARAM_READWRITE);
+
   props[PROP_COUNT] =
       g_param_spec_uint ("count",
                          "Count",
                          "Number of widgets",
                          0, G_MAXUINT,
                          0,
-                         G_PARAM_READABLE);
+                         G_PARAM_READWRITE);
+
+  props[PROP_FRAMERATE] =
+      g_param_spec_double ("framerate",
+                           "Framerate",
+                           "Framerate of this widget in frames per second",
+                           0, G_MAXDOUBLE,
+                           0,
+                           G_PARAM_READABLE);
+
+  props[PROP_UPDATE_DELAY] =
+      g_param_spec_int64 ("update-delay",
+                          "Update delay",
+                          "Number of usecs between updates",
+                          0, G_MAXINT64,
+                          G_USEC_PER_SEC,
+                          G_PARAM_READWRITE);
 
   g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
 }
@@ -425,70 +411,58 @@ gtk_fishbowl_get_count (GtkFishbowl *fishbowl)
   return priv->count;
 }
 
-char **icon_names = NULL;
-gsize n_icon_names = 0;
-
-static void
-init_icon_names (GtkIconTheme *theme)
+void
+gtk_fishbowl_set_count (GtkFishbowl *fishbowl,
+                        guint        count)
 {
-  GPtrArray *icons;
-  GList *l, *icon_list;
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
 
-  if (icon_names)
+  if (priv->count == count)
     return;
 
-  icon_list = gtk_icon_theme_list_icons (theme, NULL);
-  icons = g_ptr_array_new ();
+  g_object_freeze_notify (G_OBJECT (fishbowl));
 
-  for (l = icon_list; l; l = l->next)
+  while (priv->count > count)
     {
-      if (g_str_has_suffix (l->data, "symbolic"))
-        continue;
-
-      g_ptr_array_add (icons, g_strdup (l->data));
+      gtk_fishbowl_remove (GTK_CONTAINER (fishbowl), ((GtkFishbowlChild *) priv->children->data)->widget);
     }
 
-  n_icon_names = icons->len;
-  g_ptr_array_add (icons, NULL); /* NULL-terminate the array */
-  icon_names = (char **) g_ptr_array_free (icons, FALSE);
+  while (priv->count < count)
+    {
+      GtkWidget *new_widget;
 
-  /* don't free strings, we assigned them to the array */
-  g_list_free_full (icon_list, g_free);
+      new_widget = priv->creation_func ();
+
+      gtk_widget_show (new_widget);
+
+      gtk_fishbowl_add (GTK_CONTAINER (fishbowl), new_widget);
+    }
+
+  g_object_thaw_notify (G_OBJECT (fishbowl));
 }
 
-static const char *
-get_random_icon_name (GtkIconTheme *theme)
+gboolean
+gtk_fishbowl_get_benchmark (GtkFishbowl *fishbowl)
 {
-  init_icon_names (theme);
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
 
-  return icon_names[g_random_int_range(0, n_icon_names)];
+  return priv->benchmark;
 }
 
 void
-gtk_fishbowl_set_count (GtkFishbowl *fishbowl,
-                        guint        count)
+gtk_fishbowl_set_benchmark (GtkFishbowl *fishbowl,
+                            gboolean     benchmark)
 {
   GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
 
-  g_object_freeze_notify (G_OBJECT (fishbowl));
-
-  while (priv->count > count)
-    {
-      gtk_container_remove (GTK_CONTAINER (fishbowl),
-                            ((GtkFishbowlChild *) priv->children->data)->widget);
-    }
+  if (priv->benchmark == benchmark)
+    return;
 
-  while (priv->count < count)
-    {
-      GtkWidget *new_widget;
-        
-      new_widget = gtk_image_new_from_icon_name (get_random_icon_name (gtk_icon_theme_get_default ()),
-                                                 GTK_ICON_SIZE_DIALOG);
-      gtk_widget_show (new_widget);
-      gtk_container_add (GTK_CONTAINER (fishbowl), new_widget);
-    }
+  priv->benchmark = benchmark;
+  if (!benchmark)
+    priv->last_benchmark_change = 0;
 
-  g_object_thaw_notify (G_OBJECT (fishbowl));
+  g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_BENCHMARK]);
 }
 
 gboolean
@@ -499,6 +473,111 @@ gtk_fishbowl_get_animating (GtkFishbowl *fishbowl)
   return priv->tick_id != 0;
 }
 
+static gint64
+guess_refresh_interval (GdkFrameClock *frame_clock)
+{
+  gint64 interval;
+  gint64 i;
+
+  interval = G_MAXINT64;
+
+  for (i = gdk_frame_clock_get_history_start (frame_clock);
+       i < gdk_frame_clock_get_frame_counter (frame_clock);
+       i++)
+    {
+      GdkFrameTimings *t, *before;
+      gint64 ts, before_ts;
+
+      t = gdk_frame_clock_get_timings (frame_clock, i);
+      before = gdk_frame_clock_get_timings (frame_clock, i - 1);
+      if (t == NULL || before == NULL)
+        continue;
+
+      ts = gdk_frame_timings_get_frame_time (t);
+      before_ts = gdk_frame_timings_get_frame_time (before);
+      if (ts == 0 || before_ts == 0)
+        continue;
+
+      interval = MIN (interval, ts - before_ts);
+    }
+
+  if (interval == G_MAXINT64)
+    return 0;
+
+  return interval;
+}
+
+static void
+gtk_fishbowl_do_update (GtkFishbowl *fishbowl)
+{
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+  GdkFrameClock *frame_clock;
+  GdkFrameTimings *start, *end;
+  gint64 start_counter, end_counter;
+  gint64 n_frames, expected_frames;
+  gint64 start_timestamp, end_timestamp;
+  gint64 interval;
+
+  frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (fishbowl));
+  if (frame_clock == NULL)
+    return;
+
+  start_counter = gdk_frame_clock_get_history_start (frame_clock);
+  end_counter = gdk_frame_clock_get_frame_counter (frame_clock);
+  start = gdk_frame_clock_get_timings (frame_clock, start_counter);
+  for (end = gdk_frame_clock_get_timings (frame_clock, end_counter);
+       end_counter > start_counter && end != NULL && !gdk_frame_timings_get_complete (end);
+       end = gdk_frame_clock_get_timings (frame_clock, end_counter))
+    end_counter--;
+  if (end_counter - start_counter < 4)
+    return;
+
+  start_timestamp = gdk_frame_timings_get_presentation_time (start);
+  end_timestamp = gdk_frame_timings_get_presentation_time (end);
+  if (start_timestamp == 0 || end_timestamp == 0)
+    {
+      start_timestamp = gdk_frame_timings_get_frame_time (start);
+      end_timestamp = gdk_frame_timings_get_frame_time (end);
+    }
+
+  n_frames = end_counter - start_counter;
+  priv->framerate = ((double) n_frames) * G_USEC_PER_SEC / (end_timestamp - start_timestamp);
+  g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE]);
+
+  if (!priv->benchmark)
+    return;
+
+  interval = gdk_frame_timings_get_refresh_interval (end);
+  if (interval == 0)
+    {
+      interval = guess_refresh_interval (frame_clock);
+      if (interval == 0)
+        return;
+    }
+  expected_frames = round ((double) (end_timestamp - start_timestamp) / interval);
+
+  if (n_frames >= expected_frames)
+    {
+      if (priv->last_benchmark_change > 0)
+        priv->last_benchmark_change *= 2;
+      else
+        priv->last_benchmark_change = 1;
+    }
+  else if (n_frames + 1 < expected_frames)
+    {
+      if (priv->last_benchmark_change < 0)
+        priv->last_benchmark_change--;
+      else
+        priv->last_benchmark_change = -1;
+    }
+  else
+    {
+      priv->last_benchmark_change = 0;
+    }
+
+  gtk_fishbowl_set_count (fishbowl, MAX (1, (int) priv->count + priv->last_benchmark_change));
+}
+
 static gboolean
 gtk_fishbowl_tick (GtkWidget     *widget,
                    GdkFrameClock *frame_clock,
@@ -509,9 +588,11 @@ gtk_fishbowl_tick (GtkWidget     *widget,
   GtkFishbowlChild *child;
   GList *l;
   gint64 frame_time, elapsed;
+  gboolean do_update;
 
   frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
   elapsed = frame_time - priv->last_frame_time;
+  do_update = frame_time / priv->update_delay != priv->last_frame_time / priv->update_delay;
   priv->last_frame_time = frame_time;
 
   /* last frame was 0, so we're just starting to animate */
@@ -550,6 +631,9 @@ gtk_fishbowl_tick (GtkWidget     *widget,
 
   gtk_widget_queue_allocate (widget);
 
+  if (do_update)
+    gtk_fishbowl_do_update (fishbowl);
+
   return G_SOURCE_CONTINUE;
 }
 
@@ -574,8 +658,57 @@ gtk_fishbowl_set_animating (GtkFishbowl *fishbowl,
       priv->last_frame_time = 0;
       gtk_widget_remove_tick_callback (GTK_WIDGET (fishbowl), priv->tick_id);
       priv->tick_id = 0;
+      priv->framerate = 0;
+      g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_FRAMERATE]);
     }
 
   g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_ANIMATING]);
 }
 
+double
+gtk_fishbowl_get_framerate (GtkFishbowl *fishbowl)
+{
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+  return priv->framerate;
+}
+
+gint64
+gtk_fishbowl_get_update_delay (GtkFishbowl *fishbowl)
+{
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+  return priv->update_delay;
+}
+
+void
+gtk_fishbowl_set_update_delay (GtkFishbowl *fishbowl,
+                               gint64       update_delay)
+{
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+  if (priv->update_delay == update_delay)
+    return;
+
+  priv->update_delay = update_delay;
+
+  g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_UPDATE_DELAY]);
+}
+
+void
+gtk_fishbowl_set_creation_func (GtkFishbowl         *fishbowl,
+                                GtkFishCreationFunc  creation_func)
+{
+  GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+  g_object_freeze_notify (G_OBJECT (fishbowl));
+
+  gtk_fishbowl_set_count (fishbowl, 0);
+  priv->last_benchmark_change = 0;
+
+  priv->creation_func = creation_func;
+
+  gtk_fishbowl_set_count (fishbowl, 1);
+
+  g_object_thaw_notify (G_OBJECT (fishbowl));
+}
diff --git a/demos/gtk-demo/gtkfishbowl.h b/demos/gtk-demo/gtkfishbowl.h
index 2ac1ad12ea..47441927d0 100644
--- a/demos/gtk-demo/gtkfishbowl.h
+++ b/demos/gtk-demo/gtkfishbowl.h
@@ -32,9 +32,11 @@ G_BEGIN_DECLS
 typedef struct _GtkFishbowl              GtkFishbowl;
 typedef struct _GtkFishbowlClass         GtkFishbowlClass;
 
+typedef GtkWidget * (* GtkFishCreationFunc) (void);
+
 struct _GtkFishbowl
 {
-  GtkContainer container;
+  GtkContainer parent;
 };
 
 struct _GtkFishbowlClass
@@ -52,6 +54,15 @@ void       gtk_fishbowl_set_count         (GtkFishbowl       *fishbowl,
 gboolean   gtk_fishbowl_get_animating     (GtkFishbowl       *fishbowl);
 void       gtk_fishbowl_set_animating     (GtkFishbowl       *fishbowl,
                                            gboolean           animating);
+gboolean   gtk_fishbowl_get_benchmark     (GtkFishbowl       *fishbowl);
+void       gtk_fishbowl_set_benchmark     (GtkFishbowl       *fishbowl,
+                                           gboolean           animating);
+double     gtk_fishbowl_get_framerate     (GtkFishbowl       *fishbowl);
+gint64     gtk_fishbowl_get_update_delay  (GtkFishbowl       *fishbowl);
+void       gtk_fishbowl_set_update_delay  (GtkFishbowl       *fishbowl,
+                                           gint64             update_delay);
+void       gtk_fishbowl_set_creation_func (GtkFishbowl       *fishbowl,
+                                           GtkFishCreationFunc creation_func);
 
 G_END_DECLS
 



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