static void st_box_layout_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; StAdjustment *adjustment; switch (property_id) { case PROP_VERTICAL: g_value_set_boolean (value, priv->is_vertical); break; case PROP_PACK_START: g_value_set_boolean (value, priv->is_pack_start); break; case PROP_HADJUST: scrollable_get_adjustments (ST_SCROLLABLE (object), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: scrollable_get_adjustments (ST_SCROLLABLE (object), NULL, &adjustment); g_value_set_object (value, adjustment); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } }
static void st_box_layout_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StBoxLayout *box = ST_BOX_LAYOUT (object); switch (property_id) { case PROP_VERTICAL: st_box_layout_set_vertical (box, g_value_get_boolean (value)); break; case PROP_PACK_START: st_box_layout_set_pack_start (box, g_value_get_boolean (value)); break; case PROP_HADJUST: scrollable_set_adjustments (ST_SCROLLABLE (object), g_value_get_object (value), box->priv->vadjustment); break; case PROP_VADJUST: scrollable_set_adjustments (ST_SCROLLABLE (object), box->priv->hadjustment, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } }
static gboolean st_box_layout_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { StBoxLayout *self = ST_BOX_LAYOUT (actor); gdouble x, y; if (!CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->get_paint_volume (actor, volume)) return FALSE; /* When scrolled, st_box_layout_apply_transform() includes the scroll offset * and affects paint volumes. This is right for our children, but our paint volume * is determined by our allocation and borders and doesn't scroll, so we need * to reverse-compensate here, the same as we do when painting. */ get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { ClutterVertex origin; clutter_paint_volume_get_origin (volume, &origin); origin.x += x; origin.y += y; clutter_paint_volume_set_origin (volume, &origin); } return TRUE; }
static void st_box_layout_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_width (theme_node, &for_width); if (priv->hadjustment) { /* If we're scrolled, the parent calls us with the width that * we'll actually get, which can be smaller than the minimum * width that we give our contents. */ gfloat min_width; get_content_preferred_width (self, -1, &min_width, NULL); for_width = MAX (for_width, min_width); } get_content_preferred_height (self, for_width, min_height_p, natural_height_p); st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p); }
static void st_box_layout_child_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object); StBoxLayout *box = ST_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container); switch (property_id) { case PROP_EXPAND: child->expand = g_value_get_boolean (value); break; case PROP_X_FILL: child->x_fill = g_value_get_boolean (value); break; case PROP_Y_FILL: child->y_fill = g_value_get_boolean (value); break; case PROP_X_ALIGN: child->x_align = g_value_get_enum (value); break; case PROP_Y_ALIGN: child->y_align = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } clutter_actor_queue_relayout ((ClutterActor*) box); }
static void st_box_layout_paint (ClutterActor *actor) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); GList *l, *children; gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->paint (actor); if (x != 0 || y != 0) { cogl_pop_matrix (); } children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; /* The content area forms the viewport into the scrolled contents, while * the borders and background stay in place; after drawing the borders and * background, we clip to the content area */ if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (l = children; l; l = g_list_next (l)) { ClutterActor *child = (ClutterActor*) l->data; if (CLUTTER_ACTOR_IS_VISIBLE (child)) clutter_actor_paint (child); } if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); }
static void st_box_layout_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterLayoutManager *layout = clutter_actor_get_layout_manager (actor); ClutterActorBox content_box; gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height; CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, flags); st_theme_node_get_content_box (theme_node, box, &content_box); clutter_actor_box_get_size (&content_box, &avail_width, &avail_height); clutter_layout_manager_get_preferred_width (layout, CLUTTER_CONTAINER (actor), avail_height, &min_width, &natural_width); clutter_layout_manager_get_preferred_height (layout, CLUTTER_CONTAINER (actor), MAX (avail_width, min_width), &min_height, &natural_height); /* update adjustments for scrolling */ if (priv->vadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", MAX (min_height, avail_height), "page-size", avail_height, "step-increment", avail_height / 6, "page-increment", avail_height - avail_height / 6, NULL); prev_value = st_adjustment_get_value (priv->vadjustment); st_adjustment_set_value (priv->vadjustment, prev_value); } if (priv->hadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", MAX (min_width, avail_width), "page-size", avail_width, "step-increment", avail_width / 6, "page-increment", avail_width - avail_width / 6, NULL); prev_value = st_adjustment_get_value (priv->hadjustment); st_adjustment_set_value (priv->hadjustment, prev_value); } }
static void scrollable_set_adjustments (StScrollable *scrollable, StAdjustment *hadjustment, StAdjustment *vadjustment) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (scrollable)->priv; g_object_freeze_notify (G_OBJECT (scrollable)); if (hadjustment != priv->hadjustment) { if (priv->hadjustment) { g_signal_handlers_disconnect_by_func (priv->hadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->hadjustment); } if (hadjustment) { g_object_ref (hadjustment); g_signal_connect (hadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->hadjustment = hadjustment; g_object_notify (G_OBJECT (scrollable), "hadjustment"); } if (vadjustment != priv->vadjustment) { if (priv->vadjustment) { g_signal_handlers_disconnect_by_func (priv->vadjustment, adjustment_value_notify_cb, scrollable); g_object_unref (priv->vadjustment); } if (vadjustment) { g_object_ref (vadjustment); g_signal_connect (vadjustment, "notify::value", G_CALLBACK (adjustment_value_notify_cb), scrollable); } priv->vadjustment = vadjustment; g_object_notify (G_OBJECT (scrollable), "vadjustment"); } g_object_thaw_notify (G_OBJECT (scrollable)); }
static void st_box_layout_paint (ClutterActor *actor) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; ClutterActor *child; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } st_widget_paint_background (ST_WIDGET (actor)); if (x != 0 || y != 0) { cogl_pop_matrix (); } if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; /* The content area forms the viewport into the scrolled contents, while * the borders and background stay in place; after drawing the borders and * background, we clip to the content area */ if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) clutter_actor_paint (child); if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); }
static void st_box_layout_pick (ClutterActor *actor, const ClutterColor *color) { StBoxLayout *self = ST_BOX_LAYOUT (actor); StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; ClutterActor *child; get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); cogl_translate ((int)x, (int)y, 0); } CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->pick (actor, color); if (x != 0 || y != 0) { cogl_pop_matrix (); } if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); content_box.x1 += x; content_box.y1 += y; content_box.x2 += x; content_box.y2 += y; if (priv->hadjustment || priv->vadjustment) cogl_clip_push_rectangle ((int)content_box.x1, (int)content_box.y1, (int)content_box.x2, (int)content_box.y2); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) clutter_actor_paint (child); if (priv->hadjustment || priv->vadjustment) cogl_clip_pop (); }
static void scrollable_get_adjustments (StScrollable *scrollable, StAdjustment **hadjustment, StAdjustment **vadjustment) { StBoxLayoutPrivate *priv; priv = (ST_BOX_LAYOUT (scrollable))->priv; if (hadjustment) *hadjustment = priv->hadjustment; if (vadjustment) *vadjustment = priv->vadjustment; }
static void st_box_layout_style_changed (StWidget *self) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv; StThemeNode *theme_node = st_widget_get_theme_node (self); int old_spacing = priv->spacing; double spacing; spacing = st_theme_node_get_length (theme_node, "spacing"); priv->spacing = (int)(spacing + 0.5); if (priv->spacing != old_spacing) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); ST_WIDGET_CLASS (st_box_layout_parent_class)->style_changed (self); }
static void st_box_layout_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); st_theme_node_adjust_for_height (theme_node, &for_height); get_content_preferred_width (ST_BOX_LAYOUT (actor), for_height, min_width_p, natural_width_p); st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p); }
static void st_box_layout_dispose (GObject *object) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv; if (priv->hadjustment) { g_object_unref (priv->hadjustment); priv->hadjustment = NULL; } if (priv->vadjustment) { g_object_unref (priv->vadjustment); priv->vadjustment = NULL; } G_OBJECT_CLASS (st_box_layout_parent_class)->dispose (object); }
static gboolean st_box_layout_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { StBoxLayout *self = ST_BOX_LAYOUT (actor); gdouble x, y; StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorBox allocation_box; ClutterActorBox content_box; ClutterVertex origin; /* When have an adjustment we are clipped to the content box, so base * our paint volume on that. */ if (priv->hadjustment || priv->vadjustment) { clutter_actor_get_allocation_box (actor, &allocation_box); st_theme_node_get_content_box (theme_node, &allocation_box, &content_box); origin.x = content_box.x1 - allocation_box.x1; origin.y = content_box.y1 - allocation_box.y2; origin.z = 0.f; clutter_paint_volume_set_width (volume, content_box.x2 - content_box.x1); clutter_paint_volume_set_height (volume, content_box.y2 - content_box.y1); } else if (!CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->get_paint_volume (actor, volume)) return FALSE; /* When scrolled, st_box_layout_apply_transform() includes the scroll offset * and affects paint volumes. This is right for our children, but our paint volume * is determined by our allocation and borders and doesn't scroll, so we need * to reverse-compensate here, the same as we do when painting. */ get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { clutter_paint_volume_get_origin (volume, &origin); origin.x += x; origin.y += y; clutter_paint_volume_set_origin (volume, &origin); } return TRUE; }
static void st_box_layout_apply_transform (ClutterActor *a, CoglMatrix *m) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (a)->priv; gdouble x, y; CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->apply_transform (a, m); if (priv->hadjustment) x = st_adjustment_get_value (priv->hadjustment); else x = 0; if (priv->vadjustment) y = st_adjustment_get_value (priv->vadjustment); else y = 0; cogl_matrix_translate (m, (int) -x, (int) -y, 0); }
static void st_box_layout_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); ClutterActorBox content_box; gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height; gfloat position, next_position; GList *l, *children; gint n_expand_children = 0, i; gfloat expand_amount, shrink_amount; BoxChildShrink *shrinks = NULL; gboolean flip = (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) && (!priv->is_vertical); CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->allocate (actor, box, flags); children = st_container_get_children_list (ST_CONTAINER (actor)); if (children == NULL) return; st_theme_node_get_content_box (theme_node, box, &content_box); avail_width = content_box.x2 - content_box.x1; avail_height = content_box.y2 - content_box.y1; get_content_preferred_width (ST_BOX_LAYOUT (actor), avail_height, &min_width, &natural_width); get_content_preferred_height (ST_BOX_LAYOUT (actor), MAX (avail_width, min_width), &min_height, &natural_height); /* update adjustments for scrolling */ if (priv->vadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->vadjustment), "lower", 0.0, "upper", MAX (min_height, avail_height), "page-size", avail_height, "step-increment", avail_height / 6, "page-increment", avail_height - avail_height / 6, NULL); prev_value = st_adjustment_get_value (priv->vadjustment); st_adjustment_set_value (priv->vadjustment, prev_value); } if (priv->hadjustment) { gdouble prev_value; g_object_set (G_OBJECT (priv->hadjustment), "lower", 0.0, "upper", MAX (min_width, avail_width), "page-size", avail_width, "step-increment", avail_width / 6, "page-increment", avail_width - avail_width / 6, NULL); prev_value = st_adjustment_get_value (priv->hadjustment); st_adjustment_set_value (priv->hadjustment, prev_value); } if (avail_height < min_height) { avail_height = min_height; content_box.y2 = content_box.y1 + avail_height; } if (avail_width < min_width) { avail_width = min_width; content_box.x2 = content_box.x1 + avail_width; } if (priv->is_vertical) { expand_amount = MAX (0, avail_height - natural_height); shrink_amount = MAX (0, natural_height - avail_height); } else { expand_amount = MAX (0, avail_width - natural_width); shrink_amount = MAX (0, natural_width - avail_width); } if (expand_amount > 0) { /* count the number of children with expand set to TRUE */ n_expand_children = 0; for (l = children; l; l = l->next) { ClutterActor *child = l->data; gboolean expand; if (!CLUTTER_ACTOR_IS_VISIBLE (child) || clutter_actor_get_fixed_position_set (child)) continue; clutter_container_child_get ((ClutterContainer *) actor, child, "expand", &expand, NULL); if (expand) n_expand_children++; } if (n_expand_children == 0) expand_amount = 0; } else if (shrink_amount > 0) { shrinks = compute_shrinks (ST_BOX_LAYOUT (actor), priv->is_vertical ? avail_width : avail_height, shrink_amount); } if (priv->is_vertical) position = content_box.y1; else if (flip) position = content_box.x2; else position = content_box.x1; if (priv->is_pack_start) { l = g_list_last (children); i = g_list_length (children); } else { l = children; i = 0; } gboolean firstchild = TRUE; gfloat init_padding = (avail_width/2) - (natural_width/2); while (l) { ClutterActor *child = (ClutterActor*) l->data; ClutterActorBox child_box; gfloat child_min, child_nat, child_allocated; gboolean xfill, yfill, expand, fixed; StAlign xalign, yalign; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) goto next_child; fixed = clutter_actor_get_fixed_position_set (child); if (fixed) { clutter_actor_allocate_preferred_size (child, flags); goto next_child; } clutter_container_child_get ((ClutterContainer*) actor, child, "x-fill", &xfill, "y-fill", &yfill, "x-align", &xalign, "y-align", &yalign, "expand", &expand, NULL); if (priv->is_vertical) { _st_actor_get_preferred_height (child, avail_width, xfill, &child_min, &child_nat); } else { _st_actor_get_preferred_width (child, avail_height, yfill, &child_min, &child_nat); } child_allocated = child_nat; if (expand_amount > 0 && expand) child_allocated += expand_amount / n_expand_children; else if (shrink_amount > 0) child_allocated -= shrinks[i].shrink_amount; if (flip) { next_position = position - child_allocated; if (xalign == ST_ALIGN_CENTER_SPECIAL && next_position < content_box.x1) next_position = content_box.x1; } else { next_position = position + child_allocated; if (xalign == ST_ALIGN_CENTER_SPECIAL && next_position > content_box.x2) next_position = content_box.x2; } if (priv->is_vertical) { child_box.y1 = (int)(0.5 + position); child_box.y2 = (int)(0.5 + next_position); child_box.x1 = content_box.x1; child_box.x2 = content_box.x2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } else { if (flip) { if (firstchild && xalign == ST_ALIGN_CENTER_SPECIAL) { position -= init_padding; next_position = position - child_allocated; firstchild = FALSE; } if (xalign == ST_ALIGN_CENTER_SPECIAL && position > content_box.x2) { position = content_box.x2; } child_box.x1 = (int)(0.5 + next_position); child_box.x2 = (int)(0.5 + position); } else { if (firstchild && xalign == ST_ALIGN_CENTER_SPECIAL) { position += init_padding; if (position < content_box.x1) { position = content_box.x1; } next_position = position + child_allocated; firstchild = FALSE; } if (xalign == ST_ALIGN_CENTER_SPECIAL && position < content_box.x1) { position = content_box.x1; } child_box.x1 = (int)(0.5 + position); child_box.x2 = (int)(0.5 + next_position); } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2; _st_allocate_fill (ST_WIDGET (actor), child, &child_box, xalign, yalign, xfill, yfill); clutter_actor_allocate (child, &child_box, flags); } if (flip) position = next_position - priv->spacing; else position = next_position + priv->spacing; next_child: if (priv->is_pack_start) { l = l->prev; i--; } else { l = l->next; i++; } } if (shrinks) g_free (shrinks); }