[gtk+/gtk-3-22] gtk-demo: Add GtkFishbowl
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/gtk-3-22] gtk-demo: Add GtkFishbowl
- Date: Sat, 7 Jan 2017 02:44:57 +0000 (UTC)
commit dd406c80629c8b23eb5391608df5fea93580ea06
Author: Benjamin Otte <otte redhat com>
Date: Sat Jan 7 01:59:23 2017 +0100
gtk-demo: Add GtkFishbowl
Avoids usage of GtkFixed where child properties eat up all the CPU time.
And that's kinda not what I want to benchmark.
demos/gtk-demo/Makefile.am | 2 +
demos/gtk-demo/demo.gresource.xml | 2 +
demos/gtk-demo/fishbowl.c | 188 +-----------
demos/gtk-demo/fishbowl.ui | 3 +-
demos/gtk-demo/gtkfishbowl.c | 581 +++++++++++++++++++++++++++++++++++++
demos/gtk-demo/gtkfishbowl.h | 58 ++++
6 files changed, 658 insertions(+), 176 deletions(-)
---
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index acc1f99..e3b7ff2 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -138,6 +138,8 @@ nodist_gtk3_demo_SOURCES = demos.h
gtk3_demo_SOURCES = \
$(demos) \
+ gtkfishbowl.c \
+ gtkfishbowl.h \
demo_resources.c \
main.c
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index a6f8200..a02c484 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -96,6 +96,8 @@
</gresource>
<gresource prefix="/fishbowl">
<file>fishbowl.ui</file>
+ <file>gtkfishbowl.c</file>
+ <file>gtkfishbowl.h</file>
</gresource>
<gresource prefix="/iconview">
<file preprocess="to-pixdata">gnome-fs-directory.png</file>
diff --git a/demos/gtk-demo/fishbowl.c b/demos/gtk-demo/fishbowl.c
index 766bcdc..40c5e2a 100644
--- a/demos/gtk-demo/fishbowl.c
+++ b/demos/gtk-demo/fishbowl.c
@@ -7,47 +7,10 @@
#include <gtk/gtk.h>
-char **icon_names = NULL;
-gsize n_icon_names = 0;
+#include "gtkfishbowl.h"
GtkWidget *allow_changes;
-static void
-init_icon_names (GtkIconTheme *theme)
-{
- GPtrArray *icons;
- GList *l, *icon_list;
-
- if (icon_names)
- return;
-
- 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 const char *
-get_random_icon_name (GtkIconTheme *theme)
-{
- init_icon_names (theme);
-
- return icon_names[g_random_int_range(0, n_icon_names)];
-}
-
#define N_STATS 5
#define STATS_UPDATE_TIME G_USEC_PER_SEC
@@ -85,17 +48,16 @@ get_stats (GtkWidget *widget)
return stats;
}
-static gint64
+static void
do_stats (GtkWidget *widget,
GtkWidget *info_label,
gint *suggested_change)
{
Stats *stats;
- gint64 frame_time, elapsed;
+ gint64 frame_time;
stats = get_stats (widget);
frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
- elapsed = frame_time - stats->last_frame;
if (stats->last_stats + STATS_UPDATE_TIME < frame_time)
{
@@ -150,138 +112,16 @@ do_stats (GtkWidget *widget,
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 elapsed;
}
static void
-stats_update (GtkWidget *widget,
- gint n_items)
+stats_update (GtkWidget *widget)
{
Stats *stats;
stats = get_stats (widget);
- g_assert ((gint) stats->item_counter[stats->stats_index] + n_items > 0);
- stats->item_counter[stats->stats_index] += n_items;
-}
-
-typedef struct _FishData FishData;
-struct _FishData {
- double x;
- double y;
- double x_speed;
- double y_speed;
-};
-
-static FishData *
-get_fish_data (GtkWidget *fish)
-{
- static GQuark fish_quark = 0;
- FishData *data;
-
- if (G_UNLIKELY (fish_quark == 0))
- fish_quark = g_quark_from_static_string ("fish");
-
- data = g_object_get_qdata (G_OBJECT (fish), fish_quark);
- if (data == NULL)
- {
- data = g_new0 (FishData, 1);
- g_object_set_qdata_full (G_OBJECT (fish), fish_quark, data, g_free);
- data->x = 10;
- data->y = 10;
- data->x_speed = g_random_double_range (1, 200);
- data->y_speed = g_random_double_range (1, 200);
- }
-
- return data;
-}
-
-static void
-add_fish (GtkWidget *bowl,
- guint n_fish)
-{
- GtkWidget *new_fish;
- guint i;
-
- for (i = 0; i < n_fish; i++)
- {
- new_fish = gtk_image_new_from_icon_name (get_random_icon_name (gtk_icon_theme_get_default ()),
- GTK_ICON_SIZE_DIALOG);
- gtk_widget_show (new_fish);
-
- gtk_fixed_put (GTK_FIXED (bowl),
- new_fish,
- 10, 10);
- }
-
- stats_update (bowl, n_fish);
-}
-
-static void
-remove_fish (GtkWidget *bowl,
- guint n_fish)
-{
- GList *list, *children;
- guint i;
-
- children = gtk_container_get_children (GTK_CONTAINER (bowl));
- g_assert (n_fish < g_list_length (children));
-
- list = children;
- for (i = 0; i < n_fish; i++)
- {
- gtk_container_remove (GTK_CONTAINER (bowl), list->data);
- list = list->next;
- }
-
- g_list_free (children);
-
- stats_update (bowl, - (gint) n_fish);
-
- {
- Stats *stats = get_stats (bowl);
-
- children = gtk_container_get_children (GTK_CONTAINER (bowl));
- g_assert (stats->item_counter[stats->stats_index] == g_list_length (children));
- g_list_free (children);
- }
-}
-
-static void
-move_one_fish (GtkWidget *fish,
- gpointer elapsedp)
-{
- GtkWidget *fixed = gtk_widget_get_parent (fish);
- FishData *data = get_fish_data (fish);
- gint64 elapsed = *(gint64 *) elapsedp;
-
- data->x += data->x_speed * ((double) elapsed / G_USEC_PER_SEC);
- data->y += data->y_speed * ((double) elapsed / G_USEC_PER_SEC);
-
- if (data->x <= 0)
- {
- data->x = 0;
- data->x_speed = - g_random_double_range (1, 200) * (data->x_speed > 0 ? 1 : -1);
- }
- else if (data->x > gtk_widget_get_allocated_width (fixed) - gtk_widget_get_allocated_width (fish))
- {
- data->x = gtk_widget_get_allocated_width (fixed) - gtk_widget_get_allocated_width (fish);
- data->x_speed = - g_random_double_range (1, 200) * (data->x_speed > 0 ? 1 : -1);
- }
-
- if (data->y <= 0)
- {
- data->y = 0;
- data->y_speed = - g_random_double_range (1, 200) * (data->y_speed > 0 ? 1 : -1);
- }
- else if (data->y > gtk_widget_get_allocated_height (fixed) - gtk_widget_get_allocated_height (fish))
- {
- data->y = gtk_widget_get_allocated_height (fixed) - gtk_widget_get_allocated_height (fish);
- data->y_speed = - g_random_double_range (1, 200) * (data->y_speed > 0 ? 1 : -1);
- }
-
- gtk_fixed_move (GTK_FIXED (fixed), fish, data->x, data->y);
+ stats->item_counter[stats->stats_index] = gtk_fishbowl_get_count (GTK_FISHBOWL (widget));
}
static gboolean
@@ -289,19 +129,15 @@ move_fish (GtkWidget *bowl,
GdkFrameClock *frame_clock,
gpointer info_label)
{
- gint64 elapsed;
gint suggested_change = 0;
- elapsed = do_stats (bowl,
- info_label,
- !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (allow_changes)) ? &suggested_change
: NULL);
+ do_stats (bowl,
+ info_label,
+ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (allow_changes)) ? &suggested_change : NULL);
- gtk_container_foreach (GTK_CONTAINER (bowl), move_one_fish, &elapsed);
-
- if (suggested_change > 0)
- add_fish (bowl, suggested_change);
- else if (suggested_change < 0)
- remove_fish (bowl, - suggested_change);
+ gtk_fishbowl_set_count (GTK_FISHBOWL (bowl),
+ gtk_fishbowl_get_count (GTK_FISHBOWL (bowl)) + suggested_change);
+ stats_update (bowl);
return G_SOURCE_CONTINUE;
}
@@ -316,6 +152,8 @@ do_fishbowl (GtkWidget *do_widget)
GtkBuilder *builder;
GtkWidget *bowl, *info_label;
+ g_type_ensure (GTK_TYPE_FISHBOWL);
+
builder = gtk_builder_new_from_resource ("/fishbowl/fishbowl.ui");
gtk_builder_connect_signals (builder, NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
diff --git a/demos/gtk-demo/fishbowl.ui b/demos/gtk-demo/fishbowl.ui
index c8b2a7c..2a64091 100644
--- a/demos/gtk-demo/fishbowl.ui
+++ b/demos/gtk-demo/fishbowl.ui
@@ -51,8 +51,9 @@
</object>
</child>
<child>
- <object class="GtkFixed" id="bowl">
+ <object class="GtkFishbowl" id="bowl">
<property name="visible">True</property>
+ <property name="animating">True</property>
</object>
</child>
</object>
diff --git a/demos/gtk-demo/gtkfishbowl.c b/demos/gtk-demo/gtkfishbowl.c
new file mode 100644
index 0000000..54ee7b0
--- /dev/null
+++ b/demos/gtk-demo/gtkfishbowl.c
@@ -0,0 +1,581 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 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/>.
+ */
+
+#include "config.h"
+
+#include "gtkfishbowl.h"
+
+#include <math.h>
+
+typedef struct _GtkFishbowlPrivate GtkFishbowlPrivate;
+typedef struct _GtkFishbowlChild GtkFishbowlChild;
+
+struct _GtkFishbowlPrivate
+{
+ GList *children;
+ guint count;
+
+ gint64 last_frame_time;
+ guint tick_id;
+};
+
+struct _GtkFishbowlChild
+{
+ GtkWidget *widget;
+ double x;
+ double y;
+ double dx;
+ double dy;
+};
+
+enum {
+ PROP_0,
+ PROP_ANIMATING,
+ PROP_COUNT,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkFishbowl, gtk_fishbowl, GTK_TYPE_CONTAINER)
+
+static void
+gtk_fishbowl_init (GtkFishbowl *fishbowl)
+{
+ gtk_widget_set_has_window (GTK_WIDGET (fishbowl), FALSE);
+}
+
+/**
+ * gtk_fishbowl_new:
+ *
+ * Creates a new #GtkFishbowl.
+ *
+ * Returns: a new #GtkFishbowl.
+ */
+GtkWidget*
+gtk_fishbowl_new (void)
+{
+ return g_object_new (GTK_TYPE_FISHBOWL, NULL);
+}
+
+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)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child;
+ GList *children;
+ gint child_min, child_nat;
+
+ *minimum = 0;
+ *natural = 0;
+
+ for (children = priv->children; children; children = children->next)
+ {
+ child = children->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ gtk_widget_measure (child->widget, orientation, -1, &child_min, &child_nat, NULL, NULL);
+
+ *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);
+}
+
+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);
+}
+
+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);
+}
+
+static void
+gtk_fishbowl_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child;
+ GtkAllocation child_allocation;
+ GtkRequisition child_requisition;
+ GList *children;
+
+ gtk_widget_set_allocation (widget, allocation);
+
+ for (children = priv->children; children; children = children->next)
+ {
+ child = children->data;
+
+ if (!gtk_widget_get_visible (child->widget))
+ continue;
+
+ gtk_widget_get_preferred_size (child->widget, &child_requisition, NULL);
+ child_allocation.x = allocation->x + round (child->x * (allocation->width - child_requisition.width));
+ child_allocation.y = allocation->y + round (child->y * (allocation->height -
child_requisition.height));
+ child_allocation.width = child_requisition.width;
+ child_allocation.height = child_requisition.height;
+
+ gtk_widget_size_allocate (child->widget, &child_allocation);
+ }
+}
+
+static double
+new_speed (void)
+{
+ /* 5s to 50s to cross screen seems fair */
+ return g_random_double_range (0.02, 0.2);
+}
+
+static void
+gtk_fishbowl_add (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (container);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child_info;
+
+ g_return_if_fail (GTK_IS_FISHBOWL (fishbowl));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ child_info = g_new0 (GtkFishbowlChild, 1);
+ child_info->widget = widget;
+ child_info->x = 0;
+ child_info->y = 0;
+ child_info->dx = new_speed ();
+ child_info->dy = new_speed ();
+
+ gtk_widget_set_parent (widget, GTK_WIDGET (fishbowl));
+
+ priv->children = g_list_prepend (priv->children, child_info);
+ priv->count++;
+ g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_COUNT]);
+}
+
+static void
+gtk_fishbowl_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (container);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child;
+ GtkWidget *widget_container = GTK_WIDGET (container);
+ GList *children;
+
+ for (children = priv->children; children; children = children->next)
+ {
+ child = children->data;
+
+ if (child->widget == widget)
+ {
+ gboolean was_visible = gtk_widget_get_visible (widget);
+
+ gtk_widget_unparent (widget);
+
+ priv->children = g_list_remove_link (priv->children, children);
+ g_list_free (children);
+ g_free (child);
+
+ if (was_visible && gtk_widget_get_visible (widget_container))
+ gtk_widget_queue_resize (widget_container);
+
+ priv->count--;
+ g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_COUNT]);
+ break;
+ }
+ }
+}
+
+static void
+gtk_fishbowl_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (container);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child;
+ GList *children;
+
+ if (!include_internals)
+ return;
+
+ children = priv->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+
+ (* callback) (child->widget, callback_data);
+ }
+}
+
+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)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (object);
+
+ gtk_fishbowl_set_animating (fishbowl, FALSE);
+ gtk_fishbowl_set_count (fishbowl, 0);
+
+ G_OBJECT_CLASS (gtk_fishbowl_parent_class)->dispose (object);
+}
+
+static void
+gtk_fishbowl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ANIMATING:
+ gtk_fishbowl_set_animating (fishbowl, g_value_get_boolean (value));
+ break;
+
+ case PROP_COUNT:
+ gtk_fishbowl_set_count (fishbowl, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_fishbowl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (object);
+
+ switch (prop_id)
+ {
+ case PROP_ANIMATING:
+ g_value_set_boolean (value, gtk_fishbowl_get_animating (fishbowl));
+ break;
+
+ case PROP_COUNT:
+ g_value_set_uint (value, gtk_fishbowl_get_count (fishbowl));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_fishbowl_class_init (GtkFishbowlClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+ object_class->dispose = gtk_fishbowl_dispose;
+ object_class->set_property = gtk_fishbowl_set_property;
+ object_class->get_property = gtk_fishbowl_get_property;
+
+ 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;
+ container_class->forall = gtk_fishbowl_forall;
+
+ props[PROP_ANIMATING] =
+ g_param_spec_boolean ("animating",
+ "animating",
+ "Whether children are moving around",
+ FALSE,
+ G_PARAM_READWRITE);
+
+ props[PROP_COUNT] =
+ g_param_spec_uint ("count",
+ "Count",
+ "Number of widgets",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READABLE);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
+}
+
+guint
+gtk_fishbowl_get_count (GtkFishbowl *fishbowl)
+{
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+ return priv->count;
+}
+
+char **icon_names = NULL;
+gsize n_icon_names = 0;
+
+static void
+init_icon_names (GtkIconTheme *theme)
+{
+ GPtrArray *icons;
+ GList *l, *icon_list;
+
+ if (icon_names)
+ return;
+
+ 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 const char *
+get_random_icon_name (GtkIconTheme *theme)
+{
+ init_icon_names (theme);
+
+ return icon_names[g_random_int_range(0, n_icon_names)];
+}
+
+void
+gtk_fishbowl_set_count (GtkFishbowl *fishbowl,
+ guint count)
+{
+ 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);
+ }
+
+ 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);
+ }
+
+ g_object_thaw_notify (G_OBJECT (fishbowl));
+}
+
+gboolean
+gtk_fishbowl_get_animating (GtkFishbowl *fishbowl)
+{
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+ return priv->tick_id != 0;
+}
+
+static gboolean
+gtk_fishbowl_tick (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer unused)
+{
+ GtkFishbowl *fishbowl = GTK_FISHBOWL (widget);
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+ GtkFishbowlChild *child;
+ GList *l;
+ gint64 frame_time, elapsed;
+
+ frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
+ elapsed = frame_time - priv->last_frame_time;
+ priv->last_frame_time = frame_time;
+
+ /* last frame was 0, so we're just starting to animate */
+ if (elapsed == frame_time)
+ return G_SOURCE_CONTINUE;
+
+ for (l = priv->children; l; l = l->next)
+ {
+ child = l->data;
+
+ child->x += child->dx * ((double) elapsed / G_USEC_PER_SEC);
+ child->y += child->dy * ((double) elapsed / G_USEC_PER_SEC);
+
+ if (child->x <= 0)
+ {
+ child->x = 0;
+ child->dx = new_speed ();
+ }
+ else if (child->x >= 1)
+ {
+ child->x = 1;
+ child->dx = - new_speed ();
+ }
+
+ if (child->y <= 0)
+ {
+ child->y = 0;
+ child->dy = new_speed ();
+ }
+ else if (child->y >= 1)
+ {
+ child->y = 1;
+ child->dy = - new_speed ();
+ }
+ }
+
+ gtk_widget_queue_allocate (widget);
+
+ return G_SOURCE_CONTINUE;
+}
+
+void
+gtk_fishbowl_set_animating (GtkFishbowl *fishbowl,
+ gboolean animating)
+{
+ GtkFishbowlPrivate *priv = gtk_fishbowl_get_instance_private (fishbowl);
+
+ if (gtk_fishbowl_get_animating (fishbowl) == animating)
+ return;
+
+ if (animating)
+ {
+ priv->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (fishbowl),
+ gtk_fishbowl_tick,
+ NULL,
+ NULL);
+ }
+ else
+ {
+ priv->last_frame_time = 0;
+ gtk_widget_remove_tick_callback (GTK_WIDGET (fishbowl), priv->tick_id);
+ priv->tick_id = 0;
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (fishbowl), props[PROP_ANIMATING]);
+}
+
diff --git a/demos/gtk-demo/gtkfishbowl.h b/demos/gtk-demo/gtkfishbowl.h
new file mode 100644
index 0000000..2ac1ad1
--- /dev/null
+++ b/demos/gtk-demo/gtkfishbowl.h
@@ -0,0 +1,58 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 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/>.
+ */
+
+#ifndef __GTK_FISHBOWL_H__
+#define __GTK_FISHBOWL_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FISHBOWL (gtk_fishbowl_get_type ())
+#define GTK_FISHBOWL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FISHBOWL,
GtkFishbowl))
+#define GTK_FISHBOWL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FISHBOWL,
GtkFishbowlClass))
+#define GTK_IS_FISHBOWL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FISHBOWL))
+#define GTK_IS_FISHBOWL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FISHBOWL))
+#define GTK_FISHBOWL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FISHBOWL,
GtkFishbowlClass))
+
+typedef struct _GtkFishbowl GtkFishbowl;
+typedef struct _GtkFishbowlClass GtkFishbowlClass;
+
+struct _GtkFishbowl
+{
+ GtkContainer container;
+};
+
+struct _GtkFishbowlClass
+{
+ GtkContainerClass parent_class;
+};
+
+GType gtk_fishbowl_get_type (void) G_GNUC_CONST;
+
+GtkWidget* gtk_fishbowl_new (void);
+
+guint gtk_fishbowl_get_count (GtkFishbowl *fishbowl);
+void gtk_fishbowl_set_count (GtkFishbowl *fishbowl,
+ guint count);
+gboolean gtk_fishbowl_get_animating (GtkFishbowl *fishbowl);
+void gtk_fishbowl_set_animating (GtkFishbowl *fishbowl,
+ gboolean animating);
+
+G_END_DECLS
+
+#endif /* __GTK_FISHBOWL_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]