[gnome-documents] margin-container: add first implementation of GdMarginContainer



commit 2adfd80433e38786ad1b8c766d506cd68c8d4342
Author: Cosimo Cecchi <cosimoc gnome org>
Date:   Wed Oct 26 13:52:18 2011 -0400

    margin-container: add first implementation of GdMarginContainer
    
    GdMarginContainer is a simple GtkBin subclass that applies a bounded
    margin between its child, keeping it centered in the allocated area.
    
    The rules for the container are the follow:
    - if the container can fit the child natural size request plus the max
      margin, the child is allocated its natural size and all the extra
      space that exceeds the max margins
    - if the container cannot fit the child natural size request plus the
      max margin, but it can fit its natural request plus the min margin,
      it's allocated its natural request, and space is taken from the lateral
      margins
    - if the container cannot fit the child natural size request plus the
      min margin, the min margin will be used, and space is taken from the
      natural child allocation (i.e. child will shrink).

 src/Makefile-lib.am           |    2 +
 src/lib/gd-margin-container.c |  373 +++++++++++++++++++++++++++++++++++++++++
 src/lib/gd-margin-container.h |   75 ++++++++
 3 files changed, 450 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile-lib.am b/src/Makefile-lib.am
index cef8f48..6abcc85 100644
--- a/src/Makefile-lib.am
+++ b/src/Makefile-lib.am
@@ -11,12 +11,14 @@ gdprivate_source_h = \
     lib/gd-gdata-goa-authorizer.c \
     lib/gd-gdata-goa-authorizer.h \
     lib/gd-utils.h \
+    lib/gd-margin-container.h \
     lib/gd-pdf-loader.h \
     lib/gd-two-lines-renderer.h \
     $(NULL)
 
 gdprivate_source_c = \
     lib/gd-utils.c \
+    lib/gd-margin-container.c \
     lib/gd-pdf-loader.c \
     lib/gd-two-lines-renderer.c \
     $(NULL)
diff --git a/src/lib/gd-margin-container.c b/src/lib/gd-margin-container.c
new file mode 100644
index 0000000..67cbc35
--- /dev/null
+++ b/src/lib/gd-margin-container.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#include "config.h"
+
+#include "gd-margin-container.h"
+
+G_DEFINE_TYPE_WITH_CODE (GdMarginContainer, gd_margin_container, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
+                                                NULL))
+
+struct _GdMarginContainerPrivate {
+  gint min_margin;
+  gint max_margin;
+
+  GtkOrientation orientation;
+};
+
+enum {
+  PROP_MIN_MARGIN = 1,
+  PROP_MAX_MARGIN,
+  PROP_ORIENTATION,
+  NUM_PROPERTIES
+};
+
+static void
+gd_margin_container_queue_redraw (GdMarginContainer *self)
+{
+  GtkWidget *child;
+
+  /* Make sure that the widget and children are redrawn with the new setting: */
+  child = gtk_bin_get_child (GTK_BIN (self));
+  if (child)
+    gtk_widget_queue_resize (child);
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gd_margin_container_set_orientation (GdMarginContainer *self,
+                                     GtkOrientation orientation)
+{
+  if (self->priv->orientation != orientation)
+    {
+      self->priv->orientation = orientation;
+      g_object_notify (G_OBJECT (self), "orientation");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_min_margin (GdMarginContainer *self,
+                                    gint min_margin)
+{
+  if (self->priv->min_margin != min_margin)
+    {
+      self->priv->min_margin = min_margin;
+      g_object_notify (G_OBJECT (self), "min-margin");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_max_margin (GdMarginContainer *self,
+                                    gint max_margin)
+{
+  if (self->priv->max_margin != max_margin)
+    {
+      self->priv->max_margin = max_margin;
+      g_object_notify (G_OBJECT (self), "max-margin");
+
+      gd_margin_container_queue_redraw (self);
+    }
+}
+
+static void
+gd_margin_container_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (object);
+
+  switch (property_id)
+    {
+    case PROP_MIN_MARGIN:
+      gd_margin_container_set_min_margin (self, g_value_get_int (value));
+      break;
+    case PROP_MAX_MARGIN:
+      gd_margin_container_set_max_margin (self, g_value_get_int (value));
+      break;
+    case PROP_ORIENTATION:
+      gd_margin_container_set_orientation (self, g_value_get_enum (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_margin_container_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec) 
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (object);
+
+  switch (property_id)
+    {
+    case PROP_MIN_MARGIN: 
+      g_value_set_int (value, self->priv->min_margin);
+      break;
+    case PROP_MAX_MARGIN:
+      g_value_set_int (value, self->priv->max_margin);
+      break;
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, self->priv->orientation);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gd_margin_container_size_allocate (GtkWidget *widget,
+                                   GtkAllocation *allocation)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (widget);
+  GtkWidget *child;
+  GtkAllocation child_allocation;
+  gint avail_width, avail_height;
+
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  gtk_widget_set_allocation (widget, allocation);
+
+  if (child && gtk_widget_get_visible (child))
+    {
+      gint child_nat_width;
+      gint child_nat_height;
+      gint child_width, child_height;
+      gint offset;
+
+      /* available */
+      avail_width = allocation->width;
+      avail_height = allocation->height;
+
+      if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        avail_width  = MAX (1, avail_width - 2 * self->priv->min_margin);
+      else
+        avail_height = MAX (1, avail_height - 2 * self->priv->min_margin);
+
+      if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
+	{
+	  gtk_widget_get_preferred_width (child, NULL, &child_nat_width);
+	  child_width = MIN (avail_width, child_nat_width);
+
+	  gtk_widget_get_preferred_height_for_width (child, child_width, NULL, &child_nat_height);
+	  child_height = MIN (avail_height, child_nat_height);
+
+          offset = MIN ((gint) ((avail_height - child_height) / 2), self->priv->max_margin);
+
+          if (offset > 0)
+            child_allocation.height = avail_height  - (offset * 2);
+          else
+            child_allocation.height = avail_height;
+
+          child_allocation.width = MIN (avail_width, child_nat_width);
+	}
+      else
+	{
+	  gtk_widget_get_preferred_height (child, NULL, &child_nat_height);
+	  child_height = MIN (avail_height, child_nat_height);
+
+	  gtk_widget_get_preferred_width_for_height (child, child_height, NULL, &child_nat_width);
+	  child_width = MIN (avail_width, child_nat_width);
+
+          offset = MIN ((gint) ((avail_width - child_width) / 2), self->priv->max_margin);
+
+          if (offset > 0)
+            child_allocation.width = avail_width - (offset * 2);
+          else
+            child_allocation.width = avail_width;
+
+          child_allocation.height = MIN (avail_height, child_nat_height);
+	}
+
+      child_allocation.x = offset + allocation->x;
+      child_allocation.y = (avail_height - child_allocation.height) + allocation->y;
+
+      if (self->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+        child_allocation.x += self->priv->min_margin;
+      else
+        child_allocation.y += self->priv->min_margin;
+
+      gtk_widget_size_allocate (child, &child_allocation);
+    }
+}
+
+static void
+gd_margin_container_get_preferred_size (GtkWidget *widget,
+                                        GtkOrientation orientation,
+                                        gint for_size,
+                                        gint *minimum_size,
+                                        gint *natural_size)
+{
+  GdMarginContainer *self = GD_MARGIN_CONTAINER (widget);
+  guint natural, minimum;
+  GtkWidget *child;
+
+  if (orientation == self->priv->orientation)
+    {
+      minimum = self->priv->min_margin * 2;
+      natural = self->priv->max_margin * 2;
+    }
+  else
+    {
+      minimum = 0;
+      natural = 0;
+    }
+
+  if ((child = gtk_bin_get_child (GTK_BIN (widget))) && gtk_widget_get_visible (child))
+    {
+      gint child_min, child_nat;
+
+      if (orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+	  if (for_size < 0)
+	    gtk_widget_get_preferred_width (child, &child_min, &child_nat);
+	  else
+	    {
+	      gint min_height;
+
+	      gtk_widget_get_preferred_height (child, &min_height, NULL);
+	      for_size -= 2 * self->priv->min_margin;
+
+	      gtk_widget_get_preferred_width_for_height (child, for_size, &child_min, &child_nat);
+	    }
+        }
+      else
+        {
+	  if (for_size < 0)
+	    gtk_widget_get_preferred_height (child, &child_min, &child_nat);
+	  else
+	    {
+	      gint min_width;
+
+	      gtk_widget_get_preferred_width (child, &min_width, NULL);
+	      for_size -= 2 * self->priv->min_margin;
+
+	      gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
+	    }
+        }
+
+      natural += child_nat;
+
+      if (orientation != self->priv->orientation)
+        minimum += child_min;
+    }
+
+  if (minimum_size != NULL)
+    *minimum_size = minimum;
+  if (natural_size != NULL)
+    *natural_size = natural;
+}
+
+static void
+gd_margin_container_get_preferred_width (GtkWidget *widget,
+                                         gint      *minimum_size,
+                                         gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL,
+                                          -1, minimum_size, natural_size);
+}
+
+static void
+gd_margin_container_get_preferred_height (GtkWidget *widget,
+                                          gint      *minimum_size,
+                                          gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL,
+                                          -1, minimum_size, natural_size);
+}
+
+static void 
+gd_margin_container_get_preferred_width_for_height (GtkWidget *widget,
+                                                    gint       for_size,
+                                                    gint      *minimum_size,
+                                                    gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_HORIZONTAL,
+                                          for_size, minimum_size, natural_size);
+}
+
+static void 
+gd_margin_container_get_preferred_height_for_width (GtkWidget *widget,
+                                                    gint       for_size,
+                                                    gint      *minimum_size,
+                                                    gint      *natural_size)
+{
+  gd_margin_container_get_preferred_size (widget, GTK_ORIENTATION_VERTICAL,
+                                          for_size, minimum_size, natural_size);
+}
+
+static void
+gd_margin_container_init (GdMarginContainer *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GD_TYPE_MARGIN_CONTAINER,
+                                            GdMarginContainerPrivate);
+
+  self->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+  gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
+  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (self), FALSE);
+}
+
+static void
+gd_margin_container_class_init (GdMarginContainerClass *klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
+
+  oclass->get_property = gd_margin_container_get_property;
+  oclass->set_property = gd_margin_container_set_property;
+
+  wclass->size_allocate = gd_margin_container_size_allocate;
+  wclass->get_preferred_width = gd_margin_container_get_preferred_width;
+  wclass->get_preferred_height = gd_margin_container_get_preferred_height;
+  wclass->get_preferred_width_for_height = gd_margin_container_get_preferred_width_for_height;
+  wclass->get_preferred_height_for_width = gd_margin_container_get_preferred_height_for_width;
+
+  gtk_container_class_handle_border_width (GTK_CONTAINER_CLASS (klass));
+
+  g_object_class_install_property (oclass, PROP_MIN_MARGIN,
+                                   g_param_spec_int ("min-margin",
+                                                     "Min margin",
+                                                     "Minimum margin around the child",
+                                                     0, G_MAXINT, 6,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+  g_object_class_install_property (oclass, PROP_MAX_MARGIN,
+                                   g_param_spec_int ("max-margin",
+                                                     "Max margin",
+                                                     "Maximum margin around the child",
+                                                     0, G_MAXINT, 6,
+                                                     G_PARAM_READWRITE |
+                                                     G_PARAM_CONSTRUCT));
+  g_object_class_override_property (oclass, PROP_ORIENTATION,
+                                    "orientation");
+;
+  g_type_class_add_private (klass, sizeof (GdMarginContainerPrivate));
+}
diff --git a/src/lib/gd-margin-container.h b/src/lib/gd-margin-container.h
new file mode 100644
index 0000000..97b5f92
--- /dev/null
+++ b/src/lib/gd-margin-container.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Gnome Documents is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+#ifndef _GD_MARGIN_CONTAINER_H
+#define _GD_MARGIN_CONTAINER_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GD_TYPE_MARGIN_CONTAINER gd_margin_container_get_type()
+
+#define GD_MARGIN_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainer))
+
+#define GD_MARGIN_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainerClass))
+
+#define GD_IS_MARGIN_CONTAINER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+   GD_TYPE_MARGIN_CONTAINER))
+
+#define GD_IS_MARGIN_CONTAINER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+   GD_TYPE_MARGIN_CONTAINER))
+
+#define GD_MARGIN_CONTAINER_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+   GD_TYPE_MARGIN_CONTAINER, GdMarginContainerClass))
+
+typedef struct _GdMarginContainer GdMarginContainer;
+typedef struct _GdMarginContainerClass GdMarginContainerClass;
+typedef struct _GdMarginContainerPrivate GdMarginContainerPrivate;
+
+struct _GdMarginContainer
+{
+  GtkBin parent;
+
+  GdMarginContainerPrivate *priv;
+};
+
+struct _GdMarginContainerClass
+{
+  GtkBinClass parent_class;
+};
+
+GType gd_margin_container_get_type (void) G_GNUC_CONST;
+
+GdMarginContainer *gd_margin_container_new (void);
+
+G_END_DECLS
+
+#endif /* _GD_MARGIN_CONTAINER_H */



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