Пример #1
0
/**
 * mx_bin_allocate_child:
 * @bin: An #MxBin
 * @box: The allocation box of the parent actor.
 * @flags: #ClutterAllocationFlags, usually provided by the.
 * clutter_actor_allocate function.
 *
 * Allocates the child of an #MxBin using the width and height from @box.
 * This function should usually only be called by subclasses of #MxBin.
 *
 * This function can be used to allocate the child of an #MxBin if no special
 * allocation requirements are needed. It is similar to
 * #mx_allocate_align_fill, except that it reads the alignment, padding and
 * fill values from the #MxBin, and will call #clutter_actor_allocate on the
 * child.
 *
 */
void
mx_bin_allocate_child (MxBin                  *bin,
                       const ClutterActorBox  *box,
                       ClutterAllocationFlags  flags)
{
  MxBinPrivate *priv;

  g_return_if_fail (MX_IS_BIN (bin));

  priv = bin->priv;

  if (priv->child)
    {
      MxPadding padding;
      ClutterActorBox allocation = { 0, };

      mx_widget_get_padding (MX_WIDGET (bin), &padding);

      allocation.x1 = padding.left;
      allocation.x2 = box->x2 - box->x1 - padding.right;
      allocation.y1 = padding.top;
      allocation.y2 = box->y2 - box->y1 - padding.bottom;

      mx_allocate_align_fill (priv->child,
                              &allocation,
                              priv->x_align,
                              priv->y_align,
                              priv->x_fill,
                              priv->y_fill);

      clutter_actor_allocate (priv->child, &allocation, flags);
    }
}
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);
    }
}
Пример #3
0
static void
mx_expander_allocate (ClutterActor          *actor,
                      const ClutterActorBox *box,
                      ClutterAllocationFlags flags)
{
  MxExpanderPrivate *priv = MX_EXPANDER (actor)->priv;
  ClutterActorBox child_box;
  MxPadding padding;
  gfloat label_w, label_h;
  gfloat available_w, available_h, min_w, min_h, arrow_h, arrow_w;

  /* chain up to store allocation */
  CLUTTER_ACTOR_CLASS (mx_expander_parent_class)->allocate (actor, box, flags);

  mx_widget_get_padding (MX_WIDGET (actor), &padding);

  available_w = (box->x2 - box->x1) - padding.left - padding.right;
  available_h = (box->y2 - box->y1) - padding.top - padding.bottom;

  /* arrow */
  clutter_actor_get_preferred_width (priv->arrow, -1, NULL, &arrow_w);
  arrow_w = MIN (arrow_w, available_w);

  clutter_actor_get_preferred_height (priv->arrow, -1, NULL, &arrow_h);
  arrow_h = MIN (arrow_h, available_h);

  child_box.x1 = padding.left;
  child_box.x2 = child_box.x1 + arrow_w;
  child_box.y1 = padding.top;
  child_box.y2 = child_box.y1 + arrow_h;
  clutter_actor_allocate (priv->arrow, &child_box, flags);

  /* label */
  min_h = 0;
  min_w = 0;

  clutter_actor_get_preferred_width (priv->label,
                                     available_h, &min_w, &label_w);
  label_w = CLAMP (label_w, min_w, available_w);

  clutter_actor_get_preferred_height (priv->label,
                                      label_w, &min_h, &label_h);
  label_h = CLAMP (label_h, min_h, available_h);

  /* TODO: make a style property for padding between arrow and label */
  child_box.x1 = padding.left + arrow_w + 6.0f;
  child_box.x2 = child_box.x1 + label_w;
  child_box.y1 = padding.top;
  child_box.y2 = child_box.y1 + MAX (label_h, arrow_h);
  mx_allocate_align_fill (priv->label, &child_box, MX_ALIGN_START,
                          MX_ALIGN_MIDDLE, FALSE, FALSE);
  clutter_actor_allocate (priv->label, &child_box, flags);

  /* remove label height and spacing for child calculations */
  available_h -= MAX (label_h, arrow_h) + priv->spacing;

  /* child */
  if (priv->expanded && priv->child && CLUTTER_ACTOR_IS_VISIBLE (priv->child))
    {
      child_box.x1 = padding.left;
      child_box.x2 = child_box.x1 + available_w;
      child_box.y1 = padding.top + priv->spacing + MAX (label_h, arrow_h);
      child_box.y2 = child_box.y1 + available_h;

      clutter_actor_allocate (priv->child, &child_box, flags);
    }
}
Пример #4
0
static void
mex_tile_allocate (ClutterActor           *actor,
                   const ClutterActorBox  *box,
                   ClutterAllocationFlags  flags)
{
  MxPadding padding;
  ClutterActorBox child_box;
  gfloat available_width, available_height;
  ClutterEffect *fade;

  MexTilePrivate *priv = MEX_TILE (actor)->priv;

  CLUTTER_ACTOR_CLASS (mex_tile_parent_class)->allocate (actor, box, flags);

  mx_widget_get_padding (MX_WIDGET (actor), &padding);
  available_width = box->x2 - box->x1 - padding.left - padding.right;
  available_height = box->y2 - box->y1 - padding.top - padding.bottom;

  if (priv->child)
    {
      gfloat child_width, full_width, full_height;

      clutter_actor_get_preferred_size (priv->child, NULL, NULL,
                                        &full_width, &full_height);

      child_box.y1 = padding.top;

      if (clutter_alpha_get_alpha (priv->important_alpha) < 0.5)
        {
          child_width = full_width * (available_height / full_height);
          if (child_width > available_width)
            child_width = available_width;

          child_box.y2 = child_box.y1 + available_height;

          /* When we're in unimportant state, make sure the label
           * doesn't overlap the image.
           */
          if (available_height < full_height)
            available_width -= child_width *
              ((0.5 - clutter_alpha_get_alpha (priv->important_alpha)) * 2);
        }
      else
        {
          child_width = available_width;
          clutter_actor_set_clip_to_allocation (
            actor, (full_height > available_height));

          child_box.y2 = child_box.y1 + full_height;
        }

      child_box.x2 = box->x2 - box->x1 - padding.right;
      child_box.x1 = child_box.x2 - child_width;

      mx_allocate_align_fill (priv->child, &child_box,
                              MX_ALIGN_MIDDLE, MX_ALIGN_MIDDLE, FALSE, FALSE);
      clutter_actor_allocate (priv->child, &child_box, flags);
    }

  /* Allocate Header */
  if (priv->header_visible)
    {
      gfloat icon1_w, icon1_h, icon2_w, icon2_h, label_h, label_w, header_h;
      gfloat middle_w;

      if (priv->header_padding)
        {
          padding.top += priv->header_padding->top;
          padding.right += priv->header_padding->right;
          padding.bottom += priv->header_padding->bottom;
          padding.left += priv->header_padding->left;
        }

      clutter_actor_get_preferred_size (priv->box_layout, NULL, NULL, &label_w,
                                        &label_h);

      if (priv->icon1)
        clutter_actor_get_preferred_size (priv->icon1, NULL, NULL, &icon1_w,
                                          &icon1_h);
      else
        icon1_h = icon1_w = 0;

      if (priv->icon2)
        clutter_actor_get_preferred_size (priv->icon2, NULL, NULL, &icon2_w,
                                          &icon2_h);
      else
        icon2_h = icon2_w = 0;

      header_h = MAX (icon1_h, MAX (icon2_h, label_h));

      /* primary icon */
      if (priv->icon1)
        {
          child_box.y1 = padding.top + (header_h / 2.0) - (icon1_h / 2.0);
          child_box.x1 = padding.left;
          child_box.y2 = child_box.y1 + icon1_h;
          child_box.x2 = child_box.x1 + icon1_w;

          clutter_actor_allocate (priv->icon1, &child_box, flags);
          child_box.x1 += icon1_w + 8;
        }
      else
        child_box.x1 = padding.left;

      /* label */
      child_box.x2 = child_box.x1 + label_w;
      child_box.y1 = (int) (padding.top + (header_h / 2.0) - (label_h / 2.0));
      child_box.y2 = child_box.y1 + label_h;

      fade = clutter_actor_get_effect (priv->box_layout, "fade");

      middle_w = available_width - icon1_w - icon2_w;
      if (priv->header_padding)
        middle_w -= priv->header_padding->left + priv->header_padding->right;
      clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (fade),
                                      !(middle_w > label_w));
      mx_fade_effect_set_bounds (MX_FADE_EFFECT (fade), 0, 0, middle_w, 0);

      clutter_actor_allocate (priv->box_layout, &child_box, flags);

      /* secondary icon */
      if (priv->icon2)
        {
          child_box.x2 = (box->x2 - box->x1) - padding.right;
          child_box.x1 = child_box.x2 - icon2_w;
          child_box.y1 = padding.top + (header_h / 2.0) - (icon2_h / 2.0);
          child_box.y2 = child_box.y1 + icon2_h;

          clutter_actor_allocate (priv->icon2, &child_box, flags);
        }

      priv->header_height = header_h;
      if (priv->header_padding)
        priv->header_height += priv->header_padding->top
          + priv->header_padding->bottom;
    }
}
static void
mx_label_allocate (ClutterActor          *actor,
                   const ClutterActorBox *box,
                   ClutterAllocationFlags flags)
{
  MxLabelPrivate *priv = MX_LABEL (actor)->priv;
  gboolean label_did_fade = priv->label_should_fade;

  ClutterActorClass *parent_class;
  ClutterActorBox child_box;
  gboolean x_fill, y_fill;
  gfloat avail_width;

  parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class);
  parent_class->allocate (actor, box, flags);

  mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box);
  avail_width = child_box.x2 - child_box.x1;

  /* The default behaviour of ClutterText is to align to the
   * top-left when it gets more space than is needed. Because
   * of this behaviour, if we're aligning to the left, we can
   * assign all our horizontal space to the label without
   * measuring it (i.e. x-fill), and the same applies for
   * aligning to the top and vertical space.
   */
  x_fill = (priv->x_align == MX_ALIGN_START) ? TRUE : FALSE;
  y_fill = (priv->y_align == MX_ALIGN_START) ? TRUE : FALSE;

  mx_allocate_align_fill (priv->label, &child_box, priv->x_align,
                          priv->y_align, x_fill, y_fill);

  priv->label_should_fade = FALSE;

  if (priv->fade_out)
    {
      /* If we're fading out, make sure the label has its full width
       * allocated. This ensures that the offscreen effect has the full
       * label inside its texture.
       */
      gfloat label_width;

      clutter_actor_get_preferred_width (priv->label, -1, NULL, &label_width);

      if (label_width > avail_width)
        {
          priv->label_should_fade = TRUE;
          child_box.x2 = child_box.x1 + label_width;
        }

      mx_fade_effect_set_bounds (MX_FADE_EFFECT (priv->fade_effect),
                                 0, 0, MIN (label_width, avail_width), 0);
    }

  /* Allocate the label */
  clutter_actor_allocate (priv->label, &child_box, flags);

  if (priv->show_tooltip)
    {
      PangoLayout *layout;
      const gchar *text;

      layout = clutter_text_get_layout (CLUTTER_TEXT (priv->label));

      if (pango_layout_is_ellipsized (layout))
        text = clutter_text_get_text (CLUTTER_TEXT (priv->label));
      else
        text = NULL;

      mx_widget_set_tooltip_text (MX_WIDGET (actor), text);
    }

  /* Animate in/out the faded end of the label */
  if (label_did_fade != priv->label_should_fade)
    {
      /* Begin/reverse the fading timeline when necessary */
      if (priv->label_should_fade)
        clutter_timeline_set_direction (priv->fade_timeline,
                                        CLUTTER_TIMELINE_FORWARD);
      else
        clutter_timeline_set_direction (priv->fade_timeline,
                                        CLUTTER_TIMELINE_BACKWARD);

      if (!clutter_timeline_is_playing (priv->fade_timeline))
        clutter_timeline_rewind (priv->fade_timeline);

      clutter_timeline_start (priv->fade_timeline);
    }
}
Пример #6
0
static void
mx_table_preferred_allocate (ClutterActor          *self,
                             const ClutterActorBox *box,
                             gboolean               flags)
{
  gint row_spacing, col_spacing;
  gint i;
  MxTable *table;
  MxTablePrivate *priv;
  MxPadding padding;
  DimensionData *rows, *columns;
  ClutterActorIter iter;
  ClutterActor *child;

  table = MX_TABLE (self);
  priv = MX_TABLE (self)->priv;

  mx_widget_get_padding (MX_WIDGET (self), &padding);

  col_spacing = (priv->col_spacing);
  row_spacing = (priv->row_spacing);

  mx_table_calculate_dimensions (table, box->x2 - box->x1, box->y2 - box->y1);

  rows = &g_array_index (priv->rows, DimensionData, 0);
  columns = &g_array_index (priv->columns, DimensionData, 0);

  clutter_actor_iter_init (&iter, self);
  while (clutter_actor_iter_next (&iter, &child))
    {
      gint row, col, row_span, col_span;
      gint col_width, row_height;
      MxTableChild *meta;
      ClutterActorBox childbox;
      gint child_x, child_y;
      gdouble x_align_d, y_align_d;
      gboolean x_fill, y_fill;
      MxAlign x_align, y_align;

      meta = (MxTableChild *) clutter_container_get_child_meta (CLUTTER_CONTAINER (self), child);

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      /* get child properties */
      col = meta->col;
      row = meta->row;
      row_span = meta->row_span;
      col_span = meta->col_span;
      x_align_d = meta->x_align;
      y_align_d = meta->y_align;
      x_fill = meta->x_fill;
      y_fill = meta->y_fill;

      /* Convert to MxAlign */
      if (x_align_d < 1.0 / 3.0)
        x_align = MX_ALIGN_START;
      else if (x_align_d > 2.0 / 3.0)
        x_align = MX_ALIGN_END;
      else
        x_align = MX_ALIGN_MIDDLE;
      if (y_align_d < 1.0 / 3.0)
        y_align = MX_ALIGN_START;
      else if (y_align_d > 2.0 / 3.0)
        y_align = MX_ALIGN_END;
      else
        y_align = MX_ALIGN_MIDDLE;

      /* initialise the width and height */
      col_width = columns[col].final_size;
      row_height = rows[row].final_size;

      /* Add the widths of the spanned columns:
       *
       * First check that we have a non-zero span. Then we loop over each of
       * the columns that we're spanning but we stop short if we go past the
       * number of columns in the table. This is necessary to avoid accessing
       * uninitialised memory. We add the spacing in here too since we only
       * want to add as much spacing as times we successfully span.
       */
      if (col + col_span > priv->n_cols)
        g_warning ("MxTable: col-span exceeds number of columns");
      if (row + row_span > priv->n_rows)
        g_warning ("MxTable: row-span exceeds number of rows");

      if (col_span > 1)
        {
          for (i = col + 1; i < col + col_span && i < priv->n_cols; i++)
            {
              col_width += columns[i].final_size;
              col_width += col_spacing;
            }
        }

      /* add the height of the spanned rows */
      if (row_span > 1)
        {
          for (i = row + 1; i < row + row_span && i < priv->n_rows; i++)
            {
              row_height += rows[i].final_size;
              row_height += row_spacing;
            }
        }

      /* calculate child x */
      child_x = (int) padding.left;
      for (i = 0; i < col; i++)
        {
          if (columns[i].is_visible)
            {
              child_x += columns[i].final_size;
              child_x += col_spacing;
            }
        }

      /* calculate child y */
      child_y = (int) padding.top;
      for (i = 0; i < row; i++)
        {
          if (rows[i].is_visible)
            {
              child_y += rows[i].final_size;
              child_y += row_spacing;
            }
        }

      /* set up childbox */
      childbox.x1 = (float) child_x;
      childbox.x2 = (float) MAX (0, child_x + col_width);

      childbox.y1 = (float) child_y;
      childbox.y2 = (float) MAX (0, child_y + row_height);


      mx_allocate_align_fill (child, &childbox, x_align, y_align, x_fill, y_fill);

      clutter_actor_allocate (child, &childbox, flags);
    }
}