/** * _st_actor_get_preferred_height: * @actor: a #ClutterActor * @for_width: as with clutter_actor_get_preferred_height() * @x_fill: %TRUE if @actor will fill its allocation horizontally * @min_height_p: as with clutter_actor_get_preferred_height() * @natural_height_p: as with clutter_actor_get_preferred_height() * * Like clutter_actor_get_preferred_height(), but if @x_fill is * %FALSE, then it will compute a height request based on the * assumption that @actor will be given an allocation no wider than * its natural width. */ void _st_actor_get_preferred_height (ClutterActor *actor, gfloat for_width, gboolean x_fill, gfloat *min_height_p, gfloat *natural_height_p) { if (!x_fill && for_width != -1) { ClutterRequestMode mode; gfloat natural_width; mode = clutter_actor_get_request_mode (actor); if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (actor, -1, NULL, &natural_width); if (for_width > natural_width) for_width = natural_width; } } clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p); }
/** * _st_actor_get_preferred_width: * @actor: a #ClutterActor * @for_height: as with clutter_actor_get_preferred_width() * @y_fill: %TRUE if @actor will fill its allocation vertically * @min_width_p: as with clutter_actor_get_preferred_width() * @natural_width_p: as with clutter_actor_get_preferred_width() * * Like clutter_actor_get_preferred_width(), but if @y_fill is %FALSE, * then it will compute a width request based on the assumption that * @actor will be given an allocation no taller than its natural * height. */ void _st_actor_get_preferred_width (ClutterActor *actor, gfloat for_height, gboolean y_fill, gfloat *min_width_p, gfloat *natural_width_p) { if (!y_fill && for_height != -1) { ClutterRequestMode mode; gfloat natural_height; mode = clutter_actor_get_request_mode (actor); if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { clutter_actor_get_preferred_height (actor, -1, NULL, &natural_height); if (for_height > natural_height) for_height = natural_height; } } clutter_actor_get_preferred_width (actor, for_height, min_width_p, natural_width_p); }
static void st_scroll_view_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox content_box, child_box; ClutterActorClass *parent_parent_class; gfloat avail_width, avail_height, sb_width, sb_height; gboolean hscrollbar_visible, vscrollbar_visible; StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); /* Chain up to the parent's parent class * * We do this because we do not want StBin to allocate the child, as we * give it a different allocation later, depending on whether the scrollbars * are visible */ parent_parent_class = g_type_class_peek_parent (st_scroll_view_parent_class); CLUTTER_ACTOR_CLASS (parent_parent_class)-> allocate (actor, box, flags); 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; if (clutter_actor_get_request_mode (actor) == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1); sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), sb_width); } else { sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), -1); sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), sb_height); } /* Determine what scrollbars are visible. The basic idea of the * handling of an automatic scrollbars is that we start off with the * assumption that we don't need any scrollbars, see if that works, * and if not add horizontal and vertical scrollbars until we are no * longer overflowing. */ if (priv->child) { gfloat child_min_width; gfloat child_min_height; clutter_actor_get_preferred_width (priv->child, -1, &child_min_width, NULL); if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) { if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) { /* Pass one, try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height; hscrollbar_visible = child_min_width > avail_width - (vscrollbar_visible ? sb_width : 0); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); /* Pass two - if we needed a vertical scrollbar, get a new preferred height */ if (vscrollbar_visible) { clutter_actor_get_preferred_height (priv->child, MAX (avail_width - sb_width, 0), &child_min_height, NULL); hscrollbar_visible = child_min_width > avail_width - sb_width; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; /* try without a vertical scrollbar */ clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL); vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0); } } else { vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) hscrollbar_visible = child_min_width > avail_height - (vscrollbar_visible ? 0 : sb_width); else hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; } } else { hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; } /* Whether or not we show the scrollbars, if the scrollbars are visible * actors, we need to give them some allocation, so we unconditionally * give them the "right" allocation; that might overlap the child when * the scrollbars are not visible, but it doesn't matter because we * don't include them in pick or paint. */ /* Vertical scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1; child_box.x2 = content_box.x1 + sb_width; } else { child_box.x1 = content_box.x2 - sb_width; child_box.x2 = content_box.x2; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - (hscrollbar_visible ? sb_height : 0); clutter_actor_allocate (priv->vscroll, &child_box, flags); } /* Horizontal scrollbar */ if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll)) { if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + (vscrollbar_visible ? sb_width : 0); child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - (vscrollbar_visible ? sb_width : 0); } child_box.y1 = content_box.y2 - sb_height; child_box.y2 = content_box.y2; clutter_actor_allocate (priv->hscroll, &child_box, flags); } /* Now fold visibility into the scrollbar sizes to simplify the rest * of the computations. */ if (!hscrollbar_visible) sb_height = 0; if (!vscrollbar_visible) sb_width = 0; /* Child */ if (st_widget_get_direction (ST_WIDGET (actor)) == ST_TEXT_DIRECTION_RTL) { child_box.x1 = content_box.x1 + sb_width; child_box.x2 = content_box.x2; } else { child_box.x1 = content_box.x1; child_box.x2 = content_box.x2 - sb_width; } child_box.y1 = content_box.y1; child_box.y2 = content_box.y2 - sb_height; if (priv->child) clutter_actor_allocate (priv->child, &child_box, flags); if (priv->hscrollbar_visible != hscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->hscrollbar_visible = hscrollbar_visible; g_object_notify (G_OBJECT (actor), "hscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } if (priv->vscrollbar_visible != vscrollbar_visible) { g_object_freeze_notify (G_OBJECT (actor)); priv->vscrollbar_visible = vscrollbar_visible; g_object_notify (G_OBJECT (actor), "vscrollbar-visible"); g_object_thaw_notify (G_OBJECT (actor)); } }
static void mx_stack_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { GList *c; ClutterActorBox avail_space; MxStackPrivate *priv = MX_STACK (actor)->priv; CLUTTER_ACTOR_CLASS (mx_stack_parent_class)->allocate (actor, box, flags); mx_widget_get_available_area (MX_WIDGET (actor), box, &avail_space); memcpy (&priv->allocation, box, sizeof (priv->allocation)); for (c = priv->children; c; c = c->next) { gboolean x_fill, y_fill, fit, crop; MxAlign x_align, y_align; ClutterActor *child = c->data; ClutterActorBox child_box = avail_space; if (!CLUTTER_ACTOR_IS_VISIBLE (child)) continue; clutter_container_child_get (CLUTTER_CONTAINER (actor), child, "x-fill", &x_fill, "y-fill", &y_fill, "x-align", &x_align, "y-align", &y_align, "fit", &fit, "crop", &crop, NULL); /* when "crop" is set, fit and fill properties are ignored */ if (crop) { gfloat available_height, available_width; gfloat natural_width, natural_height; gfloat ratio_width, ratio_height, ratio_child; available_width = avail_space.x2 - avail_space.x1; available_height = avail_space.y2 - avail_space.y1; clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width, &natural_height); ratio_child = natural_width / natural_height; ratio_width = available_width / natural_width; ratio_height = available_height / natural_height; if (ratio_width > ratio_height) { natural_width = available_width; natural_height = natural_width / ratio_child; } else { natural_height = available_height; natural_width = ratio_child * natural_height; } child_box.x1 = (available_width - natural_width) / 2; child_box.y1 = (available_height - natural_height) / 2; child_box.x2 = natural_width; child_box.y2 = natural_height; clutter_actor_allocate (child, &child_box, flags); continue; } /* when "fit" is set, fill properties are ignored */ if (fit) { gfloat available_height, available_width, width, height; gfloat min_width, natural_width, min_height, natural_height; ClutterRequestMode request_mode; available_height = avail_space.y2 - avail_space.y1; available_width = avail_space.x2 - avail_space.x1; request_mode = clutter_actor_get_request_mode (child); if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, available_height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, available_width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); } child_box.x1 = 0; child_box.y1 = 0; switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)(available_width / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)(available_height / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.x2 = child_box.x1 + width; child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); continue; } /* Adjust the available space when not filling, otherwise * actors that support width-for-height or height-for-width * allocation won't shrink correctly. */ if (!x_fill) { gfloat width; clutter_actor_get_preferred_width (child, -1, NULL, &width); switch (x_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.x1 += (gint)((avail_space.x2 - avail_space.x1) / 2 - width / 2); break; case MX_ALIGN_END: child_box.x1 = avail_space.x2 - width; break; } child_box.x2 = child_box.x1 + width; if (child_box.x2 > avail_space.x2) child_box.x2 = avail_space.x2; if (child_box.x1 < avail_space.x1) child_box.x1 = avail_space.x1; } if (!y_fill) { gfloat height; clutter_actor_get_preferred_height (child, -1, NULL, &height); switch (y_align) { case MX_ALIGN_START: break; case MX_ALIGN_MIDDLE: child_box.y1 += (gint)((avail_space.y2 - avail_space.y1) / 2 - height / 2); break; case MX_ALIGN_END: child_box.y1 = avail_space.y2 - height; break; } child_box.y2 = child_box.y1 + height; if (child_box.y2 > avail_space.y2) child_box.y2 = avail_space.y2; if (child_box.y1 < avail_space.y1) child_box.y1 = avail_space.y1; } mx_allocate_align_fill (child, &child_box, x_align, y_align, x_fill, y_fill); clutter_actor_allocate (child, &child_box, flags); } }
/** * _st_allocate_fill: * @parent: the parent #StWidget * @child: the child (not necessarily an #StWidget) * @childbox: total space that could be allocated to @child * @x_alignment: horizontal alignment within @childbox * @y_alignment: vertical alignment within @childbox * @x_fill: whether or not to fill @childbox horizontally * @y_fill: whether or not to fill @childbox vertically * * Given @childbox, containing the initial allocation of @child, this * adjusts the horizontal allocation if @x_fill is %FALSE, and the * vertical allocation if @y_fill is %FALSE, by: * * - reducing the allocation if it is larger than @child's natural * size. * * - adjusting the position of the child within the allocation * according to @x_alignment/@y_alignment (and flipping * @x_alignment if @parent has %ST_TEXT_DIRECTION_RTL) * * If @x_fill and @y_fill are both %TRUE, or if @child's natural size * is larger than the initial allocation in @childbox, then @childbox * will be unchanged. * * If you are allocating children with _st_allocate_fill(), you should * determine their preferred sizes using * _st_actor_get_preferred_width() and * _st_actor_get_preferred_height(), not with the corresponding * Clutter methods. */ void _st_allocate_fill (StWidget *parent, ClutterActor *child, ClutterActorBox *childbox, StAlign x_alignment, StAlign y_alignment, gboolean x_fill, gboolean y_fill) { gfloat natural_width, natural_height; gfloat min_width, min_height; gfloat child_width, child_height; gfloat available_width, available_height; ClutterRequestMode request; gdouble x_align, y_align; available_width = childbox->x2 - childbox->x1; available_height = childbox->y2 - childbox->y1; if (available_width < 0) { available_width = 0; childbox->x2 = childbox->x1; } if (available_height < 0) { available_height = 0; childbox->y2 = childbox->y1; } /* If we are filling both horizontally and vertically then we don't * need to do anything else. */ if (x_fill && y_fill) return; _st_get_align_factors (parent, x_alignment, y_alignment, &x_align, &y_align); /* The following is based on clutter_actor_get_preferred_size(), but * modified to cope with the fact that the available size may be * less than the preferred size. */ request = clutter_actor_get_request_mode (child); if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (child, -1, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (child, child_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } else { clutter_actor_get_preferred_height (child, -1, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (child, child_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } if (!x_fill) { childbox->x1 += (int)((available_width - child_width) * x_align); childbox->x2 = childbox->x1 + (int) child_width; } if (!y_fill) { childbox->y1 += (int)((available_height - child_height) * y_align); childbox->y2 = childbox->y1 + (int) child_height; } }
/* Updates the minimum number of rows and columns needed for layout */ static void _xfdashboard_scaled_table_layout_update_rows_and_columns(XfdashboardScaledTableLayout *self, ClutterContainer *inContainer) { XfdashboardScaledTableLayoutPrivate *priv; ClutterRequestMode requestMode; ClutterActorIter iter; ClutterActor *child; gint numberChildren; gint rows; gint columns; g_return_if_fail(XFDASHBOARD_IS_SCALED_TABLE_LAYOUT(self)); g_return_if_fail(CLUTTER_IS_CONTAINER(inContainer)); g_return_if_fail(CLUTTER_IS_ACTOR(inContainer)); priv=self->priv; /* Freeze notification */ g_object_freeze_notify(G_OBJECT(self)); /* Get number of visible child actors */ numberChildren=0; clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { if(CLUTTER_ACTOR_IS_VISIBLE(child)) numberChildren++; } if(numberChildren!=priv->numberChildren) { priv->numberChildren=numberChildren; g_object_notify_by_pspec(G_OBJECT(self), XfdashboardScaledTableLayoutProperties[PROP_NUMBER_CHILDREN]); } /* Get request mode to determine if more rows than colums are needed * or the opposite */ requestMode=clutter_actor_get_request_mode(CLUTTER_ACTOR(inContainer)); /* Calculate and update number of rows and columns */ if(requestMode==CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { rows=ceil(sqrt((double)priv->numberChildren)); columns=ceil((double)priv->numberChildren / (double)priv->rows); } else { columns=ceil(sqrt((double)priv->numberChildren)); rows=ceil((double)priv->numberChildren / (double)priv->columns); } if(rows!=priv->rows) { priv->rows=rows; g_object_notify_by_pspec(G_OBJECT(self), XfdashboardScaledTableLayoutProperties[PROP_ROWS]); } if(columns!=priv->columns) { priv->columns=columns; g_object_notify_by_pspec(G_OBJECT(self), XfdashboardScaledTableLayoutProperties[PROP_COLUMNS]); } /* Thaw notification */ g_object_thaw_notify(G_OBJECT(self)); }