diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am index 261d555ae5..decba7d0e2 100644 --- a/demos/gtk-demo/Makefile.am +++ b/demos/gtk-demo/Makefile.am @@ -28,6 +28,7 @@ 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 a02c4843f9..7ab207c692 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -156,6 +156,7 @@ expander.c filtermodel.c fishbowl.c + widgetbowl.c flowbox.c foreigndrawing.c font_features.c diff --git a/demos/gtk-demo/widgetbowl.c b/demos/gtk-demo/widgetbowl.c new file mode 100644 index 0000000000..0b7e5a0ab7 --- /dev/null +++ b/demos/gtk-demo/widgetbowl.c @@ -0,0 +1,365 @@ +/* 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 + +#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; +}