[libhandy] clamp: Implement GtkOrientable



commit f624b99f59a75c5d5a585f93cb57b6c7d73b7d7f
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Mon Jun 8 13:39:22 2020 +0200

    clamp: Implement GtkOrientable
    
    This also renames the style classes from .narrow, .medium and .wide into
    .small, .medium and .large.

 src/hdy-clamp.c | 204 ++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 155 insertions(+), 49 deletions(-)
---
diff --git a/src/hdy-clamp.c b/src/hdy-clamp.c
index 72dd48f2..b7fd731c 100644
--- a/src/hdy-clamp.c
+++ b/src/hdy-clamp.c
@@ -14,22 +14,23 @@
 
 /**
  * SECTION:hdy-clamp
- * @short_description: A container letting its child grow up to a given width.
+ * @short_description: A container constraining its child to a given size.
  * @Title: HdyClamp
  *
- * The #HdyClamp widget limits the size of the widget it contains to a given
- * maximum width. The expansion of the child from its minimum to its maximum
- * size is eased out for a smooth transition.
+ * The #HdyClamp widget constraints the size of the widget it contains to a
+ * given maximum size. It will constrain the width if it is horizontal, or the
+ * height if it is vertical. The expansion of the child from its minimum to its
+ * maximum size is eased out for a smooth transition.
  *
- * If the child requires more than the requested maximum width, it will be
- * allocated the minimum width it can fit in instead.
+ * If the child requires more than the requested maximum size, it will be
+ * allocated the minimum size it can fit in instead.
  *
  * # CSS nodes
  *
  * #HdyClamp has a single CSS node with name clamp. The node will get the style
- * classes .wide when its child reached its maximum width, .narrow when the
- * clamp allocates its full width to its child, .medium in-between, or none if
- * it didn't compute its size yet.
+ * classes .large when its child reached its maximum size, .small when the clamp
+ * allocates its full size to its child, .medium in-between, or none if it
+ * didn't compute its size yet.
  *
  * Since: 1.0
  */
@@ -40,7 +41,11 @@ enum {
   PROP_0,
   PROP_MAXIMUM_WIDTH,
   PROP_LINEAR_GROWTH_WIDTH,
-  LAST_PROP,
+
+  /* Overridden properties */
+  PROP_ORIENTATION,
+
+  LAST_PROP = PROP_LINEAR_GROWTH_WIDTH + 1,
 };
 
 struct _HdyClamp
@@ -49,11 +54,26 @@ struct _HdyClamp
 
   gint maximum_width;
   gint linear_growth_width;
+
+  GtkOrientation orientation;
 };
 
 static GParamSpec *props[LAST_PROP];
 
-G_DEFINE_TYPE (HdyClamp, hdy_clamp, GTK_TYPE_BIN)
+G_DEFINE_TYPE_WITH_CODE (HdyClamp, hdy_clamp, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+static void
+set_orientation (HdyClamp       *self,
+                 GtkOrientation  orientation)
+{
+  if (self->orientation == orientation)
+    return;
+
+  self->orientation = orientation;
+  gtk_widget_queue_resize (GTK_WIDGET (self));
+  g_object_notify (G_OBJECT (self), "orientation");
+}
 
 static void
 hdy_clamp_get_property (GObject    *object,
@@ -70,6 +90,9 @@ hdy_clamp_get_property (GObject    *object,
   case PROP_LINEAR_GROWTH_WIDTH:
     g_value_set_int (value, hdy_clamp_get_linear_growth_width (self));
     break;
+  case PROP_ORIENTATION:
+    g_value_set_enum (value, self->orientation);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   }
@@ -90,30 +113,36 @@ hdy_clamp_set_property (GObject      *object,
   case PROP_LINEAR_GROWTH_WIDTH:
     hdy_clamp_set_linear_growth_width (self, g_value_get_int (value));
     break;
+  case PROP_ORIENTATION:
+    set_orientation (self, g_value_get_enum (value));
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   }
 }
 
 /**
- * get_child_width:
+ * get_child_size:
  * @self: a #HdyClamp
- * @for_width: the width of the clamp
- * @child_minimum: the minimum width reachable by the child, and hence by @self
- * @child_maximum: the maximum width @self will ever allocate its child
- * @lower_threshold: the threshold below which @self will allocate its full width to its child
+ * @for_size: the size of the clamp
+ * @child_minimum: the minimum size reachable by the child, and hence by @self
+ * @child_maximum: the maximum size @self will ever allocate its child
+ * @lower_threshold: the threshold below which @self will allocate its full size to its child
  * @upper_threshold: the threshold up from which @self will allocate its maximum size to its child
  *
  * Measures the child's extremes, the clamp's thresholds, and returns size to
  * allocate to the child.
+ *
+ * If the clamp is horizontal, all values are widths, otherwise they are
+ * heights.
  */
 static gint
-get_child_width (HdyClamp *self,
-                 gint      for_width,
-                 gint     *child_minimum,
-                 gint     *child_maximum,
-                 gint     *lower_threshold,
-                 gint     *upper_threshold)
+get_child_size (HdyClamp *self,
+                gint      for_size,
+                gint     *child_minimum,
+                gint     *child_maximum,
+                gint     *lower_threshold,
+                gint     *upper_threshold)
 {
   GtkBin *bin = GTK_BIN (self);
   GtkWidget *child;
@@ -124,8 +153,12 @@ get_child_width (HdyClamp *self,
   if (child == NULL)
     return 0;
 
-  if (gtk_widget_get_visible (child))
-    gtk_widget_get_preferred_width (child, &min, NULL);
+  if (gtk_widget_get_visible (child)) {
+    if (self->orientation == GTK_ORIENTATION_HORIZONTAL)
+      gtk_widget_get_preferred_width (child, &min, NULL);
+    else
+      gtk_widget_get_preferred_height (child, &min, NULL);
+  }
 
   lower = MAX (MIN (self->linear_growth_width, self->maximum_width), min);
   max = MAX (lower, self->maximum_width);
@@ -141,16 +174,16 @@ get_child_width (HdyClamp *self,
   if (upper_threshold)
     *upper_threshold = upper;
 
-  if (for_width < 0)
+  if (for_size < 0)
     return 0;
 
-  if (for_width <= lower)
-    return for_width;
+  if (for_size <= lower)
+    return for_size;
 
-  if (for_width >= upper)
+  if (for_size >= upper)
     return max;
 
-  progress = (double) (for_width - lower) / (double) (upper - lower);
+  progress = (double) (for_size - lower) / (double) (upper - lower);
 
   return hdy_ease_out_cubic (progress) * amplitude + lower;
 }
@@ -167,8 +200,10 @@ hdy_clamp_measure (GtkWidget      *widget,
                    int            *minimum_baseline,
                    int            *natural_baseline)
 {
+  HdyClamp *self = HDY_CLAMP (widget);
   GtkBin *bin = GTK_BIN (widget);
   GtkWidget *child;
+  gint child_size;
 
   if (minimum)
     *minimum = 0;
@@ -183,13 +218,30 @@ hdy_clamp_measure (GtkWidget      *widget,
   if (!(child && gtk_widget_get_visible (child)))
     return;
 
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
-    gtk_widget_get_preferred_width (child, minimum, natural);
-  else {
-    gint child_width = get_child_width (HDY_CLAMP (widget), for_size, NULL, NULL, NULL, NULL);
+  if (orientation == GTK_ORIENTATION_HORIZONTAL) {
+    if (self->orientation == orientation) {
+      gtk_widget_get_preferred_width (child, minimum, natural);
+
+      return;
+    }
+
+    child_size = get_child_size (HDY_CLAMP (widget), for_size, NULL, NULL, NULL, NULL);
+
+    gtk_widget_get_preferred_width_for_height (child,
+                                               child_size,
+                                               minimum,
+                                               natural);
+  } else {
+    if (self->orientation == orientation) {
+      gtk_widget_get_preferred_height (child, minimum, natural);
+
+      return;
+    }
+
+    child_size = get_child_size (HDY_CLAMP (widget), for_size, NULL, NULL, NULL, NULL);
 
     gtk_widget_get_preferred_height_and_baseline_for_width (child,
-                                                            child_width,
+                                                            child_size,
                                                             minimum,
                                                             natural,
                                                             minimum_baseline,
@@ -197,6 +249,26 @@ hdy_clamp_measure (GtkWidget      *widget,
   }
 }
 
+static GtkSizeRequestMode
+hdy_clamp_get_request_mode (GtkWidget *widget)
+{
+  HdyClamp *self = HDY_CLAMP (widget);
+
+  return self->orientation == GTK_ORIENTATION_HORIZONTAL ?
+    GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH :
+    GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+static void
+hdy_clamp_get_preferred_width_for_height (GtkWidget *widget,
+                                          gint       height,
+                                          gint      *minimum,
+                                          gint      *natural)
+{
+  hdy_clamp_measure (widget, GTK_ORIENTATION_HORIZONTAL, height,
+                     minimum, natural, NULL, NULL);
+}
+
 static void
 hdy_clamp_get_preferred_width (GtkWidget *widget,
                                gint      *minimum,
@@ -218,6 +290,16 @@ hdy_clamp_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
                      minimum, natural, minimum_baseline, natural_baseline);
 }
 
+static void
+hdy_clamp_get_preferred_height_for_width (GtkWidget *widget,
+                                          gint       width,
+                                          gint      *minimum,
+                                          gint      *natural)
+{
+  hdy_clamp_measure (widget, GTK_ORIENTATION_VERTICAL, width,
+                     minimum, natural, NULL, NULL);
+}
+
 static void
 hdy_clamp_get_preferred_height (GtkWidget *widget,
                                 gint      *minimum,
@@ -238,39 +320,56 @@ hdy_clamp_size_allocate (GtkWidget     *widget,
   GtkWidget *child;
   GtkStyleContext *context = gtk_widget_get_style_context (widget);
   gint child_maximum = 0, lower_threshold = 0;
+  gint child_clamped_size;
 
   gtk_widget_set_allocation (widget, allocation);
 
   child = gtk_bin_get_child (bin);
   if (!(child && gtk_widget_get_visible (child))) {
-    gtk_style_context_remove_class (context, "narrow");
+    gtk_style_context_remove_class (context, "small");
     gtk_style_context_remove_class (context, "medium");
-    gtk_style_context_remove_class (context, "wide");
+    gtk_style_context_remove_class (context, "large");
 
     return;
   }
 
-  child_allocation.width = get_child_width (self, allocation->width, NULL, &child_maximum, &lower_threshold, 
NULL);
-  child_allocation.height = allocation->height;
+  if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
+    child_allocation.width = get_child_size (self, allocation->width, NULL, &child_maximum, 
&lower_threshold, NULL);
+    child_allocation.height = allocation->height;
 
-  if (child_allocation.width >= child_maximum) {
-    gtk_style_context_remove_class (context, "narrow");
+    child_clamped_size = child_allocation.width;
+  }
+  else {
+    child_allocation.width = allocation->width;
+    child_allocation.height = get_child_size (self, allocation->height, NULL, &child_maximum, 
&lower_threshold, NULL);
+
+    child_clamped_size = child_allocation.height;
+  }
+
+  if (child_clamped_size >= child_maximum) {
+    gtk_style_context_remove_class (context, "small");
     gtk_style_context_remove_class (context, "medium");
-    gtk_style_context_add_class (context, "wide");
-  } else if (child_allocation.width <= lower_threshold) {
-    gtk_style_context_add_class (context, "narrow");
+    gtk_style_context_add_class (context, "large");
+  } else if (child_clamped_size <= lower_threshold) {
+    gtk_style_context_add_class (context, "small");
     gtk_style_context_remove_class (context, "medium");
-    gtk_style_context_remove_class (context, "wide");
+    gtk_style_context_remove_class (context, "large");
   } else {
-    gtk_style_context_remove_class (context, "narrow");
+    gtk_style_context_remove_class (context, "small");
     gtk_style_context_add_class (context, "medium");
-    gtk_style_context_remove_class (context, "wide");
+    gtk_style_context_remove_class (context, "large");
   }
 
   if (!gtk_widget_get_has_window (widget)) {
-    /* This allways center the child vertically. */
-    child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
-    child_allocation.y = allocation->y;
+    /* This allways center the child on the side of the orientation. */
+
+    if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
+      child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
+      child_allocation.y = allocation->y;
+    } else {
+      child_allocation.x = allocation->x;
+      child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
+    }
   }
   else {
     child_allocation.x = 0;
@@ -291,13 +390,20 @@ hdy_clamp_class_init (HdyClampClass *klass)
   object_class->get_property = hdy_clamp_get_property;
   object_class->set_property = hdy_clamp_set_property;
 
+  widget_class->get_request_mode = hdy_clamp_get_request_mode;
   widget_class->get_preferred_width = hdy_clamp_get_preferred_width;
+  widget_class->get_preferred_width_for_height = hdy_clamp_get_preferred_width_for_height;
   widget_class->get_preferred_height = hdy_clamp_get_preferred_height;
+  widget_class->get_preferred_height_for_width = hdy_clamp_get_preferred_height_for_width;
   widget_class->get_preferred_height_and_baseline_for_width = 
hdy_clamp_get_preferred_height_and_baseline_for_width;
   widget_class->size_allocate = hdy_clamp_size_allocate;
 
   gtk_container_class_handle_border_width (container_class);
 
+  g_object_class_override_property (object_class,
+                                    PROP_ORIENTATION,
+                                    "orientation");
+
   /**
    * HdyClamp:maximum_width:
    *


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