From dd406c80629c8b23eb5391608df5fea93580ea06 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sat, 7 Jan 2017 01:59:23 +0100 Subject: [PATCH] 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(-) create mode 100644 demos/gtk-demo/gtkfishbowl.c create mode 100644 demos/gtk-demo/gtkfishbowl.h diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index acc1f99d23..e3b7ff2b4c 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 a6f8200adb..a02c4843f9 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -96,6 +96,8 @@ fishbowl.ui + gtkfishbowl.c + gtkfishbowl.h gnome-fs-directory.png diff --git a/demos/gtk-demo/fishbowl.c b/demos/gtk-demo/fishbowl.c index 766bcdc595..40c5e2a692 100644 --- a/demos/gtk-demo/fishbowl.c +++ b/demos/gtk-demo/fishbowl.c @@ -7,47 +7,10 @@ #include -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 c8b2a7c413..2a6409175a 100644 --- a/demos/gtk-demo/fishbowl.ui +++ b/demos/gtk-demo/fishbowl.ui @@ -51,8 +51,9 @@ - + True + True diff --git a/demos/gtk-demo/gtkfishbowl.c b/demos/gtk-demo/gtkfishbowl.c new file mode 100644 index 0000000000..54ee7b057c --- /dev/null +++ b/demos/gtk-demo/gtkfishbowl.c @@ -0,0 +1,581 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017 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 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 . + */ + +#include "config.h" + +#include "gtkfishbowl.h" + +#include + +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 0000000000..2ac1ad12ea --- /dev/null +++ b/demos/gtk-demo/gtkfishbowl.h @@ -0,0 +1,58 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2017 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 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 . + */ + +#ifndef __GTK_FISHBOWL_H__ +#define __GTK_FISHBOWL_H__ + +#include + +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__ */