| #include <gtk/gtk.h> |
| |
| typedef struct |
| { |
| GtkWidget parent_instance; |
| GtkWidget *child; |
| float scale; |
| float angle; |
| } GtkZoom; |
| |
| typedef struct |
| { |
| GtkWidgetClass parent_class; |
| } GtkZoomClass; |
| |
| enum { |
| PROP_0, |
| PROP_CHILD, |
| PROP_SCALE, |
| PROP_ANGLE, |
| NUM_PROPERTIES |
| }; |
| |
| static GParamSpec *props[NUM_PROPERTIES] = { NULL, }; |
| |
| GType gtk_zoom_get_type (void); |
| |
| G_DEFINE_TYPE (GtkZoom, gtk_zoom, GTK_TYPE_WIDGET) |
| |
| static void |
| gtk_zoom_init (GtkZoom *zoom) |
| { |
| zoom->child = NULL; |
| zoom->scale = 1.0; |
| zoom->angle = 0.0; |
| } |
| |
| static void |
| gtk_zoom_dispose (GObject *object) |
| { |
| GtkZoom *zoom = (GtkZoom *)object; |
| |
| g_clear_pointer (&zoom->child, gtk_widget_unparent); |
| |
| G_OBJECT_CLASS (gtk_zoom_parent_class)->dispose (object); |
| } |
| |
| static void |
| update_transform (GtkZoom *zoom) |
| { |
| GtkLayoutManager *manager; |
| GtkLayoutChild *child; |
| GskTransform *transform; |
| graphene_rect_t bounds; |
| int w, h; |
| int x, y; |
| |
| manager = gtk_widget_get_layout_manager (GTK_WIDGET (zoom)); |
| child = gtk_layout_manager_get_layout_child (manager, zoom->child); |
| |
| w = gtk_widget_get_width (GTK_WIDGET (zoom)); |
| h = gtk_widget_get_height (GTK_WIDGET (zoom)); |
| |
| if (!gtk_widget_compute_bounds (GTK_WIDGET (zoom->child), GTK_WIDGET (zoom->child), &bounds)) |
| return; |
| |
| x = bounds.size.width; |
| y = bounds.size.height; |
| |
| transform = NULL; |
| transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (w/2, h/2)); |
| transform = gsk_transform_scale (transform, zoom->scale, zoom->scale); |
| transform = gsk_transform_rotate (transform, zoom->angle); |
| transform = gsk_transform_translate (transform, &GRAPHENE_POINT_INIT (-x/2, -y/2)); |
| gtk_fixed_layout_child_set_transform (GTK_FIXED_LAYOUT_CHILD (child), transform); |
| gsk_transform_unref (transform); |
| } |
| |
| static void |
| gtk_zoom_set_scale (GtkZoom *zoom, |
| float scale) |
| { |
| |
| if (zoom->scale == scale) |
| return; |
| |
| zoom->scale = scale; |
| |
| update_transform (zoom); |
| |
| g_object_notify_by_pspec (G_OBJECT (zoom), props[PROP_SCALE]); |
| |
| gtk_widget_queue_resize (GTK_WIDGET (zoom)); |
| } |
| |
| static void |
| gtk_zoom_set_angle (GtkZoom *zoom, |
| float angle) |
| { |
| |
| if (zoom->angle == angle) |
| return; |
| |
| zoom->angle = angle; |
| |
| update_transform (zoom); |
| |
| g_object_notify_by_pspec (G_OBJECT (zoom), props[PROP_ANGLE]); |
| |
| gtk_widget_queue_resize (GTK_WIDGET (zoom)); |
| } |
| |
| static void |
| gtk_zoom_set_child (GtkZoom *zoom, |
| GtkWidget *child) |
| { |
| g_clear_pointer (&zoom->child, gtk_widget_unparent); |
| |
| zoom->child = child; |
| |
| if (zoom->child) |
| gtk_widget_set_parent (zoom->child, GTK_WIDGET (zoom)); |
| |
| update_transform (zoom); |
| |
| g_object_notify_by_pspec (G_OBJECT (zoom), props[PROP_CHILD]); |
| } |
| |
| static void |
| gtk_zoom_set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| GtkZoom *zoom = (GtkZoom *)object; |
| |
| switch (prop_id) |
| { |
| case PROP_SCALE: |
| gtk_zoom_set_scale (zoom, g_value_get_float (value)); |
| break; |
| |
| case PROP_ANGLE: |
| gtk_zoom_set_angle (zoom, g_value_get_float (value)); |
| break; |
| |
| case PROP_CHILD: |
| gtk_zoom_set_child (zoom, g_value_get_object (value)); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_zoom_get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec) |
| { |
| GtkZoom *zoom = (GtkZoom *)object; |
| |
| switch (prop_id) |
| { |
| case PROP_SCALE: |
| g_value_set_float (value, zoom->scale); |
| break; |
| |
| case PROP_ANGLE: |
| g_value_set_float (value, zoom->angle); |
| break; |
| |
| case PROP_CHILD: |
| g_value_set_object (value, zoom->child); |
| break; |
| |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_zoom_class_init (GtkZoomClass *class) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (class); |
| GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); |
| |
| object_class->dispose = gtk_zoom_dispose; |
| object_class->set_property = gtk_zoom_set_property; |
| object_class->get_property = gtk_zoom_get_property; |
| |
| props[PROP_SCALE] = g_param_spec_float ("scale", "", "", |
| 0.0, 100.0, 1.0, |
| G_PARAM_READWRITE); |
| |
| props[PROP_ANGLE] = g_param_spec_float ("angle", "", "", |
| 0.0, 360.0, 1.0, |
| G_PARAM_READWRITE); |
| |
| props[PROP_CHILD] = g_param_spec_object ("child", "", "", |
| GTK_TYPE_WIDGET, |
| G_PARAM_READWRITE); |
| |
| g_object_class_install_properties (object_class, NUM_PROPERTIES, props); |
| |
| gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_FIXED_LAYOUT); |
| } |
| |
| static GtkWidget * |
| gtk_zoom_new (void) |
| { |
| return g_object_new (gtk_zoom_get_type (), NULL); |
| } |
| |
| static gboolean |
| update_transform_once (GtkWidget *widget, |
| GdkFrameClock *frame_clock, |
| gpointer data) |
| { |
| static int count = 0; |
| |
| update_transform ((GtkZoom *)widget); |
| count++; |
| |
| if (count == 2) |
| return G_SOURCE_REMOVE; |
| |
| return G_SOURCE_CONTINUE; |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| GtkWindow *window; |
| GtkWidget *zoom; |
| GtkWidget *box; |
| GtkWidget *grid; |
| GtkWidget *scale; |
| GtkWidget *angle; |
| GtkWidget *child; |
| GtkAdjustment *adjustment; |
| |
| gtk_init (); |
| |
| window = GTK_WINDOW (gtk_window_new ()); |
| gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); |
| box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); |
| gtk_window_set_child (window, box); |
| |
| grid = gtk_grid_new (); |
| gtk_grid_set_column_spacing (GTK_GRID (grid), 10); |
| |
| scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 1.0, 10.0, 1.0); |
| gtk_widget_set_hexpand (scale, TRUE); |
| gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Scale"), 0, 0, 1, 1); |
| gtk_grid_attach (GTK_GRID (grid), scale, 1, 0, 1, 1); |
| |
| angle = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.0, 360.0, 1.0); |
| gtk_scale_add_mark (GTK_SCALE (angle), 90.0, GTK_POS_BOTTOM, NULL); |
| gtk_scale_add_mark (GTK_SCALE (angle), 180.0, GTK_POS_BOTTOM, NULL); |
| gtk_scale_add_mark (GTK_SCALE (angle), 270.0, GTK_POS_BOTTOM, NULL); |
| gtk_widget_set_hexpand (angle, TRUE); |
| gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Angle"), 0, 1, 1, 1); |
| gtk_grid_attach (GTK_GRID (grid), angle, 1, 1, 1, 1); |
| gtk_box_append (GTK_BOX (box), grid); |
| |
| zoom = gtk_zoom_new (); |
| gtk_widget_set_hexpand (zoom, TRUE); |
| gtk_widget_set_vexpand (zoom, TRUE); |
| gtk_box_append (GTK_BOX (box), zoom); |
| |
| adjustment = gtk_range_get_adjustment (GTK_RANGE (scale)); |
| g_object_bind_property (adjustment, "value", |
| zoom, "scale", |
| G_BINDING_DEFAULT); |
| |
| adjustment = gtk_range_get_adjustment (GTK_RANGE (angle)); |
| g_object_bind_property (adjustment, "value", |
| zoom, "angle", |
| G_BINDING_DEFAULT); |
| |
| if (argc > 1) |
| { |
| GtkBuilder *builder = gtk_builder_new (); |
| gtk_builder_add_from_file (builder, argv[1], NULL); |
| child = GTK_WIDGET (gtk_builder_get_object (builder, "child")); |
| gtk_zoom_set_child ((GtkZoom *)zoom, child); |
| g_object_unref (builder); |
| } |
| else |
| gtk_zoom_set_child ((GtkZoom *)zoom, gtk_button_new_with_label ("Click me!")); |
| |
| gtk_window_present (window); |
| |
| /* HACK to get the transform initally updated */ |
| gtk_widget_add_tick_callback (zoom, update_transform_once, NULL, NULL); |
| |
| while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0) |
| g_main_context_iteration (NULL, TRUE); |
| |
| return 0; |
| } |