Compare commits
22 Commits
matthiasc/
...
wip/otte/c
Author | SHA1 | Date | |
---|---|---|---|
|
1ba2ef82f3 | ||
|
bcd04595dd | ||
|
ad2f4a27be | ||
|
fd4a717dda | ||
|
3adc287703 | ||
|
290d6b6de4 | ||
|
384a8730df | ||
|
366ab8ca7c | ||
|
068d73f3ae | ||
|
7f36e3619d | ||
|
eaa9fddfb0 | ||
|
e848ead629 | ||
|
9eedd3f5ac | ||
|
d329c904b8 | ||
|
1d5f54ee96 | ||
|
35534b7143 | ||
|
5ce5ab4bb6 | ||
|
143fd737a0 | ||
|
8366ba8c23 | ||
|
c2825317c4 | ||
|
0ff8c77568 | ||
|
0e49a24aec |
104
demos/gtk-demo/canvas_intro.c
Normal file
104
demos/gtk-demo/canvas_intro.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/* Canvas/Intro
|
||||
*
|
||||
* GtkCanvas is a very powerful canvas widget. Here is
|
||||
* a simple Hello World demo to get accustomed to how
|
||||
* it works.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define WIDTH 400
|
||||
#define HEIGHT 300
|
||||
|
||||
static gboolean
|
||||
center_item (GtkCanvasItem *ci,
|
||||
GtkCanvasBox *out_box,
|
||||
gpointer unused)
|
||||
{
|
||||
const GtkCanvasBox *viewport;
|
||||
GtkWidget *widget;
|
||||
int width, height;
|
||||
|
||||
/* We need to check if the viewport is available.
|
||||
* If the canvas was scrolling, it might not be avaiable yet.
|
||||
*/
|
||||
viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
|
||||
if (viewport == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* Measure the widget min for min so that it will line-break. */
|
||||
widget = gtk_canvas_item_get_widget (ci);
|
||||
gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1, &width, NULL, NULL, NULL);
|
||||
gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, width, &height, NULL, NULL, NULL);
|
||||
|
||||
/* Initialize the bounds for this widget:
|
||||
* - the point is the center of the canvas' viewport
|
||||
* - the size is the one we just computed
|
||||
* - we want the origin point to be at the center
|
||||
*/
|
||||
gtk_canvas_box_init (out_box,
|
||||
viewport->size.width * 0.5,
|
||||
viewport->size.height * 0.5,
|
||||
0, 0,
|
||||
0.5, 0.5);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_item (GtkListItemFactory *factory,
|
||||
GtkCanvasItem *ci)
|
||||
{
|
||||
gtk_canvas_item_set_widget (ci, gtk_canvas_item_get_item (ci));
|
||||
|
||||
/* Set a function to compute the position */
|
||||
g_signal_connect (ci, "compute-bounds", G_CALLBACK (center_item), NULL);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_canvas_intro (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *canvas, *widget;
|
||||
GListStore *store;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
/* GtkCanvas manages its items using an external list.
|
||||
* We do a very simple thing and put the widgets in the list
|
||||
* that the canvas should display.
|
||||
*/
|
||||
store = g_list_store_new (GTK_TYPE_WIDGET);
|
||||
widget = gtk_label_new ("Hello World");
|
||||
gtk_label_set_wrap (GTK_LABEL (widget), TRUE);
|
||||
g_list_store_append (store, widget);
|
||||
|
||||
/* GtkCanvas maps the items from the list to the canvas using factories.
|
||||
* Set up a simple factory here that just maps the widget directly
|
||||
* onto the canvas.
|
||||
*/
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
|
||||
/* Create the canvas.
|
||||
* We hand it the factory and the model, and then everything happens by itself.
|
||||
*/
|
||||
canvas = gtk_canvas_new (G_LIST_MODEL (store), factory);
|
||||
gtk_window_set_child (GTK_WINDOW (window), canvas);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
330
demos/gtk-demo/canvas_planarity.c
Normal file
330
demos/gtk-demo/canvas_planarity.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/* Canvas/Planarity
|
||||
*
|
||||
* This demonstrates how the canvas can be used to display different
|
||||
* types of data (vertices and edges) and how to operate on them.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define GTK_TYPE_DIAGONAL_LINE (gtk_diagonal_line_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkDiagonalLine, gtk_diagonal_line, GTK, DIAGONAL_LINE, GtkWidget)
|
||||
|
||||
struct _GtkDiagonalLine
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
};
|
||||
|
||||
struct _GtkDiagonalLineClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_diagonal_line_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
const float line_width = 6;
|
||||
const int width = gtk_widget_get_width (widget);
|
||||
const int height = gtk_widget_get_height (widget);
|
||||
const double length = sqrt (width * width + height * height);
|
||||
GskRoundedRect clip;
|
||||
GdkRGBA color;
|
||||
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_rotate (snapshot, atan2 (height, width) * 180 / G_PI);
|
||||
|
||||
gsk_rounded_rect_init_from_rect (&clip,
|
||||
&GRAPHENE_RECT_INIT (
|
||||
-0.5f * line_width,
|
||||
-0.5f * line_width,
|
||||
length + line_width,
|
||||
line_width),
|
||||
0.5f * line_width);
|
||||
gtk_snapshot_push_rounded_clip (snapshot, &clip);
|
||||
gtk_style_context_get_color (gtk_widget_get_style_context (widget), &color);
|
||||
gtk_snapshot_append_color (snapshot,
|
||||
&color,
|
||||
&clip.bounds);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
}
|
||||
|
||||
/* When defining the GType, we need to implement the GdkPaintable interface */
|
||||
G_DEFINE_TYPE (GtkDiagonalLine, gtk_diagonal_line, GTK_TYPE_WIDGET)
|
||||
|
||||
/* Here's the boilerplate for the GObject declaration.
|
||||
*/
|
||||
static void
|
||||
gtk_diagonal_line_class_init (GtkDiagonalLineClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
widget_class->snapshot = gtk_diagonal_line_snapshot;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_diagonal_line_init (GtkDiagonalLine *self)
|
||||
{
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
gtk_diagonal_line_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_DIAGONAL_LINE, NULL);
|
||||
}
|
||||
|
||||
typedef struct _PlanarityVertex PlanarityVertex;
|
||||
struct _PlanarityVertex
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkOrigin position;
|
||||
};
|
||||
|
||||
#define PLANARITY_TYPE_VERTEX (planarity_vertex_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (PlanarityVertex, planarity_vertex, PLANARITY, VERTEX, GObject);
|
||||
|
||||
G_DEFINE_TYPE (PlanarityVertex, planarity_vertex, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
planarity_vertex_class_init (PlanarityVertexClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
planarity_vertex_init (PlanarityVertex *self)
|
||||
{
|
||||
}
|
||||
|
||||
static PlanarityVertex *
|
||||
planarity_vertex_new (float horizontal,
|
||||
float vertical)
|
||||
{
|
||||
PlanarityVertex *self;
|
||||
|
||||
self = g_object_new (PLANARITY_TYPE_VERTEX, NULL);
|
||||
|
||||
self->position.horizontal = horizontal;
|
||||
self->position.vertical = vertical;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
typedef struct _PlanarityEdge PlanarityEdge;
|
||||
struct _PlanarityEdge
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
PlanarityVertex *from;
|
||||
PlanarityVertex *to;
|
||||
};
|
||||
|
||||
#define PLANARITY_TYPE_EDGE (planarity_edge_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (PlanarityEdge, planarity_edge, PLANARITY, EDGE, GObject);
|
||||
|
||||
G_DEFINE_TYPE (PlanarityEdge, planarity_edge, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
planarity_edge_class_init (PlanarityEdgeClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
planarity_edge_init (PlanarityEdge *self)
|
||||
{
|
||||
}
|
||||
|
||||
static PlanarityEdge *
|
||||
planarity_edge_new (PlanarityVertex *from,
|
||||
PlanarityVertex *to)
|
||||
{
|
||||
PlanarityEdge *edge;
|
||||
|
||||
edge = g_object_new (PLANARITY_TYPE_EDGE, NULL);
|
||||
|
||||
edge->from = g_object_ref (from);
|
||||
edge->to = g_object_ref (to);
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_vertex_bounds (GtkCanvasItem *ci,
|
||||
GtkCanvasBox *out_box,
|
||||
gpointer user_data)
|
||||
{
|
||||
PlanarityVertex *vertex = gtk_canvas_item_get_item (ci);
|
||||
const GtkCanvasBox *viewport;
|
||||
|
||||
viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
|
||||
if (viewport == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_canvas_box_init (out_box,
|
||||
viewport->size.width * vertex->position.horizontal,
|
||||
viewport->size.height * vertex->position.vertical,
|
||||
0, 0,
|
||||
vertex->position.horizontal,
|
||||
vertex->position.vertical);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
move_vertex (GtkGestureDrag *gesture,
|
||||
double x,
|
||||
double y,
|
||||
GtkCanvasItem *ci)
|
||||
{
|
||||
GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
|
||||
PlanarityVertex *vertex = gtk_canvas_item_get_item (ci);
|
||||
|
||||
x /= gtk_widget_get_width (GTK_WIDGET (canvas));
|
||||
y /= gtk_widget_get_height (GTK_WIDGET (canvas));
|
||||
|
||||
vertex->position.horizontal = CLAMP (vertex->position.horizontal + x, 0, 1);
|
||||
vertex->position.vertical = CLAMP (vertex->position.vertical + y, 0, 1);
|
||||
|
||||
gtk_canvas_item_invalidate_bounds (ci);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_edge_bounds (GtkCanvasItem *ci,
|
||||
GtkCanvasBox *out_box,
|
||||
gpointer user_data)
|
||||
{
|
||||
PlanarityEdge *edge = gtk_canvas_item_get_item (ci);
|
||||
GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
|
||||
GtkCanvasItem *from_item, *to_item;
|
||||
const GtkCanvasBox *from_box, *to_box;
|
||||
graphene_rect_t from_rect, to_rect;
|
||||
graphene_point_t from_center, to_center;
|
||||
|
||||
from_item = gtk_canvas_lookup_item (canvas, edge->from);
|
||||
to_item = gtk_canvas_lookup_item (canvas, edge->to);
|
||||
from_box = gtk_canvas_item_get_allocation (from_item);
|
||||
to_box = gtk_canvas_item_get_allocation (to_item);
|
||||
if (from_box == NULL || to_box == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_canvas_box_to_rect (from_box, &from_rect);
|
||||
gtk_canvas_box_to_rect (to_box, &to_rect);
|
||||
graphene_rect_get_center (&from_rect, &from_center);
|
||||
graphene_rect_get_center (&to_rect, &to_center);
|
||||
|
||||
gtk_canvas_box_init (out_box,
|
||||
from_center.x, from_center.y,
|
||||
to_center.x - from_center.x,
|
||||
to_center.y - from_center.y,
|
||||
0, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_item (GtkListItemFactory *factory,
|
||||
GtkCanvasItem *ci)
|
||||
{
|
||||
gpointer item;
|
||||
|
||||
item = gtk_canvas_item_get_item (ci);
|
||||
|
||||
if (PLANARITY_IS_VERTEX (item))
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GtkGesture *gesture;
|
||||
|
||||
widget = gtk_image_new_from_icon_name ("media-record-symbolic");
|
||||
gtk_image_set_icon_size (GTK_IMAGE (widget), GTK_ICON_SIZE_LARGE);
|
||||
gesture = gtk_gesture_drag_new ();
|
||||
g_signal_connect (gesture, "drag-update", G_CALLBACK (move_vertex), ci);
|
||||
g_signal_connect (gesture, "drag-end", G_CALLBACK (move_vertex), ci);
|
||||
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
|
||||
gtk_canvas_item_set_widget (ci, widget);
|
||||
|
||||
g_signal_connect (ci, "compute-bounds", G_CALLBACK (set_vertex_bounds), NULL);
|
||||
}
|
||||
else if (PLANARITY_IS_EDGE (item))
|
||||
{
|
||||
gtk_canvas_item_set_widget (ci, gtk_diagonal_line_new ());
|
||||
g_signal_connect (ci, "compute-bounds", G_CALLBACK (set_edge_bounds), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_model (void)
|
||||
{
|
||||
GListStore *result, *vertices, *edges;
|
||||
guint n = 10;
|
||||
guint i, j;
|
||||
|
||||
vertices = g_list_store_new (PLANARITY_TYPE_VERTEX);
|
||||
edges = g_list_store_new (PLANARITY_TYPE_EDGE);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
PlanarityVertex *vertex = planarity_vertex_new (g_random_double (), g_random_double ());
|
||||
g_list_store_append (vertices, vertex);
|
||||
|
||||
for (j = 0; j < i; j++)
|
||||
{
|
||||
PlanarityEdge *edge;
|
||||
PlanarityVertex *other;
|
||||
if (g_random_boolean ())
|
||||
continue;
|
||||
|
||||
other = g_list_model_get_item (G_LIST_MODEL (vertices), j);
|
||||
edge = planarity_edge_new (vertex, other);
|
||||
g_object_unref (other);
|
||||
g_list_store_append (edges, edge);
|
||||
g_object_unref (edge);
|
||||
}
|
||||
g_object_unref (vertex);
|
||||
}
|
||||
|
||||
result = g_list_store_new (G_TYPE_LIST_MODEL);
|
||||
/* put edges before vertices due to staking order */
|
||||
g_list_store_append (result, edges);
|
||||
g_object_unref (edges);
|
||||
g_list_store_append (result, vertices);
|
||||
g_object_unref (vertices);
|
||||
|
||||
return G_LIST_MODEL (gtk_flatten_list_model_new (G_LIST_MODEL (result)));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_canvas_planarity (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *canvas;
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
model = create_model ();
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
|
||||
/* Create the canvas.
|
||||
* We hand it the factory and the model, and then everything happens by itself.
|
||||
*/
|
||||
canvas = gtk_canvas_new (model, factory);
|
||||
gtk_window_set_child (GTK_WINDOW (window), canvas);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
136
demos/gtk-demo/canvas_puzzle.c
Normal file
136
demos/gtk-demo/canvas_puzzle.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/* Canvas/Intro
|
||||
*
|
||||
* GtkCanvas is a very powerful canvas widget. Here is
|
||||
* a simple Hello World demo to get accustomed to how
|
||||
* it works.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "puzzlepiece.h"
|
||||
|
||||
static gboolean
|
||||
set_position_from_origin (GtkCanvasItem *ci,
|
||||
GtkCanvasBox *out_box,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkOrigin *origin = g_object_get_data (G_OBJECT (ci), "position");
|
||||
const GtkCanvasBox *viewport = gtk_canvas_get_viewport (gtk_canvas_item_get_canvas (ci));
|
||||
|
||||
if (viewport == NULL)
|
||||
return FALSE;
|
||||
|
||||
gtk_canvas_box_init (out_box,
|
||||
viewport->size.width * origin->horizontal,
|
||||
viewport->size.height * origin->vertical,
|
||||
0, 0,
|
||||
origin->horizontal,
|
||||
origin->vertical);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
move_item (GtkGestureDrag *gesture,
|
||||
double x,
|
||||
double y,
|
||||
GtkCanvasItem *ci)
|
||||
{
|
||||
GtkCanvas *canvas = gtk_canvas_item_get_canvas (ci);
|
||||
GtkOrigin *origin = g_object_get_data (G_OBJECT (ci), "position");
|
||||
|
||||
origin->horizontal += x / gtk_widget_get_width (GTK_WIDGET (canvas));
|
||||
origin->vertical += y / gtk_widget_get_height (GTK_WIDGET (canvas));
|
||||
origin->horizontal = CLAMP (origin->horizontal, 0, 1);
|
||||
origin->vertical = CLAMP (origin->vertical, 0, 1);
|
||||
|
||||
gtk_canvas_item_invalidate_bounds (ci);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_item (GtkListItemFactory *factory,
|
||||
GtkCanvasItem *ci)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GtkGesture *gesture;
|
||||
GtkOrigin *origin;
|
||||
|
||||
widget = gtk_picture_new_for_paintable (gtk_canvas_item_get_item (ci));
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (widget), FALSE);
|
||||
gesture = gtk_gesture_drag_new ();
|
||||
g_signal_connect (gesture, "drag-update", G_CALLBACK (move_item), ci);
|
||||
g_signal_connect (gesture, "drag-end", G_CALLBACK (move_item), ci);
|
||||
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
|
||||
gtk_canvas_item_set_widget (ci, widget);
|
||||
|
||||
/* Set a random position */
|
||||
origin = g_new (GtkOrigin, 1);
|
||||
origin->horizontal = g_random_double ();
|
||||
origin->vertical = g_random_double ();
|
||||
g_object_set_data_full (G_OBJECT (ci), "position", origin, g_free);
|
||||
|
||||
g_signal_connect (ci, "compute-bounds", G_CALLBACK (set_position_from_origin), NULL);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_puzzle (GdkPaintable *puzzle)
|
||||
{
|
||||
GListStore *store = g_list_store_new (GDK_TYPE_PAINTABLE);
|
||||
int width = 5;
|
||||
int height = 5;
|
||||
int x, y;
|
||||
|
||||
/* add a picture for every cell */
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
GdkPaintable *piece;
|
||||
|
||||
piece = gtk_puzzle_piece_new (puzzle,
|
||||
x, y,
|
||||
width, height);
|
||||
g_list_store_append (store, piece);
|
||||
g_object_unref (piece);
|
||||
}
|
||||
}
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_canvas_puzzle (GtkWidget *do_widget)
|
||||
{
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *canvas;
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
GdkPaintable *puzzle;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 300);
|
||||
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
|
||||
|
||||
puzzle = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
|
||||
model = create_puzzle (puzzle);
|
||||
g_object_unref (puzzle);
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "bind", G_CALLBACK (bind_item), NULL);
|
||||
|
||||
canvas = gtk_canvas_new (model, factory);
|
||||
gtk_window_set_child (GTK_WINDOW (window), canvas);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (window));
|
||||
|
||||
return window;
|
||||
}
|
@@ -254,6 +254,8 @@
|
||||
<file>application_demo.c</file>
|
||||
<file>assistant.c</file>
|
||||
<file>builder.c</file>
|
||||
<file>canvas_intro.c</file>
|
||||
<file>canvas_puzzle.c</file>
|
||||
<file>clipboard.c</file>
|
||||
<file>combobox.c</file>
|
||||
<file>constraints.c</file>
|
||||
|
@@ -4,6 +4,9 @@ demos = files([
|
||||
'application_demo.c',
|
||||
'assistant.c',
|
||||
'builder.c',
|
||||
'canvas_intro.c',
|
||||
'canvas_planarity.c',
|
||||
'canvas_puzzle.c',
|
||||
'clipboard.c',
|
||||
'combobox.c',
|
||||
'constraints.c',
|
||||
|
@@ -322,7 +322,7 @@ start_puzzle (GdkPaintable *paintable)
|
||||
{
|
||||
GdkPaintable *piece;
|
||||
|
||||
/* Don't paint anything for the lsiding part of the video */
|
||||
/* Don't paint anything for the sliding part of the video */
|
||||
if (x == pos_x && y == pos_y)
|
||||
piece = NULL;
|
||||
else
|
||||
|
@@ -59,6 +59,9 @@
|
||||
#include <gtk/gtkbuilderscope.h>
|
||||
#include <gtk/gtkbutton.h>
|
||||
#include <gtk/gtkcalendar.h>
|
||||
#include <gtk/gtkcanvas.h>
|
||||
#include <gtk/gtkcanvasbox.h>
|
||||
#include <gtk/gtkcanvasitem.h>
|
||||
#include <gtk/gtkcellarea.h>
|
||||
#include <gtk/gtkcellareabox.h>
|
||||
#include <gtk/gtkcellareacontext.h>
|
||||
|
525
gtk/gtkcanvas.c
Normal file
525
gtk/gtkcanvas.c
Normal file
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcanvas.h"
|
||||
|
||||
#include "gtkcanvasbox.h"
|
||||
#include "gtkcanvasitemprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactory.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
#define GDK_ARRAY_NAME gtk_canvas_items
|
||||
#define GDK_ARRAY_TYPE_NAME GtkCanvasItems
|
||||
#define GDK_ARRAY_ELEMENT_TYPE GtkCanvasItem *
|
||||
#define GDK_ARRAY_FREE_FUNC g_object_unref
|
||||
#include "gdk/gdkarrayimpl.c"
|
||||
|
||||
/**
|
||||
* GtkCanvas:
|
||||
*
|
||||
* `GtkCanvas` is a widget that allows developers to place a list of items
|
||||
* using their own method.
|
||||
*
|
||||
* 
|
||||
*/
|
||||
|
||||
struct _GtkCanvas
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
|
||||
GtkCanvasItems items;
|
||||
GHashTable *item_lookup;
|
||||
|
||||
GtkCanvasBox viewport;
|
||||
guint viewport_valid : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FACTORY,
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_FINAL_TYPE (GtkCanvas, gtk_canvas, GTK_TYPE_WIDGET)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_canvas_clear_factory (GtkCanvas *self)
|
||||
{
|
||||
if (self->factory == NULL)
|
||||
return;
|
||||
|
||||
g_clear_object (&self->factory);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_remove_items (GtkCanvas *self,
|
||||
guint pos,
|
||||
guint n_items)
|
||||
{
|
||||
guint i;
|
||||
|
||||
/* We first remove the items, and then do teardown. That way we have a consistent
|
||||
* state in the canvas.
|
||||
*/
|
||||
for (i = pos; i < pos + n_items; i++)
|
||||
{
|
||||
GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
|
||||
g_hash_table_remove (self->item_lookup, gtk_canvas_item_get_item (ci));
|
||||
}
|
||||
for (i = pos; i < pos + n_items; i++)
|
||||
{
|
||||
gtk_canvas_item_teardown (gtk_canvas_items_get (&self->items, i), self->factory);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_add_items (GtkCanvas *self,
|
||||
guint pos,
|
||||
guint n_items)
|
||||
{
|
||||
guint i;
|
||||
|
||||
/* We first create all the items and then run the factory code
|
||||
* on them, so that the factory code can reference the items.
|
||||
*/
|
||||
for (i = pos; i < pos + n_items; i++)
|
||||
{
|
||||
gpointer item = g_list_model_get_item (self->model, i);
|
||||
*gtk_canvas_items_index (&self->items, i) = gtk_canvas_item_new (self, item);
|
||||
g_hash_table_insert (self->item_lookup, item, gtk_canvas_items_get (&self->items, i));
|
||||
}
|
||||
for (i = pos; i < pos + n_items; i++)
|
||||
{
|
||||
gtk_canvas_item_setup (gtk_canvas_items_get (&self->items, i), self->factory);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_items_changed_cb (GListModel *model,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkCanvas *self)
|
||||
{
|
||||
gtk_canvas_remove_items (self, pos, removed);
|
||||
|
||||
gtk_canvas_items_splice (&self->items, pos, removed, FALSE, NULL, added);
|
||||
|
||||
gtk_canvas_add_items (self, pos, added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_clear_model (GtkCanvas *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
gtk_canvas_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_dispose (GObject *object)
|
||||
{
|
||||
GtkCanvas *self = GTK_CANVAS (object);
|
||||
|
||||
gtk_canvas_clear_model (self);
|
||||
gtk_canvas_clear_factory (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_canvas_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_finalize (GObject *object)
|
||||
{
|
||||
GtkCanvas *self = GTK_CANVAS (object);
|
||||
|
||||
g_hash_table_unref (self->item_lookup);
|
||||
|
||||
G_OBJECT_CLASS (gtk_canvas_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCanvas *self = GTK_CANVAS (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
g_value_set_object (value, self->factory);
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCanvas *self = GTK_CANVAS (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_FACTORY:
|
||||
gtk_canvas_set_factory (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_canvas_set_model (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_invalidate_allocation (GtkCanvas *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
self->viewport_valid = FALSE;
|
||||
|
||||
for (i = 0; i < gtk_canvas_items_get_size (&self->items); i++)
|
||||
{
|
||||
GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
|
||||
|
||||
gtk_canvas_item_invalidate_allocation (ci);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkCanvas *self = GTK_CANVAS (widget);
|
||||
gboolean missing, force, success;
|
||||
gsize i;
|
||||
|
||||
gtk_canvas_invalidate_allocation (self);
|
||||
|
||||
self->viewport_valid = TRUE;
|
||||
gtk_canvas_box_init (&self->viewport,
|
||||
0, 0,
|
||||
width, height,
|
||||
0, 0);
|
||||
|
||||
force = FALSE;
|
||||
do
|
||||
{
|
||||
/* Try to allocate items in a loop */
|
||||
success = FALSE;
|
||||
missing = FALSE;
|
||||
|
||||
for (i = 0; i < gtk_canvas_items_get_size (&self->items); i++)
|
||||
{
|
||||
GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
|
||||
|
||||
if (gtk_canvas_item_has_allocation (ci))
|
||||
continue;
|
||||
|
||||
if (!gtk_canvas_item_allocate (ci, force))
|
||||
{
|
||||
g_assert (!force);
|
||||
missing = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
/* We didn't allocate a single widget in this loop, do something! */
|
||||
g_warning ("Could not allocate all Canvas items");
|
||||
force = TRUE;
|
||||
}
|
||||
}
|
||||
while (missing);
|
||||
|
||||
for (i = 0; i < gtk_canvas_items_get_size (&self->items); i++)
|
||||
{
|
||||
GtkCanvasItem *ci = gtk_canvas_items_get (&self->items, i);
|
||||
|
||||
gtk_canvas_item_allocate_widget (ci, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_class_init (GtkCanvasClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
widget_class->size_allocate = gtk_canvas_allocate;
|
||||
|
||||
gobject_class->dispose = gtk_canvas_dispose;
|
||||
gobject_class->finalize = gtk_canvas_finalize;
|
||||
gobject_class->get_property = gtk_canvas_get_property;
|
||||
gobject_class->set_property = gtk_canvas_set_property;
|
||||
|
||||
/**
|
||||
* GtkCanvas:factory: (attributes org.gtk.Property.get=gtk_canvas_get_factory org.gtk.Property.set=gtk_canvas_set_factory)
|
||||
*
|
||||
* The factory used to set up canvasitems from items in the model.
|
||||
*/
|
||||
properties[PROP_FACTORY] =
|
||||
g_param_spec_object ("factory", NULL, NULL,
|
||||
GTK_TYPE_LIST_ITEM_FACTORY,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCanvas:model: (attributes org.gtk.Property.get=gtk_canvas_get_model org.gtk.Property.set=gtk_canvas_set_model)
|
||||
*
|
||||
* The model with the items to display
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
g_param_spec_object ("model", NULL, NULL,
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("canvas"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_init (GtkCanvas *self)
|
||||
{
|
||||
self->item_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_new:
|
||||
* @model: (nullable) (transfer full): the model to use
|
||||
* @factory: (nullable) (transfer full): The factory to populate items with
|
||||
*
|
||||
* Creates a new `GtkCanvas` that uses the given @factory for
|
||||
* mapping items to widgets.
|
||||
*
|
||||
* The function takes ownership of the
|
||||
* arguments, so you can write code like
|
||||
* ```c
|
||||
* canvas = gtk_canvas_new (create_model (),
|
||||
* gtk_builder_list_item_factory_new_from_resource ("/resource.ui"));
|
||||
* ```
|
||||
*
|
||||
* Returns: a new `GtkCanvas` using the given @model and @factory
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_canvas_new (GListModel *model,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
GtkWidget *result;
|
||||
|
||||
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
|
||||
g_return_val_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory), NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_CANVAS,
|
||||
"factory", factory,
|
||||
"model", model,
|
||||
NULL);
|
||||
|
||||
g_clear_object (&model);
|
||||
g_clear_object (&factory);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_set_factory: (attributes org.gtk.Method.set_property=factory)
|
||||
* @self: a `GtkCanvas`
|
||||
* @factory: (nullable) (transfer none): the factory to use
|
||||
*
|
||||
* Sets the `GtkListItemFactory` to use for populating canvas items.
|
||||
*/
|
||||
void
|
||||
gtk_canvas_set_factory (GtkCanvas *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
guint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_CANVAS (self));
|
||||
g_return_if_fail (factory == NULL || GTK_IS_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (self->factory == factory)
|
||||
return;
|
||||
|
||||
n_items = self->model ? g_list_model_get_n_items (G_LIST_MODEL (self->model)) : 0;
|
||||
gtk_canvas_remove_items (self, 0, n_items);
|
||||
|
||||
g_set_object (&self->factory, factory);
|
||||
gtk_canvas_items_splice (&self->items, 0, n_items, FALSE, NULL, n_items);
|
||||
|
||||
gtk_canvas_add_items (self, 0, n_items);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_get_factory: (attributes org.gtk.Method.get_property=factory)
|
||||
* @self: a `GtkCanvas`
|
||||
*
|
||||
* Gets the factory that's currently used to populate canvas items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The factory in use
|
||||
*/
|
||||
GtkListItemFactory *
|
||||
gtk_canvas_get_factory (GtkCanvas *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_set_model: (attributes org.gtk.Method.set_property=model)
|
||||
* @self: a `GtkCanvas`
|
||||
* @model: (nullable) (transfer none): the model to use
|
||||
*
|
||||
* Sets the model containing the items to populate the canvas with.
|
||||
*/
|
||||
void
|
||||
gtk_canvas_set_model (GtkCanvas *self,
|
||||
GListModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CANVAS (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
gtk_canvas_clear_model (self);
|
||||
|
||||
if (model)
|
||||
{
|
||||
guint added;
|
||||
|
||||
self->model = g_object_ref (model);
|
||||
|
||||
g_signal_connect (model,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_canvas_items_changed_cb),
|
||||
self);
|
||||
|
||||
added = g_list_model_get_n_items (G_LIST_MODEL (model));
|
||||
gtk_canvas_items_splice (&self->items, 0, gtk_canvas_items_get_size (&self->items), FALSE, NULL, added);
|
||||
gtk_canvas_add_items (self, 0, added);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_canvas_items_clear (&self->items);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_get_model: (attributes org.gtk.Method.get_property=model)
|
||||
* @self: a `GtkCanvas`
|
||||
*
|
||||
* Gets the model that's currently used for the displayed items.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The model in use
|
||||
*/
|
||||
GListModel *
|
||||
gtk_canvas_get_model (GtkCanvas *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_lookup_item:
|
||||
* @self: a `GtkCanvas`
|
||||
* @item: an item in the model
|
||||
*
|
||||
* Gets the `GtkCanvasItem` that manages the given item.
|
||||
* If the item is not part of the model, %NULL will be returned.
|
||||
*
|
||||
* The resulting canvas item will return @item from a call to
|
||||
* [method@Gtk.CanvasItem.get_item].
|
||||
*
|
||||
* During addition of multiple items, this function will work
|
||||
* but may return potentially uninitialized canvasitems when the
|
||||
* factory has not run on them yet.
|
||||
* During item removal, all removed items can not be queried with
|
||||
* this function, even if the factory has not unbound the yet.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The canvasitem for item
|
||||
**/
|
||||
GtkCanvasItem *
|
||||
gtk_canvas_lookup_item (GtkCanvas *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
|
||||
g_return_val_if_fail (G_IS_OBJECT (item), NULL);
|
||||
|
||||
return g_hash_table_lookup (self->item_lookup, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_get_viewport:
|
||||
* @self: a `GtkCanvas`
|
||||
*
|
||||
* Gets the viewport of the canvas. If no viewport is available,
|
||||
* in particular if it has not been determined during size
|
||||
* allocation, %NULL is returned.
|
||||
*
|
||||
* Returns: The viewport
|
||||
**/
|
||||
const GtkCanvasBox *
|
||||
gtk_canvas_get_viewport (GtkCanvas *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS (self), NULL);
|
||||
|
||||
if (!self->viewport_valid)
|
||||
return NULL;
|
||||
|
||||
return &self->viewport;
|
||||
}
|
61
gtk/gtkcanvas.h
Normal file
61
gtk/gtkcanvas.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CANVAS_H__
|
||||
#define __GTK_CANVAS_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtkwidget.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CANVAS (gtk_canvas_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCanvas, gtk_canvas, GTK, CANVAS, GtkWidget)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_canvas_new (GListModel *children,
|
||||
GtkListItemFactory *factory);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_set_model (GtkCanvas *self,
|
||||
GListModel *children);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_canvas_get_model (GtkCanvas *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_set_factory (GtkCanvas *self,
|
||||
GtkListItemFactory *factory);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkListItemFactory * gtk_canvas_get_factory (GtkCanvas *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkCanvasItem * gtk_canvas_lookup_item (GtkCanvas *self,
|
||||
gpointer item);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkCanvasBox * gtk_canvas_get_viewport (GtkCanvas *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CANVAS_H__ */
|
81
gtk/gtkcanvasbox.c
Normal file
81
gtk/gtkcanvasbox.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* GtkCanvasBox:
|
||||
*
|
||||
* `GtkCanvasBox` describes an axis-aligned rectangular box inside
|
||||
* a `GtkCanvas`.
|
||||
*
|
||||
* A box can have no size and be just a single point.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcanvasbox.h"
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GtkCanvasBox, gtk_canvas_box,
|
||||
gtk_canvas_box_copy,
|
||||
gtk_canvas_box_free)
|
||||
|
||||
GtkCanvasBox *
|
||||
gtk_canvas_box_copy (const GtkCanvasBox *self)
|
||||
{
|
||||
GtkCanvasBox *copy;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
copy = g_slice_dup (GtkCanvasBox, self);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_box_free (GtkCanvasBox *self)
|
||||
{
|
||||
g_slice_free (GtkCanvasBox, self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_box_init (GtkCanvasBox *self,
|
||||
float point_x,
|
||||
float point_y,
|
||||
float width,
|
||||
float height,
|
||||
float origin_horizontal,
|
||||
float origin_vertical)
|
||||
{
|
||||
self->point.x = point_x;
|
||||
self->point.y = point_y;
|
||||
self->size.width = width;
|
||||
self->size.height = height;
|
||||
self->origin.horizontal = origin_horizontal;
|
||||
self->origin.vertical = origin_vertical;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_box_to_rect (const GtkCanvasBox *self,
|
||||
graphene_rect_t *rect)
|
||||
{
|
||||
rect->size = self->size;
|
||||
rect->origin = GRAPHENE_POINT_INIT (self->point.x - self->origin.horizontal * self->size.width,
|
||||
self->point.y - self->origin.vertical * self->size.height);
|
||||
graphene_rect_normalize (rect);
|
||||
}
|
71
gtk/gtkcanvasbox.h
Normal file
71
gtk/gtkcanvasbox.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_CANVAS_BOX_H__
|
||||
#define __GTK_CANVAS_BOX_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <graphene.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkOrigin GtkOrigin;
|
||||
|
||||
struct _GtkOrigin {
|
||||
float horizontal;
|
||||
float vertical;
|
||||
};
|
||||
|
||||
struct _GtkCanvasBox
|
||||
{
|
||||
graphene_point_t point;
|
||||
graphene_size_t size;
|
||||
GtkOrigin origin;
|
||||
};
|
||||
|
||||
#define GTK_TYPE_CANVAS_BOX (gtk_canvas_box_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_canvas_box_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkCanvasBox * gtk_canvas_box_copy (const GtkCanvasBox *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_box_free (GtkCanvasBox *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_box_init (GtkCanvasBox *self,
|
||||
float point_x,
|
||||
float point_y,
|
||||
float width,
|
||||
float height,
|
||||
float origin_horizontal,
|
||||
float origin_vertical);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_box_to_rect (const GtkCanvasBox *self,
|
||||
graphene_rect_t *rect);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BOX_H__ */
|
497
gtk/gtkcanvasitem.c
Normal file
497
gtk/gtkcanvasitem.c
Normal file
@@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcanvasitemprivate.h"
|
||||
|
||||
#include "gtkcanvas.h"
|
||||
#include "gtkcanvasbox.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkwidget.h"
|
||||
|
||||
/**
|
||||
* GtkCanvasItem:
|
||||
*
|
||||
* `GtkCanvasItem` holds all information relevant for placing a widget
|
||||
* onto the canvas.
|
||||
*/
|
||||
|
||||
struct _GtkCanvasItem
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkCanvas *canvas;
|
||||
gpointer item;
|
||||
GtkWidget *widget;
|
||||
|
||||
GtkCanvasBox bounds;
|
||||
GtkCanvasBox allocation;
|
||||
|
||||
guint has_allocation : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CANVAS,
|
||||
PROP_ITEM,
|
||||
PROP_WIDGET,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
enum {
|
||||
COMPUTE_BOUNDS,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
G_DEFINE_FINAL_TYPE (GtkCanvasItem, gtk_canvas_item, G_TYPE_OBJECT)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
gtk_canvas_item_dispose (GObject *object)
|
||||
{
|
||||
GtkCanvasItem *self = GTK_CANVAS_ITEM (object);
|
||||
|
||||
/* holds a reference */
|
||||
g_assert (self->canvas == NULL);
|
||||
/* must have been deleted in teardown */
|
||||
g_assert (self->item == NULL);
|
||||
g_assert (self->widget == NULL);
|
||||
|
||||
G_OBJECT_CLASS (gtk_canvas_item_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_item_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCanvasItem *self = GTK_CANVAS_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_CANVAS:
|
||||
g_value_set_object (value, self->canvas);
|
||||
break;
|
||||
|
||||
case PROP_ITEM:
|
||||
g_value_set_object (value, self->item);
|
||||
break;
|
||||
|
||||
case PROP_WIDGET:
|
||||
g_value_set_object (value, self->widget);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_item_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkCanvasItem *self = GTK_CANVAS_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_WIDGET:
|
||||
gtk_canvas_item_set_widget (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_item_class_init (GtkCanvasItemClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_canvas_item_dispose;
|
||||
gobject_class->get_property = gtk_canvas_item_get_property;
|
||||
gobject_class->set_property = gtk_canvas_item_set_property;
|
||||
|
||||
/**
|
||||
* GtkCanvasItem:canvas: (attributes org.gtk.Property.get=gtk_canvas_item_get_canvas org.gtk.Property.set=gtk_canvas_item_set_canvas)
|
||||
*
|
||||
* The canvas this item belongs to or %NULL if the canvas has been destroyed
|
||||
*/
|
||||
properties[PROP_CANVAS] =
|
||||
g_param_spec_object ("canvas", NULL, NULL,
|
||||
GTK_TYPE_CANVAS,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCanvasItem:item: (attributes org.gtk.Property.get=gtk_canvas_item_get_item org.gtk.Property.set=gtk_canvas_item_set_item)
|
||||
*
|
||||
* The item represented by this canvas item.
|
||||
*/
|
||||
properties[PROP_ITEM] =
|
||||
g_param_spec_object ("item", NULL, NULL,
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkCanvasItem:widget: (attributes org.gtk.Property.get=gtk_canvas_item_get_widget org.gtk.Property.set=gtk_canvas_item_set_widget)
|
||||
*
|
||||
* The widget managed.
|
||||
*/
|
||||
properties[PROP_WIDGET] =
|
||||
g_param_spec_object ("widget", NULL, NULL,
|
||||
GTK_TYPE_WIDGET,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
* GtkCanvasItem::compute-bounds
|
||||
* @self: the `GtkCanvasItem`
|
||||
* @bounds: (type Gtk.CanvasBox) (out caller-allocates): return
|
||||
* location for the bounds
|
||||
*
|
||||
* Emitted to determine the bounds for the widget of this canvasitem
|
||||
* during a size allocation cycle.
|
||||
*
|
||||
* A handler for this signal should fill @bounds with
|
||||
* the desired box to place the widget in.
|
||||
*
|
||||
* If the size depends on other items and cannot be computed yet,
|
||||
* handlers should return %FALSE and the signal will then be emitted
|
||||
* again once more items have been allocated.
|
||||
*
|
||||
* Because of that signal handlers are expected to be pure - not set
|
||||
* any properties or have other side effects - and idempotent -
|
||||
* return the same result if called multiple times in order.
|
||||
*
|
||||
* returns: %TRUE if @bounds was set successfully
|
||||
*/
|
||||
signals[COMPUTE_BOUNDS] =
|
||||
g_signal_new (I_("compute-bounds"),
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
_gtk_boolean_handled_accumulator, NULL,
|
||||
_gtk_marshal_BOOLEAN__BOXED,
|
||||
G_TYPE_BOOLEAN, 1,
|
||||
GTK_TYPE_CANVAS_BOX | G_SIGNAL_TYPE_STATIC_SCOPE);
|
||||
g_signal_set_va_marshaller (signals[COMPUTE_BOUNDS],
|
||||
G_TYPE_FROM_CLASS (gobject_class),
|
||||
_gtk_marshal_BOOLEAN__BOXEDv);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_canvas_item_init (GtkCanvasItem *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkCanvasItem *
|
||||
gtk_canvas_item_new (GtkCanvas *canvas,
|
||||
gpointer item)
|
||||
{
|
||||
GtkCanvasItem *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_CANVAS_ITEM,
|
||||
NULL);
|
||||
|
||||
/* no reference, the canvas references us */
|
||||
self->canvas = canvas;
|
||||
/* transfer full */
|
||||
self->item = item;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_item_invalidate_allocation (GtkCanvasItem *self)
|
||||
{
|
||||
self->has_allocation = FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_canvas_item_allocate (GtkCanvasItem *self,
|
||||
gboolean force)
|
||||
{
|
||||
gboolean result;
|
||||
int w, h;
|
||||
|
||||
g_assert (!self->has_allocation);
|
||||
|
||||
g_signal_emit (self, signals[COMPUTE_BOUNDS], 0, &self->bounds, &result);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if (!force)
|
||||
return FALSE;
|
||||
gtk_canvas_box_init (&self->bounds, 0, 0, 0, 0, 0.5, 0.5);
|
||||
}
|
||||
|
||||
if (self->widget)
|
||||
{
|
||||
if (gtk_widget_get_request_mode (self->widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
|
||||
{
|
||||
gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, -1, &w, NULL, NULL, NULL);
|
||||
w = MAX (w, ceil (ABS (self->bounds.size.width)));
|
||||
gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, w, &h, NULL, NULL, NULL);
|
||||
h = MAX (h, ceil (ABS (self->bounds.size.height)));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_measure (self->widget, GTK_ORIENTATION_VERTICAL, -1, &h, NULL, NULL, NULL);
|
||||
h = MAX (h, ceil (ABS (self->bounds.size.height)));
|
||||
gtk_widget_measure (self->widget, GTK_ORIENTATION_HORIZONTAL, h, &w, NULL, NULL, NULL);
|
||||
w = MAX (w, ceil (ABS (self->bounds.size.width)));
|
||||
}
|
||||
|
||||
if (self->bounds.size.width >= 0)
|
||||
w = MAX (self->bounds.size.width, w);
|
||||
else
|
||||
w = MIN (self->bounds.size.width, -w);
|
||||
if (self->bounds.size.height >= 0)
|
||||
h = MAX (self->bounds.size.height, h);
|
||||
else
|
||||
h = MIN (self->bounds.size.height, -h);
|
||||
}
|
||||
else
|
||||
{
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
|
||||
gtk_canvas_box_init (&self->allocation,
|
||||
round (self->bounds.point.x - self->bounds.origin.horizontal * w)
|
||||
+ self->bounds.origin.horizontal * w,
|
||||
round (self->bounds.point.y - self->bounds.origin.vertical * h)
|
||||
+ self->bounds.origin.vertical * h,
|
||||
w, h,
|
||||
self->bounds.origin.horizontal, self->bounds.origin.vertical);
|
||||
self->has_allocation = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_item_allocate_widget (GtkCanvasItem *self,
|
||||
float dx,
|
||||
float dy)
|
||||
{
|
||||
graphene_rect_t allocation;
|
||||
GskTransform *transform;
|
||||
|
||||
if (self->widget == NULL)
|
||||
return;
|
||||
|
||||
gtk_canvas_box_to_rect (&self->allocation, &allocation);
|
||||
|
||||
transform = gsk_transform_translate (NULL,
|
||||
&GRAPHENE_POINT_INIT (
|
||||
allocation.origin.x - dx
|
||||
+ (signbit (self->allocation.size.width) ? allocation.size.width : 0),
|
||||
allocation.origin.y - dy
|
||||
+ (signbit (self->allocation.size.height) ? allocation.size.height : 0)));
|
||||
transform = gsk_transform_scale (transform,
|
||||
signbit (self->allocation.size.width) ? -1 : 1,
|
||||
signbit (self->allocation.size.height) ? -1 : 1);
|
||||
|
||||
gtk_widget_allocate (self->widget,
|
||||
allocation.size.width,
|
||||
allocation.size.height,
|
||||
-1,
|
||||
transform);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_canvas_item_has_allocation (GtkCanvasItem *self)
|
||||
{
|
||||
return self->has_allocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_get_canvas: (attributes org.gtk.Method.get_property=canvas)
|
||||
* @self: a `GtkCanvasItem`
|
||||
*
|
||||
* Gets the canvas this item belongs to.
|
||||
*
|
||||
* If the canvas has discarded this item, this property willbe set to %NULL.
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The canvas
|
||||
*/
|
||||
GtkCanvas *
|
||||
gtk_canvas_item_get_canvas (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
|
||||
|
||||
return self->canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_get_item: (attributes org.gtk.Method.get_property=item)
|
||||
* @self: a `GtkCanvasItem`
|
||||
*
|
||||
* Gets the item that is associated with this canvasitem or %NULL if the canvas has
|
||||
* discarded this canvasitem.
|
||||
*
|
||||
* Returns: (transfer none) (nullable) (type GObject): The item.
|
||||
*/
|
||||
gpointer
|
||||
gtk_canvas_item_get_item (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
|
||||
|
||||
return self->item;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_item_invalidate_bounds (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
|
||||
|
||||
if (self->canvas)
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self->canvas));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_get_bounds:
|
||||
* @self: a `GtkCanvasItem`
|
||||
*
|
||||
* Gets the bounds that are used to allocate the widget
|
||||
*
|
||||
* If the bounds are not known yet - for example when called during the size
|
||||
* allocation phase before this item has succesfully computed its bounds -
|
||||
* this function returns %NULL.
|
||||
*
|
||||
* See also gtk_canvas_item_get_allocation().
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The bounds
|
||||
*/
|
||||
const GtkCanvasBox *
|
||||
gtk_canvas_item_get_bounds (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
|
||||
|
||||
if (!self->has_allocation)
|
||||
return NULL;
|
||||
|
||||
return &self->bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_get_allocation:
|
||||
* @self: a `GtkCanvasItem`
|
||||
*
|
||||
* Gets the allocation assigned to the widget.
|
||||
*
|
||||
* If the bounds are not known yet - for example when called during the size
|
||||
* allocation phase before this item has succesfully computed its bounds -
|
||||
* this function returns %NULL.
|
||||
*
|
||||
* Compared with gtk_canvas_item_get_bounds(), this function returns the actual
|
||||
* box used to allocate the widget, which may be different from the bounds
|
||||
* to conform to its size requirements.
|
||||
*
|
||||
* Returns: (transfer none) (nullable): The allocation
|
||||
*/
|
||||
const GtkCanvasBox *
|
||||
gtk_canvas_item_get_allocation (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
|
||||
|
||||
if (!self->has_allocation)
|
||||
return NULL;
|
||||
|
||||
return &self->allocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_set_widget: (attributes org.gtk.Method.set_property=widget)
|
||||
* @self: a `GtkCanvasItem`
|
||||
* @widget: (nullable) (transfer none): the widget to use
|
||||
*
|
||||
* Sets the widget to be displayed by this item.
|
||||
*/
|
||||
void
|
||||
gtk_canvas_item_set_widget (GtkCanvasItem *self,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_CANVAS_ITEM (self));
|
||||
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
|
||||
|
||||
if (self->widget == widget)
|
||||
return;
|
||||
|
||||
if (self->widget)
|
||||
{
|
||||
if (self->canvas)
|
||||
gtk_widget_unparent (self->widget);
|
||||
g_object_unref (self->widget);
|
||||
}
|
||||
|
||||
self->widget = g_object_ref_sink (widget);
|
||||
|
||||
if (self->canvas)
|
||||
{
|
||||
/* FIXME: Put in right spot */
|
||||
gtk_widget_set_parent (widget, GTK_WIDGET (self->canvas));
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WIDGET]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_canvas_item_get_widget: (attributes org.gtk.Method.get_property=widget)
|
||||
* @self: a `GtkCanvasItem`
|
||||
*
|
||||
* Gets the widget that's currently displayed by this canvasitem
|
||||
*
|
||||
* Returns: (nullable) (transfer none): The widget in use
|
||||
*/
|
||||
GtkWidget *
|
||||
gtk_canvas_item_get_widget (GtkCanvasItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_CANVAS_ITEM (self), NULL);
|
||||
|
||||
return self->widget;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_item_setup (GtkCanvasItem *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
gtk_list_item_factory_setup (factory, G_OBJECT (self), TRUE, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_canvas_item_teardown (GtkCanvasItem *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
gtk_list_item_factory_teardown (factory, G_OBJECT (self), TRUE, NULL, NULL);
|
||||
}
|
59
gtk/gtkcanvasitem.h
Normal file
59
gtk/gtkcanvasitem.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CANVAS_ITEM_H__
|
||||
#define __GTK_CANVAS_ITEM_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CANVAS_ITEM (gtk_canvas_item_get_type ())
|
||||
|
||||
/* GtkCanvasItem */
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCanvasItem, gtk_canvas_item, GTK, CANVAS_ITEM, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkCanvas * gtk_canvas_item_get_canvas (GtkCanvasItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_canvas_item_get_item (GtkCanvasItem *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_item_set_widget (GtkCanvasItem *self,
|
||||
GtkWidget *widget);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_canvas_item_get_widget (GtkCanvasItem *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_item_invalidate_bounds (GtkCanvasItem *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkCanvasBox * gtk_canvas_item_get_bounds (GtkCanvasItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkCanvasBox * gtk_canvas_item_get_allocation (GtkCanvasItem *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CANVAS_ITEM_H__ */
|
28
gtk/gtkcanvasitemprivate.h
Normal file
28
gtk/gtkcanvasitemprivate.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __GTK_CANVAS_ITEM_PRIVATE_H__
|
||||
#define __GTK_CANVAS_ITEM_PRIVATE_H__
|
||||
|
||||
#include "gtkcanvasitem.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GtkCanvasItem * gtk_canvas_item_new (GtkCanvas *canvas,
|
||||
gpointer item);
|
||||
|
||||
void gtk_canvas_item_invalidate_allocation (GtkCanvasItem *self);
|
||||
gboolean gtk_canvas_item_allocate (GtkCanvasItem *self,
|
||||
gboolean force);
|
||||
void gtk_canvas_item_allocate_widget (GtkCanvasItem *self,
|
||||
float dx,
|
||||
float dy);
|
||||
gboolean gtk_canvas_item_has_allocation (GtkCanvasItem *self);
|
||||
|
||||
void gtk_canvas_item_clear_canvas (GtkCanvasItem *self);
|
||||
|
||||
void gtk_canvas_item_setup (GtkCanvasItem *self,
|
||||
GtkListItemFactory *factory);
|
||||
void gtk_canvas_item_teardown (GtkCanvasItem *self,
|
||||
GtkListItemFactory *factory);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CANVAS_ITEM_PRIVATE_H__ */
|
91
gtk/gtkcanvaslayout.h
Normal file
91
gtk/gtkcanvaslayout.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright © 2022 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.1 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CANVAS_H__
|
||||
#define __GTK_CANVAS_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklayoutmanager.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _GtkPosition {
|
||||
float relative;
|
||||
float absolute;
|
||||
};
|
||||
|
||||
#define GTK_TYPE_CANVAS_LAYOUT (gtk_canvas_layout_get_type ())
|
||||
#define GTK_TYPE_CANVAS_LAYOUT_CHILD (gtk_canvas_layout_child_get_type ())
|
||||
|
||||
/* GtkCanvasLayout */
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCanvasLayout, gtk_canvas_layout, GTK, CANVAS_LAYOUT, GtkLayoutManager)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkLayoutManager * gtk_canvas_layout_new (void);
|
||||
|
||||
/* GtkCanvasLayoutChild */
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkCanvasLayoutChild, gtk_canvas_layout_child, GTK, CANVAS_LAYOUT_CHILD, GtkLayoutChild)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_x (GtkCanvasLayoutChild *self,
|
||||
const GtkPosition *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkPosition * gtk_canvas_layout_child_get_x (GtkCanvasLayoutChild *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_y (GtkCanvasLayoutChild *self,
|
||||
const GtkPosition *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkPosition * gtk_canvas_layout_child_get_y (GtkCanvasLayoutChild *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_origin_x (GtkCanvasLayoutChild *self,
|
||||
const GtkPosition *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkPosition * gtk_canvas_layout_child_get_origin_x (GtkCanvasLayoutChild *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_origin_y (GtkCanvasLayoutChild *self,
|
||||
const GtkPosition *position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GtkPosition * gtk_canvas_layout_child_get_origin_y (GtkCanvasLayoutChild *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_transform (GtkCanvasLayoutChild *self,
|
||||
GskTransform *transform);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const GskTransform * gtk_canvas_layout_child_get_transform (GtkCanvasLayoutChild *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_hpolicy (GtkCanvasLayoutChild *self,
|
||||
GtkScrollablePolicy policy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkScrollablePolicy gtk_canvas_layout_child_get_hpolicy (GtkCanvasLayoutChild *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_canvas_layout_child_set_vpolicy (GtkCanvasLayoutChild *self,
|
||||
GtkScrollablePolicy policy);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkScrollablePolicy gtk_canvas_layout_child_get_vpolicy (GtkCanvasLayoutChild *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CANVAS_H__ */
|
@@ -37,6 +37,9 @@ typedef struct _GtkAdjustment GtkAdjustment;
|
||||
typedef struct _GtkBitset GtkBitset;
|
||||
typedef struct _GtkBuilder GtkBuilder;
|
||||
typedef struct _GtkBuilderScope GtkBuilderScope;
|
||||
typedef struct _GtkCanvas GtkCanvas;
|
||||
typedef struct _GtkCanvasBox GtkCanvasBox;
|
||||
typedef struct _GtkCanvasItem GtkCanvasItem;
|
||||
typedef struct _GtkCssStyleChange GtkCssStyleChange;
|
||||
typedef struct _GtkEventController GtkEventController;
|
||||
typedef struct _GtkGesture GtkGesture;
|
||||
|
@@ -182,6 +182,9 @@ gtk_public_sources = files([
|
||||
'gtkbuilderscope.c',
|
||||
'gtkbutton.c',
|
||||
'gtkcalendar.c',
|
||||
'gtkcanvas.c',
|
||||
'gtkcanvasbox.c',
|
||||
'gtkcanvasitem.c',
|
||||
'gtkcellarea.c',
|
||||
'gtkcellareabox.c',
|
||||
'gtkcellareacontext.c',
|
||||
@@ -473,6 +476,9 @@ gtk_public_headers = files([
|
||||
'gtkbuilderscope.h',
|
||||
'gtkbutton.h',
|
||||
'gtkcalendar.h',
|
||||
'gtkcanvas.h',
|
||||
'gtkcanvasbox.h',
|
||||
'gtkcanvasitem.h',
|
||||
'gtkcenterbox.h',
|
||||
'gtkcenterlayout.h',
|
||||
'gtkcellarea.h',
|
||||
|
Reference in New Issue
Block a user