diff options
author | Benjamin Otte <otte@redhat.com> | 2018-04-25 23:57:44 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2018-04-26 00:32:44 +0200 |
commit | f5482e6954cb2aca70d14064ec28a3a38d1fd831 (patch) | |
tree | 83244c73b769c8f1112bbe0ae8d7186ddf8f97fd | |
parent | fff08fa319dff8542a3f3481a5273523bdfd5dd9 (diff) | |
download | gtk+-f5482e6954cb2aca70d14064ec28a3a38d1fd831.tar.gz |
fishbowl: Port version from GTK 4
This version also merges widgetbowl into fishbowl.
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/demo.gresource.xml | 1 | ||||
-rw-r--r-- | demos/gtk-demo/fishbowl.c | 307 | ||||
-rw-r--r-- | demos/gtk-demo/fishbowl.ui | 68 | ||||
-rw-r--r-- | demos/gtk-demo/gtkfishbowl.c | 425 | ||||
-rw-r--r-- | demos/gtk-demo/gtkfishbowl.h | 13 | ||||
-rw-r--r-- | 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,7 +107,7 @@ 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); @@ -140,39 +115,34 @@ gtk_fishbowl_measure (GtkWidget *widget, } 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 diff --git a/demos/gtk-demo/widgetbowl.c b/demos/gtk-demo/widgetbowl.c deleted file mode 100644 index 0b7e5a0ab7..0000000000 --- a/demos/gtk-demo/widgetbowl.c +++ /dev/null @@ -1,365 +0,0 @@ -/* Benchmark/Widgetbowl - * - * This is a version of the Fishbowl demo that instead shows different - * kinds of widgets, which is useful for comparing the rendering performance - * of theme specifics. - */ - -#include <gtk/gtk.h> - -#include "gtkfishbowl.h" - -const char *const css = -".blurred-button {" -" box-shadow: 0px 0px 5px 10px rgba(0, 0, 0, 0.5);" -"}" -""; - -GtkWidget *fishbowl; - -static GtkWidget * -create_button (void) -{ - return gtk_button_new_with_label ("Button"); -} -static GtkWidget * -create_blurred_button (void) -{ - GtkWidget *w = gtk_button_new (); - - 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); - - 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; -} - -static const struct { - const char *name; - GtkWidget * (*create_func) (void); -} widget_types[] = { - { "Button", create_button }, - { "Blurbutton", create_blurred_button }, - { "Fontbutton", create_font_button }, - { "Levelbar" , create_level_bar }, - { "Label" , create_label }, - { "Spinner" , create_spinner }, - { "Spinbutton", create_spinbutton }, -}; - -static int selected_widget_type = -1; -static const int N_WIDGET_TYPES = G_N_ELEMENTS (widget_types); - -#define N_STATS 5 - -#define STATS_UPDATE_TIME G_USEC_PER_SEC - -static void -set_widget_type (GtkWidget *headerbar, - int widget_type_index) -{ - if (widget_type_index == selected_widget_type) - return; - - /* Remove everything */ - gtk_fishbowl_set_count (GTK_FISHBOWL (fishbowl), 0); - - selected_widget_type = widget_type_index; - - gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), - widget_types[selected_widget_type].name); -} - - -typedef struct _Stats Stats; -struct _Stats { - gint64 last_stats; - gint64 last_frame; - gint last_suggestion; - guint frame_counter_max; - - guint stats_index; - guint frame_counter[N_STATS]; - guint item_counter[N_STATS]; -}; - -static Stats * -get_stats (GtkWidget *widget) -{ - static GQuark stats_quark = 0; - Stats *stats; - - if (G_UNLIKELY (stats_quark == 0)) - stats_quark = g_quark_from_static_string ("stats"); - - 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; - } - - return stats; -} - -static void -do_stats (GtkWidget *widget, - GtkWidget *info_label, - gint *suggested_change) -{ - Stats *stats; - gint64 frame_time; - - stats = get_stats (widget); - frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)); - - 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 ("widgets - %.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; - } - - 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]); -} - -static void -stats_update (GtkWidget *widget) -{ - Stats *stats; - - stats = get_stats (widget); - - stats->item_counter[stats->stats_index] = gtk_fishbowl_get_count (GTK_FISHBOWL (widget)); -} - -static gboolean -move_fish (GtkWidget *bowl, - GdkFrameClock *frame_clock, - gpointer info_label) -{ - gint suggested_change = 0; - - do_stats (bowl, info_label, &suggested_change); - - if (suggested_change > 0) - { - int i; - - for (i = 0; i < suggested_change; i ++) - { - GtkWidget *new_widget = widget_types[selected_widget_type].create_func (); - - gtk_widget_show (new_widget); - - gtk_container_add (GTK_CONTAINER (fishbowl), new_widget); - - } - } - else if (suggested_change < 0) - { - gtk_fishbowl_set_count (GTK_FISHBOWL (fishbowl), - gtk_fishbowl_get_count (GTK_FISHBOWL (fishbowl)) + suggested_change); - } - - stats_update (bowl); - - return G_SOURCE_CONTINUE; -} - -static void -next_button_clicked_cb (GtkButton *source, - gpointer user_data) -{ - GtkWidget *headerbar = user_data; - int new_index; - - if (selected_widget_type + 1 >= N_WIDGET_TYPES) - new_index = 0; - else - new_index = selected_widget_type + 1; - - set_widget_type (headerbar, new_index); -} - -static void -prev_button_clicked_cb (GtkButton *source, - gpointer user_data) -{ - GtkWidget *headerbar = user_data; - int new_index; - - if (selected_widget_type - 1 < 0) - new_index = N_WIDGET_TYPES - 1; - else - new_index = selected_widget_type - 1; - - set_widget_type (headerbar, new_index); -} - -GtkWidget * -do_widgetbowl (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) - { - GtkWidget *info_label; - GtkWidget *count_label; - GtkWidget *titlebar; - GtkWidget *title_box; - GtkWidget *left_box; - GtkWidget *next_button; - GtkWidget *prev_button; - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - titlebar = gtk_header_bar_new (); - gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (titlebar), TRUE); - info_label = gtk_label_new ("widget - 00.0 fps"); - count_label = gtk_label_new ("0"); - fishbowl = gtk_fishbowl_new (); - title_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - prev_button = gtk_button_new_from_icon_name ("pan-start-symbolic", GTK_ICON_SIZE_BUTTON); - next_button = gtk_button_new_from_icon_name ("pan-end-symbolic", GTK_ICON_SIZE_BUTTON); - left_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - - g_object_bind_property (fishbowl, "count", count_label, "label", 0); - g_signal_connect (next_button, "clicked", G_CALLBACK (next_button_clicked_cb), titlebar); - g_signal_connect (prev_button, "clicked", G_CALLBACK (prev_button_clicked_cb), titlebar); - - gtk_fishbowl_set_animating (GTK_FISHBOWL (fishbowl), TRUE); - - gtk_widget_set_hexpand (title_box, TRUE); - gtk_widget_set_halign (title_box, GTK_ALIGN_END); - - gtk_window_set_titlebar (GTK_WINDOW (window), titlebar); - gtk_container_add (GTK_CONTAINER (title_box), count_label); - gtk_container_add (GTK_CONTAINER (title_box), info_label); - gtk_header_bar_pack_end (GTK_HEADER_BAR (titlebar), title_box); - gtk_container_add (GTK_CONTAINER (window), fishbowl); - - - gtk_style_context_add_class (gtk_widget_get_style_context (left_box), "linked"); - gtk_container_add (GTK_CONTAINER (left_box), prev_button); - gtk_container_add (GTK_CONTAINER (left_box), next_button); - gtk_header_bar_pack_start (GTK_HEADER_BAR (titlebar), left_box); - - g_signal_connect (window, "destroy", - G_CALLBACK (gtk_widget_destroyed), &window); - - gtk_widget_realize (window); - gtk_widget_add_tick_callback (fishbowl, move_fish, info_label, NULL); - - set_widget_type (titlebar, 0); - } - - if (!gtk_widget_get_visible (window)) - gtk_widget_show_all (window); - else - gtk_widget_destroy (window); - - - return window; -} |