/* 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)); }
/* snap size, as affected by resizing lower right corner, * will need extension if other corners are to be supported, * it seems possible to do all needed alignments through * simple workarounds when only snapping for lower right). */ static void snap_size (ClutterActor *actor, gfloat in_width, gfloat in_height, gfloat *out_width, gfloat *out_height) { *out_width = in_width; *out_height = in_height; ClutterActor *parent; parent = clutter_actor_get_parent (actor); if (CLUTTER_IS_CONTAINER (parent)) { gfloat in_x = clutter_actor_get_x (actor); gfloat in_y = clutter_actor_get_y (actor); gfloat in_end_x = in_x + in_width; gfloat in_end_y = in_y + in_height; gfloat best_x = 0; gfloat best_x_diff = 4096; gfloat best_y = 0; gfloat best_y_diff = 4096; GList *children, *c; children = clutter_container_get_children (CLUTTER_CONTAINER (parent)); hor_pos = 0; ver_pos = 0; /* We only search our siblings for snapping... * perhaps we should search more. */ for (c=children; c; c = c->next) { gfloat this_x = clutter_actor_get_x (c->data); gfloat this_width = clutter_actor_get_width (c->data); gfloat this_height = clutter_actor_get_height (c->data); gfloat this_end_x = this_x + this_width; gfloat this_y = clutter_actor_get_y (c->data); gfloat this_end_y = this_y + this_height; /* skip self */ if (c->data == actor) continue; /* end aligned with start this_x this_mid_x this_end_x in_x in_mid_x in_end_x */ if (abs (this_x - in_end_x) < best_x_diff) { best_x_diff = abs (this_x - in_end_x); best_x = this_x; hor_pos=3; } if (abs (this_y - in_end_y) < best_y_diff) { best_y_diff = abs (this_y - in_end_y); best_y = this_y; ver_pos=3; } /* ends aligned this_x this_mid_x this_end_x in_x in_mid_x in_end_x */ if (abs (this_end_x - in_end_x) < best_x_diff) { best_x_diff = abs (this_end_x - in_end_x); best_x = this_end_x; hor_pos=3; } if (abs (this_end_y - in_end_y) < best_y_diff) { best_y_diff = abs (this_end_y - in_end_y); best_y = this_end_y; ver_pos=3; } } { if (best_x_diff < SNAP_THRESHOLD) { *out_width = best_x-in_x; } else { hor_pos = 0; } if (best_y_diff < SNAP_THRESHOLD) { *out_height = best_y-in_y; } else { ver_pos = 0; } } } }
/* Re-layout and allocate children of container we manage */ static void _xfdashboard_scaled_table_layout_allocate(ClutterLayoutManager *self, ClutterContainer *inContainer, const ClutterActorBox *inAllocation, ClutterAllocationFlags inFlags) { XfdashboardScaledTableLayoutPrivate *priv; gint row, col; ClutterActor *child; ClutterActorIter iter; gfloat cellWidth, cellHeight; gfloat childWidth, childHeight; gfloat scaledChildWidth, scaledChildHeight; gfloat largestWidth, largestHeight; gfloat scaleWidth, scaleHeight; gfloat aspectRatio; gfloat x, y; ClutterActorBox childAllocation; g_return_if_fail(XFDASHBOARD_IS_SCALED_TABLE_LAYOUT(self)); g_return_if_fail(CLUTTER_IS_CONTAINER(inContainer)); priv=XFDASHBOARD_SCALED_TABLE_LAYOUT(self)->priv; /* Get size of container holding children to layout and * determine size of a cell */ clutter_actor_get_size(CLUTTER_ACTOR(inContainer), &childWidth, &childHeight); cellWidth=childWidth-((priv->columns-1)*priv->columnSpacing); cellWidth=floor(cellWidth/priv->columns); cellHeight=childHeight-((priv->rows-1)*priv->rowSpacing); cellHeight=floor(cellHeight/priv->rows); /* Iterate through children and find largest one * if relative scale was set */ largestWidth=largestHeight=0.0f; if(priv->relativeScale==TRUE) { gfloat w, h; clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { if(!clutter_actor_is_visible(child)) continue; clutter_actor_get_preferred_size(child, NULL, NULL, &w, &h); if(w>largestWidth) largestWidth=w; if(h>largestHeight) largestHeight=h; } } /* Iterate through child actors and set their new allocation */ row=col=0; x=y=0.0f; clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { if(!clutter_actor_is_visible(child)) continue; /* Get natural size of actor */ clutter_actor_get_preferred_size(child, NULL, NULL, &childWidth, &childHeight); /* If either width or height is 0 then it is visually hidden and we * skip expensive calculation. This also has the nice effect that * do not perform invalid divisions by zero ;) */ if(childWidth>0.0f && childHeight>0.0f) { /* Get scale factor needed to apply to width and height. * If no relative scaling should be performed the scale is always 1.0 * otherwise it is the scale factor for this actor to the largest one. */ if(priv->relativeScale==TRUE) { /* Get scale factors */ scaleWidth=childWidth/largestWidth; scaleHeight=childHeight/largestHeight; } else scaleWidth=scaleHeight=1.0f; /* Get aspect ratio factor */ aspectRatio=childHeight/childWidth; /* Calculate new size of child */ scaledChildWidth=cellWidth*scaleWidth; scaledChildHeight=scaledChildWidth*aspectRatio; if(scaledChildHeight>cellHeight) { scaledChildHeight=cellHeight*scaleHeight; scaledChildWidth=scaledChildHeight/aspectRatio; } /* If upscaling should be prevent check if we are upscaling now */ if(priv->preventUpscaling) { if(scaledChildWidth>childWidth) { scaledChildWidth=childWidth; scaledChildHeight=childWidth*aspectRatio; } if(scaledChildHeight>childHeight) { scaledChildHeight=childHeight; scaledChildWidth=childHeight/aspectRatio; } } } else { /* Visually hidden so do not allocate any space */ scaledChildWidth=0.0f; scaledChildHeight=0.0f; } /* Set new allocation of child */ childAllocation.x1=ceil(x+((cellWidth-scaledChildWidth)/2.0f)); childAllocation.y1=ceil(y+((cellHeight-scaledChildHeight)/2.0f)); childAllocation.x2=ceil(childAllocation.x1+scaledChildWidth); childAllocation.y2=ceil(childAllocation.y1+scaledChildHeight); clutter_actor_allocate(child, &childAllocation, inFlags); /* Set up for next child */ col=(col+1) % priv->columns; if(col==0) row++; x=col*(cellWidth+priv->columnSpacing); y=row*(cellHeight+priv->rowSpacing); } }
/* Get largest minimum and natural size of all visible children * for calculation of one child and returns the number of visible ones */ static gint _xfdashboard_fill_box_layout_get_largest_sizes(XfdashboardFillBoxLayout *self, ClutterContainer *inContainer, gfloat *outMinWidth, gfloat *outNaturalWidth, gfloat *outMinHeight, gfloat *outNaturalHeight) { XfdashboardFillBoxLayoutPrivate *priv; ClutterActor *child; ClutterActorIter iter; gint numberChildren; gfloat largestMinWidth, largestNaturalWidth; gfloat largestMinHeight, largestNaturalHeight; gfloat childMinWidth, childNaturalWidth; gfloat childMinHeight, childNaturalHeight; ClutterActor *parent; gfloat parentWidth, parentHeight; gfloat aspectRatio; g_return_val_if_fail(XFDASHBOARD_IS_FILL_BOX_LAYOUT(self), 0); g_return_val_if_fail(CLUTTER_IS_CONTAINER(inContainer), 0); g_return_val_if_fail(CLUTTER_IS_ACTOR(inContainer), 0); priv=self->priv; /* Iterate through all children and determine sizes */ numberChildren=0; largestMinWidth=largestNaturalWidth=largestMinHeight=largestNaturalHeight=0.0f; clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { /* Only check visible children */ if(!clutter_actor_is_visible(child)) continue; /* Check for largest size */ clutter_actor_get_preferred_size(child, &childMinWidth, &childNaturalWidth, &childMinHeight, &childNaturalHeight); if(childMinWidth>largestMinWidth) largestMinWidth=childMinWidth; if(childNaturalWidth>largestNaturalWidth) largestNaturalWidth=childNaturalWidth; if(childMinHeight>largestMinHeight) largestMinHeight=childMinHeight; if(childNaturalHeight>largestNaturalHeight) largestNaturalHeight=childNaturalHeight; /* Count visible children */ numberChildren++; } /* Depending on orientation set sizes to fit into parent actor */ parent=clutter_actor_get_parent(CLUTTER_ACTOR(inContainer)); if(parent) { aspectRatio=1.0f; clutter_actor_get_size(CLUTTER_ACTOR(parent), &parentWidth, &parentHeight); if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { if(priv->keepAspect==TRUE) { aspectRatio=largestMinWidth/largestMinHeight; largestMinHeight=parentHeight; largestMinWidth=largestMinHeight*aspectRatio; aspectRatio=largestNaturalWidth/largestNaturalHeight; largestNaturalHeight=parentHeight; largestNaturalWidth=largestNaturalHeight*aspectRatio; } else { largestMinHeight=parentHeight; largestNaturalHeight=parentHeight; } } else { if(priv->keepAspect==TRUE) { aspectRatio=largestMinHeight/largestMinWidth; largestMinWidth=parentWidth; largestMinHeight=largestMinWidth*aspectRatio; aspectRatio=largestNaturalHeight/largestNaturalWidth; largestNaturalWidth=parentWidth; largestNaturalHeight=largestNaturalWidth*aspectRatio; } else { largestMinWidth=parentWidth; largestNaturalWidth=parentWidth; } } } /* Set return values */ if(outMinWidth) *outMinWidth=largestMinWidth; if(outNaturalWidth) *outNaturalWidth=largestNaturalWidth; if(outMinHeight) *outMinHeight=largestMinHeight; if(outNaturalHeight) *outNaturalHeight=largestNaturalHeight; /* Return number of visible children */ return(numberChildren); }
/* Re-layout and allocate children of container we manage */ static void _xfdashboard_fill_box_layout_allocate(ClutterLayoutManager *inLayoutManager, ClutterContainer *inContainer, const ClutterActorBox *inAllocation, ClutterAllocationFlags inFlags) { XfdashboardFillBoxLayout *self; XfdashboardFillBoxLayoutPrivate *priv; ClutterActor *child; ClutterActorIter iter; gfloat parentWidth, parentHeight; gfloat homogeneousSize; gfloat x, y, w, h; gfloat aspectRatio; ClutterActorBox childAllocation; g_return_if_fail(XFDASHBOARD_IS_FILL_BOX_LAYOUT(inLayoutManager)); g_return_if_fail(CLUTTER_IS_CONTAINER(inContainer)); self=XFDASHBOARD_FILL_BOX_LAYOUT(inLayoutManager); priv=self->priv; /* Get dimension of allocation */ parentWidth=clutter_actor_box_get_width(inAllocation); parentHeight=clutter_actor_box_get_height(inAllocation); /* If homogeneous determine sizes for all children */ if(priv->isHomogeneous==TRUE) { if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { _xfdashboard_fill_box_layout_get_largest_sizes(self, inContainer, NULL, &homogeneousSize, NULL, NULL); } else { _xfdashboard_fill_box_layout_get_largest_sizes(self, inContainer, NULL, NULL, NULL, &homogeneousSize); } } /* Iterate through children and set their sizes */ x=y=0.0f; clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { /* Only set sizes on visible children */ if(!clutter_actor_is_visible(child)) continue; /* Calculate and set new allocation of child */ if(priv->isHomogeneous==FALSE) { clutter_actor_get_size(CLUTTER_ACTOR(inContainer), &w, &h); if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { aspectRatio=w/h; h=parentHeight; w=h*aspectRatio; } else { aspectRatio=h/w; w=parentWidth; h=w*aspectRatio; } } else { if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { w=homogeneousSize; h=parentHeight; } else { w=parentWidth; h=homogeneousSize; } } /* Set new allocation of child */ childAllocation.x1=ceil(x); childAllocation.y1=ceil(y); childAllocation.x2=ceil(childAllocation.x1+w); childAllocation.y2=ceil(childAllocation.y1+h); clutter_actor_allocate(child, &childAllocation, inFlags); /* Set up for next child */ if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) x+=(w+priv->spacing); else y+=(h+priv->spacing); } }
/* Get minimum and natural size of all visible children */ static void _xfdashboard_fill_box_layout_get_sizes_for_all(XfdashboardFillBoxLayout *self, ClutterContainer *inContainer, gfloat *outMinWidth, gfloat *outNaturalWidth, gfloat *outMinHeight, gfloat *outNaturalHeight) { XfdashboardFillBoxLayoutPrivate *priv; ClutterActor *child; ClutterActorIter iter; gint numberChildren; gfloat minWidth, naturalWidth; gfloat minHeight, naturalHeight; gfloat childMinWidth, childNaturalWidth; gfloat childMinHeight, childNaturalHeight; ClutterActor *parent; gfloat parentWidth, parentHeight; gfloat aspectRatio; g_return_if_fail(XFDASHBOARD_IS_FILL_BOX_LAYOUT(self)); g_return_if_fail(CLUTTER_IS_CONTAINER(inContainer)); g_return_if_fail(CLUTTER_IS_ACTOR(inContainer)); priv=self->priv; /* Initialize return values */ numberChildren=0; minWidth=naturalWidth=minHeight=naturalHeight=0.0f; /* If not homogeneous then iterate through all children and determine sizes ... */ if(priv->isHomogeneous==FALSE) { /* Iterate through children and calculate sizes */ clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inContainer)); while(clutter_actor_iter_next(&iter, &child)) { /* Only get sizes of visible children */ if(!clutter_actor_is_visible(child)) continue; /* Count visible children */ numberChildren++; /* Determine sizes of visible child */ clutter_actor_get_preferred_size(child, &childMinWidth, &childNaturalWidth, &childMinHeight, &childNaturalHeight); if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { minWidth+=childMinWidth; naturalWidth+=childNaturalWidth; if(childMinHeight>minHeight) minHeight=childMinHeight; if(childNaturalHeight>naturalHeight) naturalHeight=childNaturalHeight; } else { minHeight+=childMinHeight; naturalHeight+=childNaturalHeight; if(childMinWidth>naturalWidth) minWidth=naturalWidth; if(childNaturalWidth>naturalHeight) naturalHeight=childNaturalWidth; } } } /* ... otherwise get largest minimum and natural size and add spacing */ else { /* Get number of visible children and also largest minimum * and natural size */ numberChildren=_xfdashboard_fill_box_layout_get_largest_sizes(self, inContainer, &childMinWidth, &childNaturalWidth, &childMinHeight, &childNaturalHeight); /* Multiply largest sizes with number visible children */ if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { minWidth=(numberChildren*childMinWidth); naturalWidth=(numberChildren*childNaturalWidth); minHeight=childMinHeight; naturalHeight=childNaturalHeight; } else { minWidth=childMinWidth; naturalWidth=childNaturalWidth; minHeight=(numberChildren*childMinHeight); naturalHeight=(numberChildren*childNaturalHeight); } } /* Add spacing */ if(numberChildren>0) { numberChildren--; if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { minWidth+=numberChildren*priv->spacing; naturalWidth+=numberChildren*priv->spacing; } else { minHeight+=numberChildren*priv->spacing; naturalHeight+=numberChildren*priv->spacing; } } /* Depending on orientation set sizes to fit into parent actor */ parent=clutter_actor_get_parent(CLUTTER_ACTOR(inContainer)); if(parent) { aspectRatio=1.0f; clutter_actor_get_size(CLUTTER_ACTOR(parent), &parentWidth, &parentHeight); if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) { if(priv->keepAspect==TRUE) { aspectRatio=minWidth/minHeight; minHeight=parentHeight; minWidth=minHeight*aspectRatio; aspectRatio=naturalWidth/naturalHeight; naturalHeight=parentHeight; naturalWidth=naturalHeight*aspectRatio; } else { minHeight=parentHeight; naturalHeight=parentHeight; } } else { if(priv->keepAspect==TRUE) { aspectRatio=minHeight/minWidth; minWidth=parentWidth; minHeight=minWidth*aspectRatio; aspectRatio=naturalHeight/naturalWidth; naturalWidth=parentWidth; naturalHeight=naturalWidth*aspectRatio; } else { minWidth=parentWidth; naturalWidth=parentWidth; } } } /* Set return values */ if(outMinWidth) *outMinWidth=minWidth; if(outNaturalWidth) *outNaturalWidth=naturalWidth; if(outMinHeight) *outMinHeight=minHeight; if(outNaturalHeight) *outNaturalHeight=naturalHeight; }
/** * clutter_container_child_set: * @container: a #ClutterContainer * @actor: a #ClutterActor that is a child of @container. * @first_prop: name of the first property to be set. * @...: value for the first property, followed optionally by more name/value * pairs terminated with NULL. * * Sets container specific properties on the child of a container. * * Since: 0.8 */ void clutter_container_child_set (ClutterContainer *container, ClutterActor *actor, const gchar *first_prop, ...) { GObjectClass *klass; const gchar *name; va_list var_args; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); klass = G_OBJECT_GET_CLASS (container); va_start (var_args, first_prop); name = first_prop; while (name) { GValue value = { 0, }; gchar *error = NULL; GParamSpec *pspec; pspec = clutter_container_class_find_child_property (klass, name); if (!pspec) { g_warning ("%s: Containers of type '%s' have no child " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (container), name); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Child property '%s' of the container '%s' " "is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); break; } #if GLIB_CHECK_VERSION (2, 23, 2) G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); #else g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (&value, var_args, 0, &error); #endif /* GLIB_CHECK_VERSION (2, 23, 2) */ if (error) { /* we intentionally leak the GValue because it might * be in an undefined state and calling g_value_unset() * on it might crash */ g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } container_set_child_property (container, actor, &value, pspec); g_value_unset (&value); name = va_arg (var_args, gchar*); } va_end (var_args); }
/** * clutter_layout_manager_child_get: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @first_property: the name of the first property * @Varargs: a list of property name and return location for the value pairs * * Retrieves the values for a list of properties out of the * #ClutterLayoutMeta created by @manager and attached to the * child of a @container * * Since: 1.2 */ void clutter_layout_manager_child_get (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) { ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (first_property != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); va_start (var_args, first_property); pname = first_property; while (pname) { GValue value = { 0, }; GParamSpec *pspec; gchar *error; gboolean res; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); res = layout_get_property_internal (manager, G_OBJECT (meta), pspec, &value); if (!res) { g_value_unset (&value); break; } G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); pname = va_arg (var_args, gchar*); } va_end (var_args); }