From a849ffbd79c1418f0a94ab4b6758438fcd7cf18c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 5 Jun 2018 21:02:57 -0400 Subject: [PATCH] Add tweaks Allow setting the puzzle size, and allow chosing between the rose and the radioactive animation. Change the default size to 3. 6 is just too hard. --- demos/gtk-demo/sliding_puzzle.c | 255 ++++++++++++++++++++++++++++++-- 1 file changed, 245 insertions(+), 10 deletions(-) diff --git a/demos/gtk-demo/sliding_puzzle.c b/demos/gtk-demo/sliding_puzzle.c index f7615fc15e..09671024dc 100644 --- a/demos/gtk-demo/sliding_puzzle.c +++ b/demos/gtk-demo/sliding_puzzle.c @@ -9,13 +9,179 @@ /* Include the header for the puzzle piece */ #include "puzzlepiece.h" +#include "paintable.h" + +#define ICON_TYPE_PAINTABLE (icon_paintable_get_type ()) + +G_DECLARE_FINAL_TYPE (IconPaintable, icon_paintable, ICON, PAINTABLE, GObject) + +GdkPaintable * icon_paintable_new (GdkPaintable *paintable, + double height); + +struct _IconPaintable +{ + GObject parent_instance; + + GdkPaintable *paintable; + double height; + double scale_factor; +}; + +struct _IconPaintableClass +{ + GObjectClass parent_class; +}; + + +static void +icon_paintable_paintable_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + + if (self->scale_factor == 1.0) + { + gdk_paintable_snapshot (self->paintable, snapshot, width, height); + } + else + { + graphene_matrix_t scale_matrix; + + graphene_matrix_init_scale (&scale_matrix, 1.0 / self->scale_factor, 1.0 / self->scale_factor, 1.0); + gtk_snapshot_push_transform (snapshot, &scale_matrix); + gdk_paintable_snapshot (self->paintable, + snapshot, + width * self->scale_factor, + height * self->scale_factor); + gtk_snapshot_pop (snapshot); + } +} + +static GdkPaintable * +icon_paintable_paintable_get_current_image (GdkPaintable *paintable) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + GdkPaintable *current_paintable, *current_self; + + current_paintable = gdk_paintable_get_current_image (self->paintable); + current_self = icon_paintable_new (current_paintable, self->height); + g_object_unref (current_paintable); + + return current_self; +} + +static GdkPaintableFlags +icon_paintable_paintable_get_flags (GdkPaintable *paintable) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + + return gdk_paintable_get_flags (self->paintable); +} + +static int +icon_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + + return gdk_paintable_get_intrinsic_width (self->paintable) / self->scale_factor; +} + +static int +icon_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + + return gdk_paintable_get_intrinsic_height (self->paintable) / self->scale_factor; +} + +static double icon_paintable_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) +{ + IconPaintable *self = ICON_PAINTABLE (paintable); + + return gdk_paintable_get_intrinsic_aspect_ratio (self->paintable); +}; + +static void +icon_paintable_paintable_init (GdkPaintableInterface *iface) +{ + iface->snapshot = icon_paintable_paintable_snapshot; + iface->get_current_image = icon_paintable_paintable_get_current_image; + iface->get_flags = icon_paintable_paintable_get_flags; + iface->get_intrinsic_width = icon_paintable_paintable_get_intrinsic_width; + iface->get_intrinsic_height = icon_paintable_paintable_get_intrinsic_height; + iface->get_intrinsic_aspect_ratio = icon_paintable_paintable_get_intrinsic_aspect_ratio; +} + +G_DEFINE_TYPE_EXTENDED (IconPaintable, icon_paintable, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, + icon_paintable_paintable_init)) + +static void +icon_paintable_dispose (GObject *object) +{ + IconPaintable *self = ICON_PAINTABLE (object); + + if (self->paintable) + { + g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_contents, self); + g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_size, self); + g_clear_object (&self->paintable); + } + + G_OBJECT_CLASS (icon_paintable_parent_class)->dispose (object); +} + +static void +icon_paintable_class_init (IconPaintableClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = icon_paintable_dispose; +} + +static void +icon_paintable_init (IconPaintable *self) +{ + self->scale_factor = 1.0; +} + +GdkPaintable * +icon_paintable_new (GdkPaintable *paintable, + double height) +{ + IconPaintable *self; + + g_return_val_if_fail (GDK_IS_PAINTABLE (paintable), NULL); + g_return_val_if_fail (height > 0.0, NULL); + + self = g_object_new (ICON_TYPE_PAINTABLE, NULL); + + self->paintable = g_object_ref (paintable); + g_signal_connect_swapped (paintable, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self); + g_signal_connect_swapped (paintable, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self); + self->height = height; + if (gdk_paintable_get_intrinsic_height (paintable) == 0) + self->scale_factor = 1.0; + else + self->scale_factor = gdk_paintable_get_intrinsic_height (paintable) / height; + + return GDK_PAINTABLE (self); +} + static GtkWidget *window = NULL; static GtkWidget *frame = NULL; +static GtkWidget *size_spin = NULL; +static GtkWidget *rose_button = NULL; +static GdkPaintable *puzzle = NULL; +static GdkPaintable *rose = NULL; +static GdkPaintable *atom = NULL; static gboolean solved = TRUE; -static guint width = 6; -static guint height = 6; +static guint width = 3; +static guint height = 3; static guint pos_x; static guint pos_y; @@ -280,24 +446,98 @@ reshuffle (void) GtkWidget *grid; grid = gtk_bin_get_child (GTK_BIN (frame)); - shuffle_puzzle (grid); + if (solved) + start_puzzle (puzzle); + else + shuffle_puzzle (grid); + gtk_widget_grab_focus (grid); +} + +static void +reconfigure (void) +{ + GtkWidget *popover; + GtkWidget *grid; + + width = height = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (size_spin)); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rose_button))) + puzzle = rose; + else + puzzle= atom; + + start_puzzle (puzzle); + popover = gtk_widget_get_ancestor (size_spin, GTK_TYPE_POPOVER); + gtk_popover_popdown (GTK_POPOVER (popover)); + grid = gtk_bin_get_child (GTK_BIN (frame)); + gtk_widget_grab_focus (grid); } GtkWidget * do_sliding_puzzle (GtkWidget *do_widget) { - GdkPaintable *puzzle; - if (!window) { GtkWidget *header; GtkWidget *restart; + GtkWidget *tweak; + GtkWidget *popover; + GtkWidget *tweaks; + GtkWidget *apply; + GtkWidget *image; + GtkWidget *button; + GtkWidget *label; + rose = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg")); + atom = gtk_nuclear_animation_new (); + + puzzle = rose; + + tweaks = gtk_grid_new (); + gtk_grid_set_row_spacing (GTK_GRID (tweaks), 10); + gtk_grid_set_column_spacing (GTK_GRID (tweaks), 10); + g_object_set (tweaks, "margin", 10, NULL); + + rose_button = button = gtk_radio_button_new (NULL); + image = gtk_image_new_from_paintable (icon_paintable_new (rose, 40)); + gtk_image_set_can_shrink (GTK_IMAGE (image), TRUE); + gtk_widget_set_size_request (image, 40, 40); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + gtk_grid_attach (GTK_GRID (tweaks), button, 0, 0, 1, 1); + + button = gtk_radio_button_new_from_widget (button); + image = gtk_image_new_from_paintable (icon_paintable_new (atom, 40)); + gtk_image_set_can_shrink (GTK_IMAGE (image), TRUE); + gtk_widget_set_size_request (image, 40, 40); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_set_halign (button, GTK_ALIGN_CENTER); + gtk_widget_set_valign (button, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (tweaks), button, 1, 0, 1, 1); + + label = gtk_label_new ("Size"); + gtk_label_set_xalign (label, 0.0); + gtk_grid_attach (GTK_GRID (tweaks), label, 0, 1, 1, 1); + size_spin = gtk_spin_button_new_with_range (2, 10, 1); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (size_spin), width); + gtk_grid_attach (GTK_GRID (tweaks), size_spin, 1, 1, 1, 1); + + apply = gtk_button_new_with_label ("Apply"); + gtk_widget_set_halign (apply, GTK_ALIGN_END); + gtk_grid_attach (GTK_GRID (tweaks), apply, 1, 2, 1, 1); + g_signal_connect (apply, "clicked", G_CALLBACK (reconfigure), NULL); + popover = gtk_popover_new (NULL); + gtk_popover_set_modal (GTK_POPOVER (popover), TRUE); + gtk_container_add (GTK_CONTAINER (popover), tweaks); restart = gtk_button_new_from_icon_name ("view-refresh-symbolic"); g_signal_connect (restart, "clicked", G_CALLBACK (reshuffle), NULL); + tweak = gtk_menu_button_new (); + gtk_menu_button_set_popover (GTK_MENU_BUTTON (tweak), popover); + gtk_button_set_icon_name (tweak, "emblem-system-symbolic"); + header = gtk_header_bar_new (); gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE); gtk_header_bar_pack_start (GTK_HEADER_BAR (header), restart); + gtk_header_bar_pack_end (GTK_HEADER_BAR (header), tweak); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget)); @@ -307,15 +547,10 @@ do_sliding_puzzle (GtkWidget *do_widget) g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window); - puzzle = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg")); - frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, (float) gdk_paintable_get_intrinsic_aspect_ratio (puzzle), FALSE); gtk_container_add (GTK_CONTAINER (window), frame); - /* Start a puzzle with a default image */ - start_puzzle (puzzle); - g_object_unref (puzzle); } if (!gtk_widget_get_visible (window))