Compare commits
6 Commits
foreach-ap
...
wip/otte/p
Author | SHA1 | Date | |
---|---|---|---|
|
ca888e5809 | ||
|
ad246d8a28 | ||
|
ec07349da6 | ||
|
fae6a32629 | ||
|
ac015972aa | ||
|
d93b9bedc3 |
@@ -105,9 +105,6 @@
|
||||
<file>gnome-fs-directory.png</file>
|
||||
<file>gnome-fs-regular.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/stack">
|
||||
<file>stack.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/shortcuts">
|
||||
<file>shortcuts.ui</file>
|
||||
<file>shortcuts-builder.ui</file>
|
||||
@@ -115,6 +112,14 @@
|
||||
<file>shortcuts-clocks.ui</file>
|
||||
<file>shortcuts-boxes.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/sliding_puzzle">
|
||||
<file>puzzlepiece.c</file>
|
||||
<file>puzzlepiece.h</file>
|
||||
<file>portland-rose.jpg</file>
|
||||
</gresource>
|
||||
<gresource prefix="/stack">
|
||||
<file>stack.ui</file>
|
||||
</gresource>
|
||||
<gresource prefix="/revealer">
|
||||
<file>revealer.ui</file>
|
||||
</gresource>
|
||||
@@ -198,6 +203,7 @@
|
||||
<file>shortcuts.c</file>
|
||||
<file>sizegroup.c</file>
|
||||
<file>sidebar.c</file>
|
||||
<file>sliding_puzzle.c</file>
|
||||
<file>stack.c</file>
|
||||
<file>spinbutton.c</file>
|
||||
<file>spinner.c</file>
|
||||
|
@@ -61,6 +61,7 @@ demos = files([
|
||||
'shortcuts.c',
|
||||
'sidebar.c',
|
||||
'sizegroup.c',
|
||||
'sliding_puzzle.c',
|
||||
'spinbutton.c',
|
||||
'spinner.c',
|
||||
'stack.c',
|
||||
@@ -76,7 +77,7 @@ demos = files([
|
||||
|
||||
gtkdemo_deps = [ libgtk_dep, ]
|
||||
|
||||
extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c'])
|
||||
extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c'])
|
||||
|
||||
if harfbuzz_dep.found() and pangoft_dep.found()
|
||||
demos += files('font_features.c')
|
||||
|
220
demos/gtk-demo/puzzlepiece.c
Normal file
220
demos/gtk-demo/puzzlepiece.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* Paintable/A simple paintable
|
||||
*
|
||||
* GdkPaintable is an interface used by GTK for drawings of any sort
|
||||
* that do not require layouting or positioning.
|
||||
*
|
||||
* This demo code gives a simple example on how a paintable can
|
||||
* be created.
|
||||
*
|
||||
* Paintables can be used in many places inside GTK widgets, but the
|
||||
* most common usage is inside GtkImage and that's what we're going
|
||||
* to do here.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "puzzlepiece.h"
|
||||
|
||||
/* Declare the struct. */
|
||||
struct _GtkPuzzlePiece
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GdkPaintable *puzzle;
|
||||
guint x;
|
||||
guint y;
|
||||
guint width;
|
||||
guint height;
|
||||
};
|
||||
|
||||
struct _GtkPuzzlePieceClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* This is the function that draws the puzzle piece.
|
||||
* It just draws a rectangular cutout of the puzzle by clipping
|
||||
* away the rest.
|
||||
*/
|
||||
static void
|
||||
gtk_puzzle_piece_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
double width,
|
||||
double height)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (paintable);
|
||||
|
||||
gtk_snapshot_push_clip (snapshot,
|
||||
&GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
gtk_snapshot_offset (snapshot,
|
||||
- width * self->x,
|
||||
- height * self->y);
|
||||
gdk_paintable_snapshot (self->puzzle,
|
||||
snapshot,
|
||||
width * self->width,
|
||||
height * self->height);
|
||||
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
|
||||
static GdkPaintableFlags
|
||||
gtk_puzzle_piece_get_flags (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (paintable);
|
||||
|
||||
/* The flags are the same as the ones of the puzzle.
|
||||
* If the puzzle changes in some way, so do the pieces.
|
||||
*/
|
||||
return gdk_paintable_get_flags (self->puzzle);
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_puzzle_piece_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (paintable);
|
||||
|
||||
/* We can compute our width relative to the puzzle.
|
||||
* This logic even works for the case where the puzzle
|
||||
* has no width, because the 0 return value is unchanged.
|
||||
* Round up the value.
|
||||
*/
|
||||
return (gdk_paintable_get_intrinsic_width (self->puzzle) + self->width - 1) / self->width;
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_puzzle_piece_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (paintable);
|
||||
|
||||
/* Do the same thing we did for the width with the height.
|
||||
*/
|
||||
return (gdk_paintable_get_intrinsic_height (self->puzzle) + self->height - 1) / self->height;
|
||||
}
|
||||
|
||||
static double
|
||||
gtk_puzzle_piece_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (paintable);
|
||||
|
||||
/* We can compute our aspect ratio relative to the puzzle.
|
||||
* This logic again works for the case where the puzzle
|
||||
* has no aspect ratio, because the 0 return value is unchanged.
|
||||
*/
|
||||
return gdk_paintable_get_intrinsic_aspect_ratio (self->puzzle) * self->height / self->width;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_puzzle_piece_paintable_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = gtk_puzzle_piece_snapshot;
|
||||
iface->get_flags = gtk_puzzle_piece_get_flags;
|
||||
iface->get_intrinsic_width = gtk_puzzle_piece_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gtk_puzzle_piece_get_intrinsic_height;
|
||||
iface->get_intrinsic_aspect_ratio = gtk_puzzle_piece_get_intrinsic_aspect_ratio;
|
||||
}
|
||||
|
||||
/* When defining the GType, we need to implement the GdkPaintable interface */
|
||||
G_DEFINE_TYPE_WITH_CODE (GtkPuzzlePiece, gtk_puzzle_piece, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gtk_puzzle_piece_paintable_init))
|
||||
|
||||
/* We need to declare a destructor to release our reference to the
|
||||
* puzzle paintable and disconnect our signal handlers.
|
||||
*/
|
||||
static void
|
||||
gtk_puzzle_piece_dispose (GObject *object)
|
||||
{
|
||||
GtkPuzzlePiece *self = GTK_PUZZLE_PIECE (object);
|
||||
|
||||
if (self->puzzle)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->puzzle, gdk_paintable_invalidate_contents, self);
|
||||
g_signal_handlers_disconnect_by_func (self->puzzle, gdk_paintable_invalidate_size, self);
|
||||
g_clear_object (&self->puzzle);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_puzzle_piece_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
/* Here's the boilerplate for the GObject declaration.
|
||||
*/
|
||||
static void
|
||||
gtk_puzzle_piece_class_init (GtkPuzzlePieceClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_puzzle_piece_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_puzzle_piece_init (GtkPuzzlePiece *self)
|
||||
{
|
||||
}
|
||||
|
||||
/* And finally, we add a constructor.
|
||||
* It is declared in the header so that the other examples
|
||||
* can use it.
|
||||
*/
|
||||
GdkPaintable *
|
||||
gtk_puzzle_piece_new (GdkPaintable *puzzle,
|
||||
guint x,
|
||||
guint y,
|
||||
guint width,
|
||||
guint height)
|
||||
{
|
||||
GtkPuzzlePiece *self;
|
||||
|
||||
/* These are sanity checks, so that we get warnings if we accidentally
|
||||
* do anything stupid. */
|
||||
g_return_val_if_fail (GDK_IS_PAINTABLE (puzzle), NULL);
|
||||
g_return_val_if_fail (width > 0, NULL);
|
||||
g_return_val_if_fail (height > 0, NULL);
|
||||
g_return_val_if_fail (x < width, NULL);
|
||||
g_return_val_if_fail (y < height, NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_PUZZLE_PIECE, NULL);
|
||||
|
||||
self->puzzle = g_object_ref (puzzle);
|
||||
g_signal_connect_swapped (puzzle, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
||||
g_signal_connect_swapped (puzzle, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
||||
self->x = x;
|
||||
self->y = y;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
return GDK_PAINTABLE (self);
|
||||
}
|
||||
|
||||
/* Here are the accessors that we need to inspect the puzzle
|
||||
* pieces in other code.
|
||||
*/
|
||||
GdkPaintable *
|
||||
gtk_puzzle_piece_get_puzzle (GtkPuzzlePiece *self)
|
||||
{
|
||||
/* Add sanity checks here, too.
|
||||
* If you make a habit out of this, you can always rely
|
||||
* on your code having sanity checks, which makes it
|
||||
* way easier to debug.
|
||||
*/
|
||||
g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), NULL);
|
||||
|
||||
return self->puzzle;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_puzzle_piece_get_x (GtkPuzzlePiece *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), 0);
|
||||
|
||||
return self->x;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_puzzle_piece_get_y (GtkPuzzlePiece *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PUZZLE_PIECE (self), 0);
|
||||
|
||||
return self->y;
|
||||
}
|
||||
|
23
demos/gtk-demo/puzzlepiece.h
Normal file
23
demos/gtk-demo/puzzlepiece.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef __PUZZLE_PIECE_H__
|
||||
#define __PUZZLE_PIECE_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* First, add the boilerplate for the object itself.
|
||||
*/
|
||||
#define GTK_TYPE_PUZZLE_PIECE (gtk_puzzle_piece_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkPuzzlePiece, gtk_puzzle_piece, GTK, PUZZLE_PIECE, GObject)
|
||||
|
||||
/* Then, declare all constructors */
|
||||
GdkPaintable * gtk_puzzle_piece_new (GdkPaintable *puzzle,
|
||||
guint x,
|
||||
guint y,
|
||||
guint width,
|
||||
guint height);
|
||||
|
||||
/* Next, add the getters and setters for object properties */
|
||||
GdkPaintable * gtk_puzzle_piece_get_puzzle (GtkPuzzlePiece *self);
|
||||
guint gtk_puzzle_piece_get_x (GtkPuzzlePiece *self);
|
||||
guint gtk_puzzle_piece_get_y (GtkPuzzlePiece *self);
|
||||
|
||||
#endif /* __PUZZLE_PIECE_H__ */
|
460
demos/gtk-demo/sliding_puzzle.c
Normal file
460
demos/gtk-demo/sliding_puzzle.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/* Sliding puzzle
|
||||
*
|
||||
* This demo demonstrates how to use gestures and paintables to create a
|
||||
* small sliding puzzle game.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
/* Include the header for the puzzle piece */
|
||||
#include "puzzlepiece.h"
|
||||
#include "paintable.h"
|
||||
|
||||
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 = 3;
|
||||
static guint height = 3;
|
||||
static guint pos_x;
|
||||
static guint pos_y;
|
||||
|
||||
static gboolean
|
||||
move_puzzle (GtkWidget *grid,
|
||||
int dx,
|
||||
int dy)
|
||||
{
|
||||
GtkWidget *pos, *next;
|
||||
GdkPaintable *piece;
|
||||
guint next_x, next_y;
|
||||
|
||||
/* We don't move anything if the puzzle is solved */
|
||||
if (solved)
|
||||
return FALSE;
|
||||
|
||||
/* Return FALSE if we can't move to where the call
|
||||
* wants us to move.
|
||||
*/
|
||||
if ((dx < 0 && pos_x < -dx) ||
|
||||
dx + pos_x >= width ||
|
||||
(dy < 0 && pos_y < -dy) ||
|
||||
dy + pos_y >= height)
|
||||
return FALSE;
|
||||
|
||||
/* Compute the new position */
|
||||
next_x = pos_x + dx;
|
||||
next_y = pos_y + dy;
|
||||
|
||||
/* Get the current and next image */
|
||||
pos = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
|
||||
next = gtk_grid_get_child_at (GTK_GRID (grid), next_x, next_y);
|
||||
|
||||
/* Move the displayed piece. */
|
||||
piece = gtk_image_get_paintable (GTK_IMAGE (next));
|
||||
gtk_image_set_from_paintable (GTK_IMAGE (pos), piece);
|
||||
gtk_image_clear (GTK_IMAGE (next));
|
||||
|
||||
/* Update the current position */
|
||||
pos_x = next_x;
|
||||
pos_y = next_y;
|
||||
|
||||
/* Return TRUE because we successfully moved the piece */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
shuffle_puzzle (GtkWidget *grid)
|
||||
{
|
||||
guint i, n_steps;
|
||||
|
||||
/* Do this many random moves */
|
||||
n_steps = width * height * 50;
|
||||
|
||||
for (i = 0; i < n_steps; i++)
|
||||
{
|
||||
/* Get a random number for the direction to move in */
|
||||
switch (g_random_int_range (0, 4))
|
||||
{
|
||||
case 0:
|
||||
/* left */
|
||||
move_puzzle (grid, -1, 0);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* up */
|
||||
move_puzzle (grid, 0, -1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* right */
|
||||
move_puzzle (grid, 1, 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
/* down */
|
||||
move_puzzle (grid, 0, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_solved (GtkWidget *grid)
|
||||
{
|
||||
GtkWidget *image;
|
||||
GdkPaintable *piece;
|
||||
guint x, y;
|
||||
|
||||
/* Nothing to check if the puzzle is already solved */
|
||||
if (solved)
|
||||
return TRUE;
|
||||
|
||||
/* If the empty cell isn't in the bottom right,
|
||||
* the puzzle is obviously not solved */
|
||||
if (pos_x != width - 1 ||
|
||||
pos_y != height - 1)
|
||||
return FALSE;
|
||||
|
||||
/* Check that all pieces are in the right position */
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
image = gtk_grid_get_child_at (GTK_GRID (grid), x, y);
|
||||
piece = gtk_image_get_paintable (GTK_IMAGE (image));
|
||||
|
||||
/* empty cell */
|
||||
if (piece == NULL)
|
||||
continue;
|
||||
|
||||
if (gtk_puzzle_piece_get_x (GTK_PUZZLE_PIECE (piece)) != x ||
|
||||
gtk_puzzle_piece_get_y (GTK_PUZZLE_PIECE (piece)) != y)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* We solved the puzzle!
|
||||
*/
|
||||
solved = TRUE;
|
||||
|
||||
/* Fill the empty cell to show that we're done.
|
||||
*/
|
||||
image = gtk_grid_get_child_at (GTK_GRID (grid), 0, 0);
|
||||
piece = gtk_image_get_paintable (GTK_IMAGE (image));
|
||||
|
||||
piece = gtk_puzzle_piece_new (gtk_puzzle_piece_get_puzzle (GTK_PUZZLE_PIECE (piece)),
|
||||
pos_x, pos_y,
|
||||
width, height);
|
||||
image = gtk_grid_get_child_at (GTK_GRID (grid), pos_x, pos_y);
|
||||
gtk_image_set_from_paintable (GTK_IMAGE (image), piece);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
puzzle_key_pressed (GtkEventControllerKey *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType state,
|
||||
GtkWidget *grid)
|
||||
{
|
||||
int dx, dy;
|
||||
|
||||
dx = 0;
|
||||
dy = 0;
|
||||
|
||||
switch (keyval)
|
||||
{
|
||||
case GDK_KEY_KP_Left:
|
||||
case GDK_KEY_Left:
|
||||
/* left */
|
||||
dx = -1;
|
||||
break;
|
||||
|
||||
case GDK_KEY_KP_Up:
|
||||
case GDK_KEY_Up:
|
||||
/* up */
|
||||
dy = -1;
|
||||
break;
|
||||
|
||||
case GDK_KEY_KP_Right:
|
||||
case GDK_KEY_Right:
|
||||
/* right */
|
||||
dx = 1;
|
||||
break;
|
||||
|
||||
case GDK_KEY_KP_Down:
|
||||
case GDK_KEY_Down:
|
||||
/* down */
|
||||
dy = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* We return FALSE here because we didn't handle the key that was pressed */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!move_puzzle (grid, dx, dy))
|
||||
{
|
||||
/* Make the error sound and then return TRUE.
|
||||
* We handled this key, even though we didn't
|
||||
* do anything to the puzzle.
|
||||
*/
|
||||
gtk_widget_error_bell (grid);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
check_solved (grid);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
puzzle_button_pressed (GtkGestureMultiPress *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkWidget *grid)
|
||||
{
|
||||
GtkWidget *child;
|
||||
int l, t, i;
|
||||
int pos;
|
||||
|
||||
child = gtk_widget_pick (grid, x, y);
|
||||
|
||||
if (!child)
|
||||
{
|
||||
gtk_widget_error_bell (grid);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_container_child_get (GTK_CONTAINER (grid), child,
|
||||
"left-attach", &l,
|
||||
"top-attach", &t,
|
||||
NULL);
|
||||
|
||||
if (l == pos_x && t == pos_y)
|
||||
{
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
else if (l == pos_x)
|
||||
{
|
||||
pos = pos_y;
|
||||
for (i = t; i < pos; i++)
|
||||
{
|
||||
if (!move_puzzle (grid, 0, -1))
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
for (i = pos; i < t; i++)
|
||||
{
|
||||
if (!move_puzzle (grid, 0, 1))
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
}
|
||||
else if (t == pos_y)
|
||||
{
|
||||
pos = pos_x;
|
||||
for (i = l; i < pos; i++)
|
||||
{
|
||||
if (!move_puzzle (grid, -1, 0))
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
for (i = pos; i < l; i++)
|
||||
{
|
||||
if (!move_puzzle (grid, 1, 0))
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_error_bell (grid);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_puzzle (GdkPaintable *puzzle)
|
||||
{
|
||||
GtkWidget *image, *grid;
|
||||
GtkEventController *controller;
|
||||
guint x, y;
|
||||
|
||||
/* Remove the old grid (if there is one) */
|
||||
grid = gtk_bin_get_child (GTK_BIN (frame));
|
||||
if (grid)
|
||||
gtk_container_remove (GTK_CONTAINER (frame), grid);
|
||||
|
||||
/* Create a new grid */
|
||||
grid = gtk_grid_new ();
|
||||
gtk_widget_set_can_focus (grid, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (frame), grid);
|
||||
|
||||
/* Add a key event controller so people can use the arrow
|
||||
* keys to move the puzzle */
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed",
|
||||
G_CALLBACK (puzzle_key_pressed),
|
||||
grid);
|
||||
gtk_widget_add_controller (GTK_WIDGET (grid), controller);
|
||||
|
||||
controller = gtk_gesture_multi_press_new ();
|
||||
g_signal_connect (controller, "pressed",
|
||||
G_CALLBACK (puzzle_button_pressed),
|
||||
grid);
|
||||
gtk_widget_add_controller (GTK_WIDGET (grid), controller);
|
||||
|
||||
/* Make sure the cells have equal size */
|
||||
gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
|
||||
gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
|
||||
|
||||
/* Reset the variables */
|
||||
solved = FALSE;
|
||||
pos_x = width - 1;
|
||||
pos_y = height - 1;
|
||||
|
||||
/* add an image for every cell */
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
GdkPaintable *piece;
|
||||
|
||||
/* Don't paint anything for the lsiding part of the video */
|
||||
if (x == pos_x && y == pos_y)
|
||||
piece = NULL;
|
||||
else
|
||||
piece = gtk_puzzle_piece_new (puzzle,
|
||||
x, y,
|
||||
width, height);
|
||||
image = gtk_image_new_from_paintable (piece);
|
||||
gtk_image_set_keep_aspect_ratio (GTK_IMAGE (image), FALSE);
|
||||
gtk_image_set_can_shrink (GTK_IMAGE (image), TRUE);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
image,
|
||||
x, y,
|
||||
1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
shuffle_puzzle (grid);
|
||||
}
|
||||
|
||||
static void
|
||||
reshuffle (void)
|
||||
{
|
||||
GtkWidget *grid;
|
||||
|
||||
grid = gtk_bin_get_child (GTK_BIN (frame));
|
||||
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)
|
||||
{
|
||||
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_with_label (NULL, "Rose");
|
||||
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_with_label_from_widget (button, "Danger");
|
||||
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));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Sliding Puzzle");
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &window);
|
||||
|
||||
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_puzzle (puzzle);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
Reference in New Issue
Block a user