[gegl-gtk] GeglGtkView: Set size of widget to match node bounding box



commit 842bbef29f474a59a7de5300e1a3d9b9793fe729
Author: Jon Nordby <jononor gmail com>
Date:   Sun Oct 9 16:08:17 2011 +0200

    GeglGtkView: Set size of widget to match node bounding box
    
    Especially useful for putting the widget in a scrolled window,
    see the gegl-gtk-scroll example.

 examples/c/gegl-gtk-scroll.c    |   13 +++----
 gegl-gtk/gegl-gtk-view.c        |   20 +++++++++--
 gegl-gtk/internal/view-helper.c |   76 +++++++++++++++++++++++++++++---------
 gegl-gtk/internal/view-helper.h |    3 +-
 4 files changed, 82 insertions(+), 30 deletions(-)
---
diff --git a/examples/c/gegl-gtk-scroll.c b/examples/c/gegl-gtk-scroll.c
index 64f1e6a..ffcf9e0 100644
--- a/examples/c/gegl-gtk-scroll.c
+++ b/examples/c/gegl-gtk-scroll.c
@@ -45,7 +45,7 @@ main (gint    argc,
   /* Build graph that loads an image */
   graph = gegl_node_new ();
   node = gegl_node_new_child (graph,
-    "operation", "gegl:load", 
+    "operation", "gegl:load",
     "path", argv[1], NULL);
 
   gegl_node_process (node);
@@ -53,20 +53,17 @@ main (gint    argc,
   /* Setup */
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_title (GTK_WINDOW (window), "GEGL-GTK scrolled example");
-  
-  
+
+
   scrolled = gtk_scrolled_window_new(NULL, NULL);
 
   view = GTK_WIDGET(gegl_gtk_view_new_for_node(node));
 
-  // FIXME: should be possible for the widget to do this automatically, and take changes into account
-  gtk_widget_set_size_request(view, 500, 500);
-  
   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), view);
-  
+
   gtk_container_add (GTK_CONTAINER (window), scrolled);
 
-  g_signal_connect (window, "destroy", 
+  g_signal_connect (window, "destroy",
                     G_CALLBACK (gtk_main_quit), NULL);
   gtk_widget_show_all (window);
 
diff --git a/gegl-gtk/gegl-gtk-view.c b/gegl-gtk/gegl-gtk-view.c
index 0259eba..dde9e22 100644
--- a/gegl-gtk/gegl-gtk-view.c
+++ b/gegl-gtk/gegl-gtk-view.c
@@ -86,6 +86,8 @@ trigger_redraw(ViewHelper* priv, GeglRectangle *rect, GeglGtkView *view);
 static void
 size_allocate(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data);
 
+static void
+view_size_changed(ViewHelper* priv, GeglRectangle *rect, GeglGtkView *view);
 
 static void
 gegl_gtk_view_class_init (GeglGtkViewClass * klass)
@@ -148,7 +150,8 @@ gegl_gtk_view_init (GeglGtkView *self)
   self->priv = (GeglGtkViewPrivate *)view_helper_new();
 
   g_signal_connect(self->priv, "redraw-needed", G_CALLBACK (trigger_redraw), (gpointer)self);
-  
+  g_signal_connect(self->priv, "size-changed", G_CALLBACK (view_size_changed), (gpointer)self);
+
   g_signal_connect(self, "size-allocate", G_CALLBACK (size_allocate), NULL);
 }
 
@@ -233,13 +236,24 @@ trigger_redraw (ViewHelper *priv,
               GeglRectangle *rect,
               GeglGtkView *view)
 {
-    if (rect->width < 0 || rect->height < 0) 
+    if (rect->width < 0 || rect->height < 0)
         gtk_widget_queue_draw(GTK_WIDGET(view));
     else
         gtk_widget_queue_draw_area(GTK_WIDGET(view),
 			      rect->x, rect->y, rect->width, rect->height);
 }
 
+/* Bounding box of the node view changed */
+static void
+view_size_changed(ViewHelper* priv, GeglRectangle *rect, GeglGtkView *view)
+{
+    /* Resize the widget to fit the entire view bounding box
+     * TODO: implement a policy for this
+     * consumers should be able to have the view not autoscale at all
+     * or to have it autoscale the content to fit the size of widget */
+    gtk_widget_set_size_request(GTK_WIDGET(view), rect->width, rect->height);
+}
+
 static void
 size_allocate(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data)
 {
@@ -287,7 +301,7 @@ expose_event (GtkWidget      *widget,
   gdk_region_get_clipbox (event->region, &rect);
 
   view_helper_draw (priv, cr, &rect);
-  
+
   cairo_destroy (cr);
 
   view_helper_repaint (priv); /* Only needed due to possible allocation changes? */
diff --git a/gegl-gtk/internal/view-helper.c b/gegl-gtk/internal/view-helper.c
index 682d6d6..add98a3 100644
--- a/gegl-gtk/internal/view-helper.c
+++ b/gegl-gtk/internal/view-helper.c
@@ -28,6 +28,7 @@ G_DEFINE_TYPE (ViewHelper, view_helper, G_TYPE_OBJECT)
 enum
 {
   SIGNAL_REDRAW_NEEDED,
+  SIGNAL_SIZE_CHANGED,
   N_SIGNALS
 };
 
@@ -42,7 +43,7 @@ view_helper_class_init (ViewHelperClass * klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->finalize = finalize;
-    
+
   /* Emitted when a redraw is needed, with the area that needs redrawing. */
   view_helper_signals[SIGNAL_REDRAW_NEEDED] = g_signal_new ("redraw-needed",
                 G_TYPE_FROM_CLASS (klass),
@@ -52,17 +53,33 @@ view_helper_class_init (ViewHelperClass * klass)
                 g_cclosure_marshal_VOID__BOXED,
                 G_TYPE_NONE, 1,
                 GEGL_TYPE_RECTANGLE);
+
+  /* Emitted when the size of the view changes, with the new size. */
+  view_helper_signals[SIGNAL_SIZE_CHANGED] = g_signal_new ("size-changed",
+                G_TYPE_FROM_CLASS (klass),
+                G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+                0,
+                NULL, NULL,
+                g_cclosure_marshal_VOID__BOXED,
+                G_TYPE_NONE, 1,
+                GEGL_TYPE_RECTANGLE);
 }
 
 static void
 view_helper_init (ViewHelper *self)
 {
+  GeglRectangle invalid_rect = {0, 0, -1, -1};
+  GdkRectangle invalid_gdkrect = {0, 0, -1, -1};
+
   self->node        = NULL;
   self->x           = 0;
   self->y           = 0;
   self->scale       = 1.0;
   self->monitor_id  = 0;
   self->processor   = NULL;
+
+  self->widget_allocation = invalid_gdkrect;
+  self->view_bbox   = invalid_rect;
 }
 
 static void
@@ -71,7 +88,7 @@ finalize (GObject *gobject)
    ViewHelper *self = VIEW_HELPER(gobject);
 
   if (self->monitor_id)
-    {  
+    {
       g_source_remove (self->monitor_id);
       self->monitor_id = 0;
     }
@@ -81,8 +98,21 @@ finalize (GObject *gobject)
 
   if (self->processor)
     g_object_unref (self->processor);
-}    
+}
+
+/* Transform a rectangle from model to view coordinates. */
+static void
+model_rect_to_view_rect(ViewHelper *self, GeglRectangle *rect)
+{
+  GeglRectangle temp;
 
+  temp.x = self->scale * (rect->x) - rect->x;
+  temp.y = self->scale * (rect->y) - rect->y;
+  temp.width = ceil (self->scale * rect->width);
+  temp.height = ceil (self->scale * rect->height);
+
+  *rect = temp;
+}
 
 static void
 invalidated_event (GeglNode      *node,
@@ -107,18 +137,28 @@ task_monitor (ViewHelper *self)
 
 
 /* When the GeglNode has been computed,
- * find out which area in the view changed and emit the
- * "redraw" signal to notify it that a redraw is needed */
+ * find out if the size of the vie changed and
+ * emit the "size-changed" signal to notify view
+ * find out which area in the view was computed and emit the
+ * "redraw-needed" signal to notify it that a redraw is needed */
 static void
 computed_event (GeglNode      *node,
                 GeglRectangle *rect,
                 ViewHelper    *self)
 {
-  gint x = self->scale * (rect->x) - self->x;
-  gint y = self->scale * (rect->y) - self->y;
-  gint w = ceil (self->scale * rect->width);
-  gint h = ceil (self->scale * rect->height);
-  GeglRectangle redraw_rect = {x, y, w, h};
+  /* Notify about potential size change */
+  GeglRectangle bbox = gegl_node_get_bounding_box(node);
+  model_rect_to_view_rect(self, &bbox);
+
+  if (!gegl_rectangle_equal(&bbox, &(self->view_bbox))) {
+      self->view_bbox = bbox;
+      g_signal_emit (self, view_helper_signals[SIGNAL_SIZE_CHANGED],
+                 0, &bbox, NULL);
+  }
+
+  /* Emit redraw-needed */
+  GeglRectangle redraw_rect = *rect;
+  model_rect_to_view_rect(self, &redraw_rect);
 
   g_signal_emit (self, view_helper_signals[SIGNAL_REDRAW_NEEDED],
 	         0, &redraw_rect, NULL);
@@ -131,9 +171,9 @@ view_helper_new(void)
 }
 
 /* Draw the view of the GeglNode to the provided cairo context,
- * taking into account transformations et.c. 
+ * taking into account transformations et.c.
  * @rect the bounding box of the area to draw in view coordinates
- * 
+ *
  * For instance called by widget during the draw/expose */
 void
 view_helper_draw (ViewHelper *self, cairo_t *cr, GdkRectangle *rect)
@@ -157,9 +197,9 @@ view_helper_draw (ViewHelper *self, cairo_t *cr, GdkRectangle *rect)
                   GEGL_AUTO_ROWSTRIDE,
                   GEGL_BLIT_CACHE | (self->block ? 0 : GEGL_BLIT_DIRTY));
 
-  surface = cairo_image_surface_create_for_data (buf, 
-                                                 CAIRO_FORMAT_ARGB32, 
-                                                 roi.width, roi.height, 
+  surface = cairo_image_surface_create_for_data (buf,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 roi.width, roi.height,
                                                  roi.width*4);
   cairo_set_source_surface (cr, surface, rect->x, rect->y);
   cairo_paint (cr);
@@ -172,7 +212,7 @@ view_helper_draw (ViewHelper *self, cairo_t *cr, GdkRectangle *rect)
 void
 view_helper_set_allocation(ViewHelper *self, GdkRectangle *allocation)
 {
-    self->allocation = *allocation;
+    self->widget_allocation = *allocation;
     view_helper_repaint(self);
 }
 
@@ -185,8 +225,8 @@ view_helper_repaint (ViewHelper *self)
   roi.x = self->x / self->scale;
   roi.y = self->y / self->scale;
 
-  roi.width = ceil(self->allocation.width / self->scale+1);
-  roi.height = ceil(self->allocation.height / self->scale+1);
+  roi.width = ceil(self->widget_allocation.width / self->scale+1);
+  roi.height = ceil(self->widget_allocation.height / self->scale+1);
 
   if (self->monitor_id == 0)
     {
diff --git a/gegl-gtk/internal/view-helper.h b/gegl-gtk/internal/view-helper.h
index ec07835..51df6b5 100644
--- a/gegl-gtk/internal/view-helper.h
+++ b/gegl-gtk/internal/view-helper.h
@@ -46,7 +46,8 @@ struct _ViewHelper
   gboolean       block;    /* blocking render */
   guint          monitor_id;
   GeglProcessor *processor;
-  GdkRectangle   allocation;
+  GdkRectangle   widget_allocation; /* The allocated size of the widget */
+  GeglRectangle  view_bbox; /* Bounding box of the node, in view coordinates */
 };
 
 struct _ViewHelperClass



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]