static gboolean mex_column_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { MexColumnPrivate *priv = MEX_COLUMN (self)->priv; ClutterVertex v; if (!clutter_paint_volume_set_from_allocation (volume, self)) return FALSE; if (priv->adjustment) { clutter_paint_volume_get_origin (volume, &v); v.y += priv->adjustment_value, clutter_paint_volume_set_origin (volume, &v); } return TRUE; }
static void mex_column_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { GList *c; MxPadding padding; gfloat min_width = 0, nat_width = 0; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; for_height = -1; if (priv->n_items > 0) { min_width = nat_width = 0; for_height /= (gfloat)priv->n_items; for (c = priv->children; c; c = c->next) { gfloat child_min_width, child_nat_width; ClutterActor *child = c->data; clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_nat_width); if (child_min_width > min_width) min_width = child_min_width; if (child_nat_width > nat_width) nat_width = child_nat_width; } } mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = min_width + padding.left + padding.right; if (nat_width_p) *nat_width_p = nat_width + padding.left + padding.right; }
static MxFocusable * mex_column_accept_focus (MxFocusable *focusable, MxFocusHint hint) { GList *link_; MexColumn *self = MEX_COLUMN (focusable); MexColumnPrivate *priv = self->priv; focusable = NULL; switch (hint) { case MX_FOCUS_HINT_FROM_LEFT: case MX_FOCUS_HINT_FROM_RIGHT: case MX_FOCUS_HINT_PRIOR: if (priv->current_focus && (focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (priv->current_focus), hint))) break; /* If there's no prior focus, or the prior focus rejects focus, * try to just focus the first actor. */ case MX_FOCUS_HINT_FIRST: case MX_FOCUS_HINT_FROM_ABOVE: if (priv->n_items) focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (priv->children->data), hint); break; case MX_FOCUS_HINT_LAST: case MX_FOCUS_HINT_FROM_BELOW: link_ = g_list_last (priv->children); if (link_) focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (link_->data), hint); break; } return focusable; }
static void mex_column_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MxAdjustment *adjustment; MexColumn *self = MEX_COLUMN (object); switch (prop_id) { case PROP_LABEL: g_value_set_string (value, mex_column_get_label (self)); break; case PROP_PLACEHOLDER_ACTOR: g_value_set_object (value, mex_column_get_placeholder_actor (self)); break; case PROP_ICON_NAME: g_value_set_string (value, mex_column_get_icon_name (self)); break; case PROP_HADJUST: mex_column_get_adjustments (MX_SCROLLABLE (self), &adjustment, NULL); g_value_set_object (value, adjustment); break; case PROP_VADJUST: mex_column_get_adjustments (MX_SCROLLABLE (self), NULL, &adjustment); g_value_set_object (value, adjustment); break; case PROP_COLLAPSE_ON_FOCUS: g_value_set_boolean (value, mex_column_get_collapse_on_focus (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void mex_column_view_pick (ClutterActor *actor, const ClutterColor *color) { MexColumnView *self = MEX_COLUMN_VIEW (actor); MexColumnViewPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (mex_column_view_parent_class)->pick (actor, color); /* Don't pick children when we don't have focus */ if (!priv->has_focus) return; if (mex_column_is_empty (MEX_COLUMN (priv->column))) { if (priv->placeholder_actor) clutter_actor_paint (priv->placeholder_actor); } else clutter_actor_paint (priv->scroll); clutter_actor_paint (priv->header); }
static void mex_column_get_adjustments (MxScrollable *scrollable, MxAdjustment **hadjust, MxAdjustment **vadjust) { MexColumnPrivate *priv = MEX_COLUMN (scrollable)->priv; if (hadjust) *hadjust = NULL; if (!vadjust) return; if (!priv->adjustment) { *vadjust = mx_adjustment_new (); mx_scrollable_set_adjustments (scrollable, NULL, *vadjust); g_object_unref (*vadjust); } else *vadjust = priv->adjustment; }
static void mex_column_dispose (GObject *object) { MexColumn *self = MEX_COLUMN (object); MexColumnPrivate *priv = self->priv; if (priv->adjustment) { g_signal_handlers_disconnect_by_func (priv->adjustment, clutter_actor_queue_redraw, object); g_object_unref (priv->adjustment); priv->adjustment = NULL; } if (priv->header) { /* The header includes the label and icon */ clutter_actor_destroy (priv->header); priv->header = NULL; } if (priv->expand_timeline) { g_object_unref (priv->expand_timeline); priv->expand_timeline = NULL; } if (priv->placeholder_actor) { clutter_actor_unparent (priv->placeholder_actor); priv->placeholder_actor = NULL; } while (priv->children) clutter_actor_destroy (CLUTTER_ACTOR (priv->children->data)); G_OBJECT_CLASS (mex_column_parent_class)->dispose (object); }
static void mex_column_add (ClutterContainer *container, ClutterActor *actor) { MexColumn *self = MEX_COLUMN (container); MexColumnPrivate *priv = self->priv; if (priv->sort_func) priv->children = g_list_insert_sorted_with_data (priv->children, actor, (GCompareDataFunc)priv->sort_func, priv->sort_data); else priv->children = g_list_append (priv->children, actor); priv->n_items ++; /* Expand/collapse any drawer that gets added as appropriate */ if (MEX_IS_EXPANDER_BOX (actor)) { g_signal_connect (actor, "notify::open", G_CALLBACK (expander_box_open_notify), container); mex_expander_box_set_important (MEX_EXPANDER_BOX (actor), priv->has_focus); if (MEX_IS_CONTENT_BOX (actor)) { ClutterActor *tile = mex_content_box_get_tile (MEX_CONTENT_BOX (actor)); mex_tile_set_important (MEX_TILE (tile), priv->has_focus); } } clutter_actor_set_parent (actor, CLUTTER_ACTOR (self)); g_signal_emit_by_name (self, "actor-added", actor); }
static void mex_column_pick (ClutterActor *actor, const ClutterColor *color) { GList *c; gdouble value; MxPadding padding; ClutterActorBox box; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; CLUTTER_ACTOR_CLASS (mex_column_parent_class)->pick (actor, color); /* Don't pick children when we don't have focus */ if (!priv->has_focus) return; mx_widget_get_padding (MX_WIDGET (actor), &padding); clutter_actor_get_allocation_box (actor, &box); if (priv->adjustment) value = mx_adjustment_get_value (priv->adjustment); else value = 0; cogl_clip_push_rectangle (padding.left, clutter_actor_get_height (priv->header) + padding.top + value, box.x2 - box.x1 - padding.right, box.y2 - box.y1 - padding.bottom + value); for (c = priv->children; c; c = c->next) clutter_actor_paint (c->data); cogl_clip_pop (); clutter_actor_paint (priv->header); }
MexColumn * mex_column_view_get_column (MexColumnView *column) { g_return_val_if_fail (MEX_IS_COLUMN_VIEW (column), NULL); return MEX_COLUMN (column->priv->column); }
static void mex_column_allocate (ClutterActor *actor, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox child_box; MxPadding padding; GList *c; MexColumn *column = MEX_COLUMN (actor); MexColumnPrivate *priv = column->priv; CLUTTER_ACTOR_CLASS (mex_column_parent_class)->allocate (actor, box, flags); mx_widget_get_padding (MX_WIDGET (actor), &padding); child_box.x1 = padding.left; child_box.x2 = box->x2 - box->x1 - padding.right; child_box.y1 = padding.top; child_box.y2 = box->y2 - box->y1 - padding.bottom; if (priv->n_items) { /* Calculate child height multiplier */ gfloat width, pref_height, avail_height, ratio, remainder; if (!priv->adjustment) { /* Find out the height available for each actor as a ratio of * their preferred height. */ clutter_actor_get_preferred_height (actor, box->x2 - box->x1, NULL, &pref_height); pref_height -= padding.top + padding.bottom; avail_height = child_box.y2 - child_box.y1; ratio = avail_height / pref_height; } else ratio = 1; /* Allocate children */ remainder = 0; width = child_box.x2 - child_box.x1; for (c = priv->children; c; c = c->next) { gfloat min_height, nat_height, height; ClutterActor *child = c->data; clutter_actor_get_preferred_height (child, width, &min_height, &nat_height); /* Calculate the allocatable height and keep an accumulator so * when we round to a pixel, we don't end up with a lot of * empty space at the end of the actor. */ height = MAX (min_height, nat_height / ratio); remainder += (height - (gint)height); height = (gint)height; while (remainder >= 1.f) { height += 1.f; remainder -= 1.f; } /* Allocate the child */ child_box.y2 = child_box.y1 + height; clutter_actor_allocate (child, &child_box, flags); /* Set the top position of the next child box */ child_box.y1 = child_box.y2; } } /* Make sure the adjustment reflects the column's allocation */ if (priv->adjustment) { gdouble page_size = box->y2 - box->y1 - padding.top - padding.bottom; mx_adjustment_set_values (priv->adjustment, mx_adjustment_get_value (priv->adjustment), 0.0, child_box.y2 - padding.top, 1.0, page_size, page_size); } }
static void mex_column_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { GList *c; gfloat min_height, nat_height; MxPadding padding; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; mx_widget_get_padding (MX_WIDGET (actor), &padding); if (for_width >= 0) for_width = MAX (0, for_width - padding.left - padding.right); clutter_actor_get_preferred_height (priv->header, for_width, NULL, &min_height); nat_height = min_height; if (priv->n_items < 1) { gfloat min_placeholder, nat_placeholder; clutter_actor_get_preferred_height (priv->placeholder_actor, for_width, &min_placeholder, &nat_placeholder); min_height += min_placeholder; nat_height += nat_placeholder; } else { gfloat child_min_height, child_nat_height; for (c = priv->children; c; c = c->next) { ClutterActor *child = c->data; clutter_actor_get_preferred_height (child, for_width, &child_min_height, &child_nat_height); min_height += child_min_height; nat_height += child_nat_height; if (priv->adjustment) break; } } if (min_height_p) *min_height_p = min_height + padding.top + padding.bottom; if (nat_height_p) *nat_height_p += nat_height + padding.top + padding.bottom; }
static void mex_column_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { GList *c; MxPadding padding; gfloat min_width, nat_width; gfloat min_header, nat_header; gfloat min_placeholder, nat_placeholder; MexColumn *self = MEX_COLUMN (actor); MexColumnPrivate *priv = self->priv; clutter_actor_get_preferred_width (priv->header, -1, &min_header, &nat_header); if (priv->adjustment) for_height = -1; else if (for_height >= 0) { gfloat height; clutter_actor_get_preferred_height (priv->header, -1, NULL, &height); for_height = MAX (0, for_height - height); } if (priv->n_items < 1) { if (priv->placeholder_actor) { clutter_actor_get_preferred_width (priv->placeholder_actor, for_height, &min_placeholder, &nat_placeholder); min_width = MAX (min_header, min_placeholder); nat_width = MAX (min_header, nat_placeholder); } else { min_width = min_header; nat_width = nat_header; } } else { min_width = nat_width = 0; for_height /= (gfloat)priv->n_items; for (c = priv->children; c; c = c->next) { gfloat child_min_width, child_nat_width; ClutterActor *child = c->data; clutter_actor_get_preferred_width (child, for_height, &child_min_width, &child_nat_width); if (child_min_width > min_width) min_width = child_min_width; if (child_nat_width > nat_width) nat_width = child_nat_width; } if (min_header > min_width) min_width = min_header; if (min_header > nat_width) nat_width = min_header; } mx_widget_get_padding (MX_WIDGET (actor), &padding); if (min_width_p) *min_width_p = min_width + padding.left + padding.right; if (nat_width_p) *nat_width_p = nat_width + padding.left + padding.right; }
static MxFocusable * mex_column_move_focus (MxFocusable *focusable, MxFocusDirection direction, MxFocusable *from) { MxFocusHint hint; GList *link_ = NULL; MexColumn *self = MEX_COLUMN (focusable); MexColumnPrivate *priv = self->priv; focusable = NULL; if ((ClutterActor *)from == priv->header) { if (((direction == MX_FOCUS_DIRECTION_NEXT) || (direction == MX_FOCUS_DIRECTION_DOWN)) && priv->n_items) { hint = (direction == MX_FOCUS_DIRECTION_NEXT) ? MX_FOCUS_HINT_FIRST : MX_FOCUS_HINT_FROM_ABOVE; if ((focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (priv->children->data), hint))) { priv->current_focus = priv->children->data; return focusable; } } else return NULL; } link_ = g_list_find (priv->children, from); if (!link_) return NULL; switch (direction) { case MX_FOCUS_DIRECTION_PREVIOUS: case MX_FOCUS_DIRECTION_UP: hint = (direction == MX_FOCUS_DIRECTION_PREVIOUS) ? MX_FOCUS_HINT_LAST : MX_FOCUS_HINT_FROM_BELOW; link_ = g_list_previous (link_); if (!link_) { if ((focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (priv->header), hint))) priv->current_focus = priv->header; } else if ((focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (link_->data), hint))) priv->current_focus = link_->data; break; case MX_FOCUS_DIRECTION_NEXT: case MX_FOCUS_DIRECTION_DOWN: hint = (direction == MX_FOCUS_DIRECTION_NEXT) ? MX_FOCUS_HINT_FIRST : MX_FOCUS_HINT_FROM_ABOVE; link_ = g_list_next (link_); if (link_ && (focusable = mx_focusable_accept_focus ( MX_FOCUSABLE (link_->data), hint))) priv->current_focus = link_->data; break; case MX_FOCUS_DIRECTION_OUT: if (from && (clutter_actor_get_parent (CLUTTER_ACTOR (from)) == CLUTTER_ACTOR (self))) priv->current_focus = CLUTTER_ACTOR (from); break; default: break; } return focusable; }