Ejemplo n.º 1
0
/**
 * _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;
    }
}
Ejemplo n.º 2
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);
    }
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
    }
}
Ejemplo n.º 5
0
static void
scroll_bar_allocate_children (StScrollBar           *bar,
                              const ClutterActorBox *box,
                              ClutterAllocationFlags flags)
{
  StScrollBarPrivate *priv = bar->priv;
  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (bar));
  ClutterActorBox content_box, bw_box, fw_box, trough_box;
  gfloat bw_stepper_size, fw_stepper_size, min_size, natural_size;

  st_theme_node_get_content_box (theme_node, box, &content_box);

  if (priv->vertical)
    {
      gfloat width = content_box.x2 - content_box.x1;

      clutter_actor_get_preferred_height (priv->bw_stepper, width,
                                          &min_size, &natural_size);
      bw_stepper_size = MAX (min_size, natural_size);

      /* Backward stepper */
      bw_box.x1 = content_box.x1;
      bw_box.y1 = content_box.y1;
      bw_box.x2 = content_box.x2;
      bw_box.y2 = bw_box.y1 + bw_stepper_size;
      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);


      clutter_actor_get_preferred_height (priv->fw_stepper, width,
                                          &min_size, &natural_size);
      fw_stepper_size = MAX (min_size, natural_size);

      /* Forward stepper */
      fw_box.x1 = content_box.x1;
      fw_box.y1 = content_box.y2 - fw_stepper_size;
      fw_box.x2 = content_box.x2;
      fw_box.y2 = content_box.y2;
      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);

      /* Trough */
      trough_box.x1 = content_box.x1;
      trough_box.y1 = content_box.y1 + bw_stepper_size;
      trough_box.x2 = content_box.x2;
      trough_box.y2 = content_box.y2 - fw_stepper_size;
      clutter_actor_allocate (priv->trough, &trough_box, flags);

    }
  else
    {
      gfloat height = content_box.y2 - content_box.y1;

      clutter_actor_get_preferred_width (priv->bw_stepper, height,
                                         &min_size, &natural_size);
      bw_stepper_size = MAX (min_size, natural_size);

      /* Backward stepper */
      bw_box.x1 = content_box.x1;
      bw_box.y1 = content_box.y1;
      bw_box.x2 = bw_box.x1 + bw_stepper_size;
      bw_box.y2 = content_box.y2;
      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);


      clutter_actor_get_preferred_width (priv->fw_stepper, height,
                                         &min_size, &natural_size);
      fw_stepper_size = MAX (min_size, natural_size);

      /* Forward stepper */
      fw_box.x1 = content_box.x2 - fw_stepper_size;
      fw_box.y1 = content_box.y1;
      fw_box.x2 = content_box.x2;
      fw_box.y2 = content_box.y2;
      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);

      /* Trough */
      trough_box.x1 = content_box.x1 + bw_stepper_size;
      trough_box.y1 = content_box.y1;
      trough_box.x2 = content_box.x2 - fw_stepper_size;
      trough_box.y2 = content_box.y2;
      clutter_actor_allocate (priv->trough, &trough_box, flags);
    }


  if (priv->adjustment)
    {
      float handle_size, position, avail_size, stepper_size;
      gdouble value, lower, upper, page_size, increment, min_size, max_size;
      ClutterActorBox handle_box = { 0, };

      stepper_size = bw_stepper_size + fw_stepper_size;

      st_adjustment_get_values (priv->adjustment,
                                &value,
                                &lower,
                                &upper,
                                NULL,
                                NULL,
                                &page_size);

      if ((upper == lower)
          || (page_size >= (upper - lower)))
        increment = 1.0;
      else
        increment = page_size / (upper - lower);

      min_size = 32.;
      st_theme_node_lookup_length (theme_node, "min-size", FALSE, &min_size);
      max_size = G_MAXINT16;
      st_theme_node_lookup_length (theme_node, "max-size", FALSE, &max_size);

      if (upper - lower - page_size <= 0)
        position = 0;
      else
        position = (value - lower) / (upper - lower - page_size);

      if (priv->vertical)
        {
          avail_size = content_box.y2 - content_box.y1 - stepper_size;
          handle_size = increment * avail_size;
          handle_size = CLAMP (handle_size, min_size, max_size);

          handle_box.x1 = content_box.x1;
          handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size);

          handle_box.x2 = content_box.x2;
          handle_box.y2 = handle_box.y1 + handle_size;
        }
      else
        {
          avail_size = content_box.x2 - content_box.x1 - stepper_size;
          handle_size = increment * avail_size;
          handle_size = CLAMP (handle_size, min_size, max_size);

          handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size);
          handle_box.y1 = content_box.y1;

          handle_box.x2 = handle_box.x1 + handle_size;
          handle_box.y2 = content_box.y2;
        }

      /* snap to pixel */
      handle_box.x1 = (int) handle_box.x1;
      handle_box.y1 = (int) handle_box.y1;
      handle_box.x2 = (int) handle_box.x2;
      handle_box.y2 = (int) handle_box.y2;

      clutter_actor_allocate (priv->handle,
                              &handle_box,
                              flags);
    }
}
Ejemplo n.º 6
0
static void
clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager,
                                          ClutterContainer     *container,
                                          gfloat                for_width,
                                          gfloat               *min_height_p,
                                          gfloat               *nat_height_p)
{
  ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
  gint n_columns, line_item_count, line_count;
  gfloat total_min_height, total_natural_height;
  gfloat line_min_height, line_natural_height;
  gfloat max_min_height, max_natural_height;
  ClutterActor *actor, *child;
  ClutterActorIter iter;
  gfloat item_x;

  n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width);

  total_min_height = 0;
  total_natural_height = 0;

  line_min_height = 0;
  line_natural_height = 0;

  line_item_count = 0;
  line_count = 0;

  item_x = 0;

  actor = CLUTTER_ACTOR (container);

  /* clear the line height arrays */
  if (priv->line_min != NULL)
    g_array_free (priv->line_min, TRUE);

  if (priv->line_natural != NULL)
    g_array_free (priv->line_natural, TRUE);

  priv->line_min = g_array_sized_new (FALSE, FALSE,
                                      sizeof (gfloat),
                                      16);
  priv->line_natural = g_array_sized_new (FALSE, FALSE,
                                          sizeof (gfloat),
                                          16);

  if (clutter_actor_get_n_children (actor) != 0)
    line_count = 1;

  max_min_height = max_natural_height = 0;

  clutter_actor_iter_init (&iter, actor);
  while (clutter_actor_iter_next (&iter, &child))
    {
      gfloat child_min, child_natural;
      gfloat new_x, item_width;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
        {
          if (line_item_count == n_columns)
            {
              total_min_height += line_min_height;
              total_natural_height += line_natural_height;

              g_array_append_val (priv->line_min,
                                  line_min_height);
              g_array_append_val (priv->line_natural,
                                  line_natural_height);

              line_min_height = line_natural_height = 0;

              line_item_count = 0;
              line_count += 1;
              item_x = 0;
            }

          new_x = ((line_item_count + 1) * (for_width + priv->col_spacing))
                / n_columns;
          item_width = new_x - item_x - priv->col_spacing;

          clutter_actor_get_preferred_height (child, item_width,
                                              &child_min,
                                              &child_natural);

          line_min_height = MAX (line_min_height, child_min);
          line_natural_height = MAX (line_natural_height, child_natural);

          item_x = new_x;
          line_item_count += 1;

          max_min_height = MAX (max_min_height, line_min_height);
          max_natural_height = MAX (max_natural_height, line_natural_height);
        }
      else
        {
          clutter_actor_get_preferred_height (child, for_width,
                                              &child_min,
                                              &child_natural);

          max_min_height = MAX (max_min_height, child_min);
          max_natural_height = MAX (max_natural_height, child_natural);

          total_min_height += max_min_height;
          total_natural_height += max_natural_height;

          line_count += 1;
        }
    }

  priv->row_height = max_natural_height;

  if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height)
    priv->row_height = MAX (priv->max_row_height, max_min_height);

  if (priv->row_height < priv->min_row_height)
    priv->row_height = priv->min_row_height;

  if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0)
    {
      /* if we have a non-full row we need to add it */
      if (line_item_count > 0)
        {
          total_min_height += line_min_height;
          total_natural_height += line_natural_height;

          g_array_append_val (priv->line_min,
                              line_min_height);
          g_array_append_val (priv->line_natural,
                              line_natural_height);
        }

      priv->line_count = line_count;
      if (priv->line_count > 0)
        {
          gfloat total_spacing;

          total_spacing = priv->row_spacing * (priv->line_count - 1);

          total_min_height += total_spacing;
          total_natural_height += total_spacing;
        }
    }
  else
    {
      g_array_append_val (priv->line_min, line_min_height);
      g_array_append_val (priv->line_natural, line_natural_height);

      priv->line_count = line_count;

      if (priv->line_count > 0)
        {
          gfloat total_spacing;

          total_spacing = priv->col_spacing * priv->line_count;

          total_min_height += total_spacing;
          total_natural_height += total_spacing;
        }
    }

  CLUTTER_NOTE (LAYOUT,
                "Flow[h]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f",
                n_columns, priv->line_count,
                total_min_height,
                total_natural_height,
                for_width);

  priv->req_width = for_width;

  if (min_height_p)
    *min_height_p = max_min_height;

  if (nat_height_p)
    *nat_height_p = total_natural_height;
}
Ejemplo n.º 7
0
int main(int argc, char *argv[])
{
  ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
  ClutterColor actor_color = { 0xff, 0xff, 0xcc, 0xff };

  clutter_init (&argc, &argv);

  /* Get the stage and set its size and color: */
  ClutterActor *stage = clutter_stage_get_default ();
  clutter_actor_set_size (stage, 800, 200);
  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);


  /* Add a non-editable text actor to the stage: */
  ClutterActor *text = clutter_text_new ();

  /* Setup text properties */
  clutter_text_set_color (CLUTTER_TEXT (text), &actor_color);
  clutter_text_set_text (CLUTTER_TEXT (text), 
    "Non-editable text: Wizard imps and sweat sock pimps, interstellar mongrel nymphs.");
  clutter_text_set_font_name (CLUTTER_TEXT (text), "Sans 12");
  clutter_text_set_editable (CLUTTER_TEXT (text), FALSE);
  clutter_text_set_line_wrap (CLUTTER_TEXT (text), FALSE);
  
  /* Discover the preferred height and use that height: */
  float min_height = 0;
  float natural_height = 0;
  clutter_actor_get_preferred_height (text, 750, &min_height,
    &natural_height);
  clutter_actor_set_size (text, 750, natural_height);
  
  clutter_actor_set_position (text, 5, 5);
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), text);
  clutter_actor_show (text);
  
  
  /* Add a multi-line editable text actor to the stage: */
  text = clutter_text_new ();

  /* Setup text properties */
  clutter_text_set_color (CLUTTER_TEXT (text), &actor_color);
  clutter_text_set_text (CLUTTER_TEXT (text), 
    "Editable text: And as I sat there brooding on the old, unknown world, I thought of "
     "Gatsby's wonder when he first picked out the green light at the end of "
     "Daisy's dock. He had come a long way to this blue lawn and his dream "
     "must have seemed so close that he could hardly fail to grasp it. He did "
     "not know that it was already behind him, somewhere back in that vast "
     "obscurity beyond the city, where the dark fields of the republic rolled "
     "on under the night.");
  clutter_text_set_font_name (CLUTTER_TEXT (text), "Sans 12");
  clutter_text_set_editable (CLUTTER_TEXT (text), TRUE);
  clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE);

  /* Discover the preferred height and use that height: */
  min_height = 0;
  natural_height = 0;
  clutter_actor_get_preferred_height (text, 750, &min_height,
    &natural_height);
  clutter_actor_set_size (text, 750, natural_height);
  
  clutter_actor_set_position (text, 5, 50);
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), text);
  clutter_actor_show (text);

  /* Set focus to handle key presses on the stage: */
  clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text);
  

  /* Show the stage: */
  clutter_actor_show (stage);

  /* Start the main loop, so we can respond to events: */
  clutter_main ();

  return EXIT_SUCCESS;

}
Ejemplo n.º 8
0
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));
    }

}
Ejemplo n.º 9
0
static void
calculate_row_heights (ClutterTableLayout *self,
                       ClutterContainer   *container,
                       gint                for_height)
{
  ClutterTableLayoutPrivate *priv = self->priv;
  ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (self);
  ClutterActor *actor, *child;
  gint i;
  DimensionData *rows, *columns;
  ClutterOrientation orientation = CLUTTER_ORIENTATION_VERTICAL;

  update_row_col (self, container);
  g_array_set_size (priv->rows, 0);
  g_array_set_size (priv->rows, self->priv->n_rows);

  rows = (DimensionData *) (void *) priv->rows->data;
  columns = (DimensionData *) (void *) priv->columns->data;

  /* reset the visibility of all rows */
  priv->visible_rows = 0;
  for (i = 0; i < priv->n_rows; i++)
    {
      rows[i].expand = FALSE;
      rows[i].visible = FALSE;
    }

  actor = CLUTTER_ACTOR (container);

  /* STAGE ONE: calculate row heights for non-spanned children */
  for (child = clutter_actor_get_first_child (actor);
       child != NULL;
       child = clutter_actor_get_next_sibling (child))
    {
      ClutterTableChild *meta;
      DimensionData *row;
      gfloat c_min, c_pref;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      meta =
        CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
                                                                    container,
                                                                    child));

      if (meta->row_span > 1)
        continue;

      row = &rows[meta->row];

      if (!row->visible)
        {
          row->visible = TRUE;
          priv->visible_rows += 1;
        }

      clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
                                          &c_min, &c_pref);

      row->min_size = MAX (row->min_size, c_min);
      row->pref_size = MAX (row->pref_size, c_pref);

      if (!row->expand)
        row->expand = clutter_actor_needs_expand (child, orientation);
    }

  /* STAGE TWO: take spanning children into account */
  for (child = clutter_actor_get_first_child (actor);
       child != NULL;
       child = clutter_actor_get_next_sibling (child))
    {
      ClutterTableChild *meta;
      gfloat c_min, c_pref;
      gfloat min_height, pref_height;
      gint start_row, end_row;
      gint n_expand;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      meta =
        CLUTTER_TABLE_CHILD (clutter_layout_manager_get_child_meta (manager,
                                                                    container,
                                                                    child));

      if (meta->row_span < 2)
        continue;

      start_row = meta->row;
      end_row = meta->row + meta->row_span - 1;

      clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
                                         &c_min, &c_pref);


      /* check there is enough room for this actor */
      min_height = 0;
      pref_height = 0;
      n_expand = 0;
      for (i = start_row; i <= end_row; i++)
        {
          min_height += rows[i].min_size;
          pref_height += rows[i].pref_size;

          if (rows[i].expand)
            n_expand++;

          if (!rows[i].visible)
            {
              rows[i].visible = TRUE;
              priv->visible_rows += 1;
            }

          if (!rows[i].expand)
            rows[i].expand = clutter_actor_needs_expand (child, orientation);
        }

      min_height += priv->row_spacing * (meta->row_span - 1);
      pref_height += priv->row_spacing * (meta->row_span - 1);

      /* 1) If the minimum height of the rows spanned is less than the
       *    minimum height of the child that is spanning them, then we
       *    must increase the minimum height of the rows spanned.
       *
       * 2) If the preferred height of the spanned rows is more than
       *    the minimum height of the spanning child, then we can start
       *    at this size and decrease each row evenly.
       *
       * 3) If the preferred height of the rows is more than the minimum
       *    height of the spanned child, then we can start at the preferred
       *    height and expand.
       */

      /* (1) */
      if (c_min > min_height)
        {

          /* (2) */
          /* we can start from preferred height and decrease */
          if (pref_height > c_min)
            {
              for (i = start_row; i <= end_row; i++)
                rows[i].final_size = rows[i].pref_size;

              while (pref_height > c_min)
                {
                  for (i = start_row; i <= end_row; i++)
                    {
                      if (rows[i].final_size > rows[i].min_size)
                        {
                          rows[i].final_size--;
                          pref_height--;
                        }
                    }
                }

              for (i = start_row; i <= end_row; i++)
                rows[i].min_size = rows[i].final_size;
            }
          else
            {
              /* (3) */
              /* we can expand from preferred size */
              gfloat expand_by = c_pref - pref_height;

              for (i = start_row; i <= end_row; i++)
                {
                  if (n_expand)
                    {
                      if (rows[i].expand)
                        rows[i].min_size = rows[i].pref_size
                                         + expand_by / n_expand;
                    }
                  else
                    rows[i].min_size = rows[i].pref_size
                                     + expand_by / meta->row_span;
                }
            }
        }
    }

  /* calculate final heights */
  if (for_height >= 0)
    {
      gfloat min_height, pref_height;
      gint n_expand;

      min_height = 0;
      pref_height = 0;
      n_expand = 0;

      for (i = 0; i < self->priv->n_rows; i++)
        {
          pref_height += rows[i].pref_size;
          min_height += rows[i].min_size;
          if (rows[i].expand)
            n_expand++;
        }

      pref_height += priv->row_spacing * (priv->n_rows - 1);
      min_height += priv->row_spacing * (priv->n_rows - 1);

      if (for_height <= min_height)
        {
          /* erk, we can't shrink this! */
          for (i = 0; i < self->priv->n_rows; i++)
            rows[i].final_size = rows[i].min_size;

          return;
        }

      if (for_height == pref_height)
        {
          /* perfect! */
          for (i = 0; i < self->priv->n_rows; i++)
            rows[i].final_size = rows[i].pref_size;

          return;
        }

      /* for_height is between min_height and pref_height */
      if (for_height < pref_height && for_height > min_height)
        {
          gfloat height;

          /* shrink rows until they reach min_height */

          /* start with all rows at preferred size */
          for (i = 0; i < self->priv->n_rows; i++)
            rows[i].final_size = rows[i].pref_size;

          height = pref_height;

          while (height > for_height)
            {
              for (i = 0; i < priv->n_rows; i++)
                {
                  if (rows[i].final_size > rows[i].min_size)
                    {
                      rows[i].final_size--;
                      height--;
                    }
                }
            }

          return;
        }

      /* expand rows */
      if (for_height > pref_height)
        {
          gfloat extra_height = for_height - pref_height;
          gint remaining;

          if (n_expand)
            remaining = (gint) extra_height % n_expand;
          else
            remaining = (gint) extra_height % self->priv->n_rows;

          for (i = 0; i < self->priv->n_rows; i++)
            {
              if (rows[i].expand)
                {
                  rows[i].final_size = rows[i].pref_size
                                     + (extra_height / n_expand);
                }
              else
                rows[i].final_size = rows[i].pref_size;
            }

          /* distribute the remainder among children */
          i = 0;
          while (remaining)
            {
              rows[i].final_size++;
              i++;
              remaining--;
            }
        }
    }

}
Ejemplo n.º 10
0
static void
mx_menu_allocate (ClutterActor           *actor,
                  const ClutterActorBox  *box,
                  ClutterAllocationFlags  flags)
{
    gint i;
    MxPadding padding;
    ClutterActorBox child_box;
    MxMenuPrivate *priv = MX_MENU (actor)->priv;

    gfloat available_h = box->y2-box->y1;

    /*
     * first of all, we have to check if the allocated height is our
     * natural height...
     */
    gfloat menu_nat_h;
    mx_menu_get_preferred_height (actor, box->x2-box->x1, NULL, &menu_nat_h);
    if (available_h < menu_nat_h)
    {
        /*
         * ...if not, we have to draw 2 buttons in order to let the
         * user be able to navigate in the whole menu.
         */
        priv->scrolling_mode = TRUE;
    }
    else
    {
        priv->scrolling_mode = FALSE;
    }

    /* Allocate children */
    mx_widget_get_padding (MX_WIDGET (actor), &padding);
    child_box.x1 = padding.left;
    child_box.y1 = padding.top;
    child_box.x2 = box->x2 - box->x1 - padding.right;

    gfloat down_but_height;
    clutter_actor_get_preferred_height (priv->down_button,
                                        child_box.x2 - child_box.x1,
                                        NULL,
                                        &down_but_height);

    if (priv->scrolling_mode)
    {
        clutter_actor_get_preferred_height (priv->up_button,
                                            child_box.x2 - child_box.x1,
                                            NULL,
                                            &child_box.y2);
        child_box.y2 += child_box.y1;
        clutter_actor_allocate (priv->up_button, &child_box, flags);
        child_box.y1 = child_box.y2 + 1;

        available_h -= down_but_height;
    }


    for (i = priv->id_offset; i < priv->children->len; i++)
    {
        gfloat natural_height;

        MxMenuChild *child = &g_array_index (priv->children, MxMenuChild,
                                             i);

        clutter_actor_get_preferred_height (CLUTTER_ACTOR (child->box),
                                            child_box.x2 - child_box.x1,
                                            NULL,
                                            &natural_height);
        child_box.y2 = child_box.y1 + natural_height;
        if (child_box.y2 >= available_h)
        {
            priv->last_shown_id = i-1;
            break;
        }

        clutter_actor_allocate (CLUTTER_ACTOR (child->box), &child_box, flags);

        child_box.y1 = child_box.y2 + 1;
    }
    if (priv->children->len == i)
    {
        priv->last_shown_id = i-1;
    }

    if (priv->scrolling_mode)
    {
        child_box.y2 = child_box.y1 + down_but_height;
        clutter_actor_allocate (priv->down_button, &child_box, flags);
    }
    /* Chain up and allocate background */
    CLUTTER_ACTOR_CLASS (mx_menu_parent_class)->allocate (actor, box, flags);
}
Ejemplo n.º 11
0
static void
mx_tooltip_update_position (MxTooltip *tooltip)
{
  MxTooltipPrivate *priv = tooltip->priv;
  ClutterGeometry tip_area = *tooltip->priv->tip_area;
  gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y, abs_x, abs_y;
  ClutterActor *stage, *parent;
  gfloat stage_w, stage_h, parent_w, parent_h;
  MxWindow *window;

  /* If there's no stage, bail out - there's nothing we can do */
  stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
  if (!stage)
    return;

  /* find out the stage's size to keep the tooltip on-screen */
  clutter_actor_get_size (stage, &stage_w, &stage_h);


  parent = clutter_actor_get_parent ((ClutterActor *) tooltip);
  clutter_actor_get_transformed_position (parent, &abs_x, &abs_y);
  clutter_actor_get_size (parent, &parent_w, &parent_h);

  /* ensure the tooltip with is not fixed size */
  clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);

  /* if no area set, just position ourselves top left */
  if (!priv->tip_area)
    {
      clutter_actor_set_position ((ClutterActor*) tooltip, abs_x, abs_y);
      return;
    }

  /* check if we're in a window and if there's rotation */
  window = mx_window_get_for_stage (CLUTTER_STAGE (stage));
  if (window)
    {
      MxWindowRotation rotation;
      gfloat old_x;

      g_object_get (G_OBJECT (window),
                    "window-rotation", &rotation,
                    NULL);

      if (rotation == MX_WINDOW_ROTATION_90
          || rotation == MX_WINDOW_ROTATION_270)
        {
          /* swap stage width and height */
          old_x = stage_w;
          stage_w = stage_h;
          stage_h = old_x;

          /* swap tip area width and height */
          old_x = tip_area.width;
          tip_area.width = tip_area.height;
          tip_area.height = old_x;
        }

      switch (rotation)
        {
        case MX_WINDOW_ROTATION_90:
          /* absolute position */
          old_x = abs_x;
          abs_x = abs_y;
          abs_y = stage_h - old_x;

          /* tip area */
          old_x = tip_area.x;
          tip_area.x = tip_area.y;
          tip_area.y = stage_h - old_x - tip_area.height;
          break;

        case MX_WINDOW_ROTATION_180:
          tip_area.x = stage_w - tip_area.x - tip_area.width;
          tip_area.y = stage_h - tip_area.y - tip_area.height;

          abs_x = stage_w - abs_x;
          abs_y = stage_h - abs_y;
          break;

        case MX_WINDOW_ROTATION_270:
          /* absolute position */
          old_x = abs_x;
          abs_x = stage_w - abs_y;
          abs_y = old_x;

          /* tip area */
          old_x = tip_area.x;
          tip_area.x = stage_w - tip_area.y - tip_area.width;
          tip_area.y = old_x;
          break;

        default:
          break;
        }
    }

  /* we need to have a style in case there are padding values to take into
   * account when calculating width/height */
  mx_stylable_style_changed (MX_STYLABLE (tooltip), MX_STYLE_CHANGED_FORCE);

  /* find out the tooltip's size */
  clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);

  /* attempt to place the tooltip */
  /* This special-cases the 4 window rotations, as doing this with
   * arbitrary rotations would massively complicate the code for
   * little benefit.
   */
  priv->actor_below = FALSE;

  tooltip_x = (int)(tip_area.x + (tip_area.width / 2) -
                    (tooltip_w / 2));
  tooltip_y = (int)(tip_area.y + tip_area.height);

  /* Keep on the screen vertically */
  if (tooltip_y + tooltip_h > stage_h)
    {
      priv->actor_below = TRUE;

      /* re-query size as may have changed */
      clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
                                          -1, NULL, &tooltip_h);
      tooltip_y = MAX (0, tip_area.y - tooltip_h);
    }


  /* Keep on the screen horizontally */
  if (tooltip_w > stage_w)
    {
      tooltip_x = 0;
      clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
    }
  else if (tooltip_x < 0)
    tooltip_x = 0;
  else if (tooltip_x + tooltip_w > stage_w)
    tooltip_x = (int)(stage_w) - tooltip_w;

  gfloat pos_x, pos_y;

  pos_x = tooltip_x - abs_x;
  pos_y = tooltip_y - abs_y;

  /* calculate the arrow offset */
  priv->arrow_offset = tip_area.x + tip_area.width / 2 - tooltip_x;
  clutter_actor_set_position ((ClutterActor*) tooltip, pos_x, pos_y);
}
Ejemplo n.º 12
0
static void
mx_table_calculate_row_heights (MxTable *table,
                                gint     for_height)
{
  MxTablePrivate *priv = MX_TABLE (table)->priv;
  gint i;
  DimensionData *rows, *columns;
  MxPadding padding;
  ClutterActorIter iter;
  ClutterActor *child;

  mx_widget_get_padding (MX_WIDGET (table), &padding);

  /* take padding off available height */
  for_height -= (int)(padding.top + padding.bottom);

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

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

  /* Reset the visible rows */
  priv->visible_rows = 0;
  for (i = 0; i < priv->n_rows; i++)
    rows[i].is_visible = FALSE;

  /* STAGE ONE: calculate row heights for non-spanned children */
  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (table));
  while (clutter_actor_iter_next (&iter, &child))
    {
      MxTableChild *meta;
      DimensionData *row;
      gfloat c_min, c_pref;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

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

      if (meta->row_span > 1)
        continue;

      row = &rows[meta->row];

      /* If this child is visible, then its row is visible */
      if (!row->is_visible)
        {
          row->is_visible = TRUE;
          priv->visible_rows++;
        }

      clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
                                          &c_min, &c_pref);

      row->min_size = MAX (row->min_size, c_min);
      row->final_size = row->pref_size = MAX (row->pref_size, c_pref);
      row->expand = MAX (row->expand, meta->y_expand);
    }



  /* STAGE TWO: take spanning children into account */
  clutter_actor_iter_init (&iter, CLUTTER_ACTOR (table));
  while (clutter_actor_iter_next (&iter, &child))
    {
      MxTableChild *meta;
      gfloat c_min, c_pref;
      gfloat min_height, pref_height;
      gint start_row, end_row;
      gint n_expand;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

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

      if (meta->row_span < 2)
        continue;

      start_row = meta->row;
      end_row = meta->row + meta->row_span - 1;

      clutter_actor_get_preferred_height (child, columns[meta->col].final_size,
                                         &c_min, &c_pref);


      /* check there is enough room for this actor */
      min_height = 0;
      pref_height = 0;
      n_expand = 0;
      for (i = start_row; i <= end_row; i++)
        {
          min_height += rows[i].min_size;
          pref_height += rows[i].pref_size;

          if (rows[i].expand)
            {
              n_expand++;
            }

          /* If this actor is visible, then all the rows is spans are visible */
          if (!rows[i].is_visible)
            {
              rows[i].is_visible = TRUE;
              priv->visible_rows++;
            }
          rows[i].expand = MAX (rows[i].expand, meta->y_expand);
        }
      min_height += priv->row_spacing * (meta->row_span - 1);
      pref_height += priv->row_spacing * (meta->row_span - 1);

      /* 1) If the minimum height of the rows spanned is less than the minimum
       * height of the child that is spanning them, then we must increase the
       * minimum height of the rows spanned.
       *
       * 2) If the preferred height of the spanned rows is more that the minimum
       * height of the spanning child, then we can start at this size and
       * decrease each row evenly.
       *
       * 3) If the preferred height of the rows is more than the minimum height
       * of the spanned child, then we can start at the preferred height and
       * expand.
       */
      /* (1) */
      if (c_min > min_height)
        {

          /* (2) */
          /* we can start from preferred height and decrease */
          if (pref_height > c_min)
            {
              for (i = start_row; i <= end_row; i++)
                {
                  rows[i].final_size = rows[i].pref_size;
                }

              while (pref_height > c_min)
                {
                  for (i = start_row; i <= end_row; i++)
                    {
                      if (rows[i].final_size > rows[i].min_size)
                        {
                          rows[i].final_size--;
                          pref_height--;
                        }
                    }
                }
              for (i = start_row; i <= end_row; i++)
                {
                  rows[i].min_size = rows[i].final_size;
                }

            }
          else
            {
              /* (3) */
              /* we can expand from preferred size */
              gfloat expand_by;

              expand_by = c_pref - pref_height;

              for (i = start_row; i <= end_row; i++)
                {
                  if (n_expand)
                    {
                      if (rows[i].expand)
                        rows[i].min_size =
                          rows[i].pref_size + expand_by / n_expand;
                    }
                  else
                    {
                      rows[i].min_size =
                        rows[i].pref_size + expand_by / meta->row_span;
                    }

                }
            }
        }

    }


  /* calculate final heights */
  if (for_height >= 0)
    {
      gfloat min_height, pref_height;
      gint n_expand;

      min_height = 0;
      pref_height = 0;
      n_expand = 0;
      for (i = 0; i < priv->n_rows; i++)
        {
          pref_height += rows[i].pref_size;
          min_height += rows[i].min_size;
          if (rows[i].expand)
            n_expand++;
        }
      pref_height += priv->row_spacing * (priv->n_rows - 1);
      min_height += priv->row_spacing * (priv->n_rows - 1);

      if (for_height <= min_height)
        {
          /* erk, we can't shrink this! */
          for (i = 0; i < priv->n_rows; i++)
            {
              rows[i].final_size = rows[i].min_size;
            }
          return;
        }

      if (for_height == pref_height)
        {
          /* perfect! */
          for (i = 0; i < priv->n_rows; i++)
            {
              rows[i].final_size = rows[i].pref_size;
            }
          return;
        }

      /* for_height is between min_height and pref_height */
      if (for_height < pref_height && for_height > min_height)
        {
          gfloat height;

          /* shrink rows until they reach min_height */

          /* start with all rows at preferred size */
          for (i = 0; i < priv->n_rows; i++)
            {
              rows[i].final_size = rows[i].pref_size;
            }
          height = pref_height;

          while (height > for_height)
            {
              for (i = 0; i < priv->n_rows; i++)
                {
                  if (rows[i].final_size > rows[i].min_size)
                    {
                      rows[i].final_size--;
                      height--;
                    }
                }
            }

          return;
        }

      /* expand rows */
      if (for_height > pref_height)
        {
          gfloat extra_height = for_height - pref_height;
          gint remaining;

          if (n_expand)
            remaining = (gint) extra_height % n_expand;
          else
            remaining = (gint) extra_height % priv->n_rows;

          for (i = 0; i < priv->n_rows; i++)
            {
              if (rows[i].expand)
                {
                  if (n_expand)
                    {
                      rows[i].final_size =
                        rows[i].pref_size + (extra_height / n_expand);
                    }
                  else
                    {
                      rows[i].final_size =
                        rows[i].pref_size + (extra_height / priv->n_rows);
                    }
                }
              else
                rows[i].final_size = rows[i].pref_size;
            }

          /* distribute the remainder among children */
          i = 0;
          while (remaining)
            {
              rows[i].final_size++;
              i++;
              remaining--;
            }
        }
    }

}
Ejemplo n.º 13
0
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
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);
    }
}
Ejemplo n.º 15
0
static void
mx_combo_box_allocate (ClutterActor          *actor,
                       const ClutterActorBox *box,
                       ClutterAllocationFlags flags)
{
  MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv;
  MxPadding padding;
  gfloat x, y, width, height;
  gfloat min_menu_h, nat_menu_h;
  gfloat label_h;
  gfloat nat_icon_h, icon_h, icon_w;
  gfloat nat_marker_h, marker_h, marker_w;
  ClutterActorBox childbox;
  ClutterActor *menu, *stage;

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

  mx_widget_get_padding (MX_WIDGET (actor), &padding);

  x = padding.left;
  y = padding.top;
  width = box->x2 - box->x1 - padding.left - padding.right;
  height = box->y2 - box->y1 - padding.top - padding.bottom;

  icon_w = marker_w = 0;

  if (priv->icon)
    {
      /* Allocate the icon, if there is one, the space not used by the text */
      clutter_actor_get_preferred_height (priv->icon, -1, NULL, &nat_icon_h);

      if (height >= nat_icon_h)
        {
          icon_h = nat_icon_h;
          clutter_actor_get_preferred_width (priv->icon, -1, NULL, &icon_w);
        }
      else
        {
          icon_h = height;
          clutter_actor_get_preferred_width (priv->icon, icon_h, NULL, &icon_w);
        }

      childbox.x1 = (int)(x);
      childbox.y1 = (int)(y + (height - icon_h) / 2);
      childbox.x2 = (int)(x + icon_w);
      childbox.y2 = (int)(childbox.y1 + icon_h);

      clutter_actor_allocate (priv->icon, &childbox, flags);

      icon_w += priv->spacing;
    }

  if (priv->marker)
    {
      clutter_actor_get_preferred_height (priv->marker, -1,
                                          NULL, &nat_marker_h);

      if (height >= nat_marker_h)
        {
          marker_h = nat_marker_h;
          clutter_actor_get_preferred_width (priv->marker, -1, NULL,
                                             &marker_w);
        }
      else
        {
          marker_h = height;
          clutter_actor_get_preferred_width (priv->marker, marker_h,
                                             NULL, &marker_w);
        }

      childbox.x2 = (int)(x + width);
      childbox.x1 = (int)(childbox.x2 - marker_w);
      childbox.y1 = (int)(y + (height - marker_h) / 2);
      childbox.y2 = (int)(childbox.y1 + marker_h);

      clutter_actor_allocate (priv->marker, &childbox, flags);

      marker_w += priv->spacing;
    }

  clutter_actor_get_preferred_height (priv->label, -1, NULL, &label_h);

  childbox.x1 = (int)(x + icon_w);
  childbox.y1 = (int)(y + (height / 2 - label_h / 2));
  childbox.x2 = (int)(x + width - marker_w);
  childbox.y2 = (int)(childbox.y1 + label_h);
  clutter_actor_allocate (priv->label, &childbox, flags);

  menu = (ClutterActor*) mx_widget_get_menu (MX_WIDGET (actor));
  clutter_actor_get_preferred_height (menu, (box->x2 - box->x1), &min_menu_h,
                                      &nat_menu_h);

  childbox.x1 = 0;
  childbox.x2 = (box->x2 - box->x1);
  childbox.y1 = (box->y2 - box->y1);

  childbox.y2 = childbox.y1 + nat_menu_h;

  stage = clutter_actor_get_stage (actor);
  if (stage != NULL)
    {
      ClutterVertex point = { 0, };
      gfloat stage_w, stage_h, combo_h = box->y2 - box->y1;

      clutter_actor_get_size (stage, &stage_w, &stage_h);
      point.y = combo_h + nat_menu_h;
      clutter_actor_apply_transform_to_point (actor, &point, &point);

      /* If the menu would appear off the stage, flip it around. */
      if ((point.y < 0) || (point.y >= stage_h))
        {
          childbox.y1 = -nat_menu_h;
          point.y = -nat_menu_h;
          clutter_actor_apply_transform_to_point (actor, &point, &point);
          /* if the menu would still appear out of the stage, force
           * it to appear on the top of the stage.
           */
          if (point.y < 0)
            {
              gfloat xactor, yactor;
              clutter_actor_get_transformed_position (actor, &xactor,
                                                      &yactor);
              childbox.y1 = -yactor;
            }
        }

      point.y = childbox.y1 + nat_menu_h;
      clutter_actor_apply_transform_to_point (actor, &point, &point);
      if (point.y >= stage_h)
        {
          gfloat xactor, yactor;
          clutter_actor_get_transformed_position (actor, &xactor, &yactor);
          /*
           * clamp so that the menu doesn't appear out of the screen
           */
          clutter_actor_transform_stage_point (actor, xactor, stage_h,
                                               NULL, &childbox.y2);
          /*
           * The previous transformation can lead to negative height
           * allocation if the top-left corner of the menu is already
           * flipped around. This happens when you put a combobox deep
           * enough in a scrollview taller that the stage.
           */
          childbox.y2 = MAX (childbox.y1, childbox.y2);
        }
      else
        {
          childbox.y2 = childbox.y1 + nat_menu_h;
        }

    }
  clutter_actor_allocate (menu, &childbox, flags);
}
Ejemplo n.º 16
0
static void
st_scroll_view_get_preferred_height (ClutterActor *actor,
                                     gfloat        for_width,
                                     gfloat       *min_height_p,
                                     gfloat       *natural_height_p)
{
  StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
  gboolean account_for_hscrollbar = FALSE;
  gfloat min_height = 0, natural_height;
  gfloat child_min_height, child_natural_height;
  gfloat child_min_width;
  gfloat sb_width;

  if (!priv->child)
    return;

  st_theme_node_adjust_for_width (theme_node, &for_width);

  clutter_actor_get_preferred_width (priv->child, -1,
                                     &child_min_width, NULL);

  if (min_height_p)
    *min_height_p = 0;

  sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1);

  switch (priv->vscrollbar_policy)
    {
    case GTK_POLICY_NEVER:
      break;
    case GTK_POLICY_ALWAYS:
    case GTK_POLICY_AUTOMATIC:
      /* We've requested space for the scrollbar, subtract it back out */
      for_width -= sb_width;
      break;
    }

  switch (priv->hscrollbar_policy)
    {
    case GTK_POLICY_NEVER:
      account_for_hscrollbar = FALSE;
      break;
    case GTK_POLICY_ALWAYS:
      account_for_hscrollbar = TRUE;
      break;
    case GTK_POLICY_AUTOMATIC:
      account_for_hscrollbar = for_width < child_min_width;
      break;
    }

  clutter_actor_get_preferred_height (priv->child, for_width,
                                      &child_min_height, &child_natural_height);

  natural_height = child_natural_height;

  switch (priv->vscrollbar_policy)
    {
    case GTK_POLICY_NEVER:
      min_height = child_min_height;
      break;
    case GTK_POLICY_ALWAYS:
    case GTK_POLICY_AUTOMATIC:
      /* Should theoretically use the min height of the vscrollbar,
       * but that's not cleanly defined at the moment */
      min_height = 0;
      break;
    }

  if (account_for_hscrollbar)
    {
      float sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), for_width);

      min_height += sb_height;
      natural_height += sb_height;
    }

  if (min_height_p)
    *min_height_p = min_height;

  if (natural_height_p)
    *natural_height_p = natural_height;

  st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
penge_calendar_pane_allocate (ClutterActor          *actor,
                              const ClutterActorBox *box,
                              ClutterAllocationFlags flags)
{
  PengeCalendarPanePrivate *priv = GET_PRIVATE (actor);
  gfloat events_nat_h, tasks_nat_h, events_header_nat_h, tasks_header_nat_h;
  gfloat width, height, remaining_height, last_y;
  gfloat tasks_available_h, events_available_h;
  ClutterActorBox child_box;
  MxPadding padding = { 0, };

  if (CLUTTER_ACTOR_CLASS (penge_calendar_pane_parent_class)->allocate)
    CLUTTER_ACTOR_CLASS (penge_calendar_pane_parent_class)->allocate (actor, box, flags);

  mx_widget_get_padding (MX_WIDGET (actor), &padding);

  width = box->x2 - box->x1;
  height = box->y2 - box->y1;

  clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->events_header_table),
                                      width,
                                      NULL,
                                      &events_header_nat_h);

  clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->events_pane),
                                      width,
                                      NULL,
                                      &events_nat_h);

  clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->tasks_header_table),
                                      width,
                                      NULL,
                                      &tasks_header_nat_h);

  clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->tasks_pane),
                                      width,
                                      NULL,
                                      &tasks_nat_h);

  child_box.x1 = padding.left;
  child_box.x2 = width - padding.right;
  child_box.y1 = padding.top;
  child_box.y2 = child_box.y1 + events_header_nat_h;
  last_y = child_box.y2;
  clutter_actor_allocate (CLUTTER_ACTOR (priv->events_header_table),
                          &child_box,
                          flags);

  remaining_height = height - padding.top - padding.bottom -
                     events_header_nat_h - tasks_header_nat_h;

  if (tasks_nat_h <= (remaining_height / 2.0))
  {
    tasks_available_h = tasks_nat_h;
  } else {
    tasks_available_h = (remaining_height / 2.0);
  }

  events_available_h = remaining_height - tasks_available_h;
  if (events_available_h > events_nat_h)
  {
    tasks_available_h += (tasks_available_h - events_nat_h);
    events_available_h = events_nat_h;
  }

  child_box.x1 = padding.left;
  child_box.x2 = width - padding.right;
  child_box.y1 = last_y;
  child_box.y2 = child_box.y1 + (int)events_available_h;
  last_y = child_box.y2;

  clutter_actor_allocate (CLUTTER_ACTOR (priv->events_pane),
                          &child_box,
                          flags);

  child_box.x1 = padding.left;
  child_box.x2 = width - padding.right;
  child_box.y1 = last_y;
  child_box.y2 = child_box.y1 + tasks_header_nat_h;
  last_y = child_box.y2;
  clutter_actor_allocate (CLUTTER_ACTOR (priv->tasks_header_table),
                          &child_box,
                          flags);


  child_box.x1 = padding.left;
  child_box.x2 = width - padding.right;
  child_box.y1 = last_y;
  child_box.y2 = child_box.y1 + (int)tasks_available_h;

  clutter_actor_allocate (CLUTTER_ACTOR (priv->tasks_pane),
                          &child_box,
                          flags);

}
Ejemplo n.º 18
0
static void
st_entry_allocate (ClutterActor          *actor,
                   const ClutterActorBox *box,
                   ClutterAllocationFlags flags)
{
  StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
  ClutterActorClass *parent_class;
  ClutterActorBox content_box, child_box, icon_box;
  gfloat icon_w, icon_h;
  gfloat entry_h, min_h, pref_h, avail_h;

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

  st_theme_node_get_content_box (theme_node, box, &content_box);

  avail_h = content_box.y2 - content_box.y1;

  child_box.x1 = content_box.x1;
  child_box.x2 = content_box.x2;

  if (priv->primary_icon)
    {
      clutter_actor_get_preferred_width (priv->primary_icon,
                                         -1, NULL, &icon_w);
      clutter_actor_get_preferred_height (priv->primary_icon,
                                          -1, NULL, &icon_h);

      icon_box.x1 = content_box.x1;
      icon_box.x2 = icon_box.x1 + icon_w;

      icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
      icon_box.y2 = icon_box.y1 + icon_h;

      clutter_actor_allocate (priv->primary_icon,
                              &icon_box,
                              flags);

      /* reduce the size for the entry */
      child_box.x1 += icon_w + priv->spacing;
    }

  if (priv->secondary_icon)
    {
      clutter_actor_get_preferred_width (priv->secondary_icon,
                                         -1, NULL, &icon_w);
      clutter_actor_get_preferred_height (priv->secondary_icon,
                                          -1, NULL, &icon_h);

      icon_box.x2 = content_box.x2;
      icon_box.x1 = icon_box.x2 - icon_w;

      icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
      icon_box.y2 = icon_box.y1 + icon_h;

      clutter_actor_allocate (priv->secondary_icon,
                              &icon_box,
                              flags);

      /* reduce the size for the entry */
      child_box.x2 -= icon_w - priv->spacing;
    }

  clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
                                      &min_h, &pref_h);

  entry_h = CLAMP (pref_h, min_h, avail_h);

  child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2);
  child_box.y2 = child_box.y1 + entry_h;

  clutter_actor_allocate (priv->entry, &child_box, flags);
}
Ejemplo n.º 19
0
/* Allocate position and size of actor and its children */
static void _xfdashboard_viewpad_allocate(ClutterActor *self,
											const ClutterActorBox *inBox,
											ClutterAllocationFlags inFlags)
{
	XfdashboardViewpadPrivate	*priv=XFDASHBOARD_VIEWPAD(self)->priv;
	ClutterActorClass			*actorClass=CLUTTER_ACTOR_CLASS(xfdashboard_viewpad_parent_class);
	gfloat						viewWidth, viewHeight;
	gfloat						vScrollbarWidth, vScrollbarHeight;
	gfloat						hScrollbarWidth, hScrollbarHeight;
	gboolean					hScrollbarVisible, vScrollbarVisible;
	ClutterActorBox				*box;
	gfloat						x, y, w, h;

	/* Chain up to store the allocation of the actor */
	if(actorClass->allocate) actorClass->allocate(self, inBox, inFlags);

	/* Initialize largest possible allocation for view and determine
	 * real size of view to show. The real size is used to determine
	 * scroll bar visibility if policy is automatic */
	viewWidth=clutter_actor_box_get_width(inBox);
	viewHeight=clutter_actor_box_get_height(inBox);

	/* Determine visibility of scroll bars */
	hScrollbarVisible=FALSE;
	if(priv->hScrollbarPolicy==XFDASHBOARD_POLICY_ALWAYS ||
		(priv->hScrollbarPolicy==XFDASHBOARD_POLICY_AUTOMATIC &&
			xfdashboard_scrollbar_get_range(XFDASHBOARD_SCROLLBAR(priv->hScrollbar))>viewWidth))
	{
		hScrollbarVisible=TRUE;
	}
	if(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_HORIZONTAL ||
		xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_BOTH)
	{
		hScrollbarVisible=FALSE;
	}

	vScrollbarVisible=FALSE;
	if(priv->vScrollbarPolicy==XFDASHBOARD_POLICY_ALWAYS ||
		(priv->vScrollbarPolicy==XFDASHBOARD_POLICY_AUTOMATIC &&
			xfdashboard_scrollbar_get_range(XFDASHBOARD_SCROLLBAR(priv->vScrollbar))>viewHeight))
	{
		vScrollbarVisible=TRUE;
	}
	if(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_VERTICAL ||
		xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView))==XFDASHBOARD_FIT_MODE_BOTH)
	{
		vScrollbarVisible=FALSE;
	}

	/* Set allocation for visible scroll bars */
	vScrollbarWidth=0.0f;
	vScrollbarHeight=viewHeight;
	clutter_actor_get_preferred_width(priv->vScrollbar, -1, NULL, &vScrollbarWidth);

	hScrollbarWidth=viewWidth;
	hScrollbarHeight=0.0f;
	clutter_actor_get_preferred_height(priv->hScrollbar, -1, NULL, &hScrollbarHeight);

	if(hScrollbarVisible && vScrollbarVisible)
	{
		vScrollbarHeight-=hScrollbarHeight;
		hScrollbarWidth-=vScrollbarWidth;
	}

	if(vScrollbarVisible==FALSE) box=clutter_actor_box_new(0, 0, 0, 0);
		else box=clutter_actor_box_new(viewWidth-vScrollbarWidth, 0, viewWidth, vScrollbarHeight);
	clutter_actor_allocate(priv->vScrollbar, box, inFlags);
	clutter_actor_box_free(box);

	if(hScrollbarVisible==FALSE) box=clutter_actor_box_new(0, 0, 0, 0);
		else box=clutter_actor_box_new(0, viewHeight-hScrollbarHeight, hScrollbarWidth, viewHeight);
	clutter_actor_allocate(priv->hScrollbar, box, inFlags);
	clutter_actor_box_free(box);

	/* Reduce allocation for view by any visible scroll bar
	 * and set allocation and clipping of view
	 */
	if(priv->activeView)
	{
		/* Set allocation */
		if(vScrollbarVisible) viewWidth-=vScrollbarWidth;
		if(hScrollbarVisible) viewHeight-=hScrollbarHeight;

		x=y=0.0f;
		if(clutter_actor_has_clip(CLUTTER_ACTOR(priv->activeView)))
		{
			clutter_actor_get_clip(CLUTTER_ACTOR(priv->activeView), &x, &y, NULL, NULL);
		}

		switch(xfdashboard_view_get_fit_mode(XFDASHBOARD_VIEW(priv->activeView)))
		{
			case XFDASHBOARD_FIT_MODE_BOTH:
				w=viewWidth;
				h=viewHeight;
				break;

			case XFDASHBOARD_FIT_MODE_HORIZONTAL:
				w=viewWidth;
				clutter_actor_get_preferred_height(CLUTTER_ACTOR(priv->activeView), w, NULL, &h);
				break;

			case XFDASHBOARD_FIT_MODE_VERTICAL:
				h=viewHeight;
				clutter_actor_get_preferred_width(CLUTTER_ACTOR(priv->activeView), h, NULL, &w);
				break;

			default:
				clutter_actor_get_preferred_size(CLUTTER_ACTOR(priv->activeView), NULL, NULL, &w, &h);
				break;
		}

		box=clutter_actor_box_new(0, 0, w, h);
		clutter_actor_allocate(CLUTTER_ACTOR(priv->activeView), box, inFlags);
		clutter_actor_box_free(box);

		clutter_actor_set_clip(CLUTTER_ACTOR(priv->activeView), x, y, viewWidth, viewHeight);
	}

	/* Only set value if it changes */
	if(priv->hScrollbarVisible!=hScrollbarVisible)
	{
		/* Set new value */
		priv->hScrollbarVisible=hScrollbarVisible;

		/* Notify about property change */
		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardViewpadProperties[PROP_HSCROLLBAR_VISIBLE]);
	}

	if(priv->vScrollbarVisible!=vScrollbarVisible)
	{
		/* Set new value */
		priv->vScrollbarVisible=vScrollbarVisible;

		/* Notify about property change */
		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardViewpadProperties[PROP_VSCROLLBAR_VISIBLE]);
	}
}
Ejemplo n.º 20
0
static void
mx_combo_box_allocate (ClutterActor          *actor,
                       const ClutterActorBox *box,
                       ClutterAllocationFlags flags)
{
  MxComboBoxPrivate *priv = MX_COMBO_BOX (actor)->priv;
  MxPadding padding;
  gfloat x, y, width, height;
  gfloat min_menu_h, nat_menu_h;
  gfloat label_h;
  gfloat nat_icon_h, icon_h, icon_w;
  gfloat nat_marker_h, marker_h, marker_w;
  ClutterActorBox childbox;
  ClutterActor *menu, *stage;

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

  mx_widget_get_padding (MX_WIDGET (actor), &padding);

  x = padding.left;
  y = padding.top;
  width = box->x2 - box->x1 - padding.left - padding.right;
  height = box->y2 - box->y1 - padding.top - padding.bottom;

  icon_w = marker_w = 0;

  if (priv->icon)
    {
      /* Allocate the icon, if there is one, the space not used by the text */
      clutter_actor_get_preferred_height (priv->icon, -1, NULL, &nat_icon_h);

      if (height >= nat_icon_h)
        {
          icon_h = nat_icon_h;
          clutter_actor_get_preferred_width (priv->icon, -1, NULL, &icon_w);
        }
      else
        {
          icon_h = height;
          clutter_actor_get_preferred_width (priv->icon, icon_h, NULL, &icon_w);
        }

      childbox.x1 = (int)(x);
      childbox.y1 = (int)(y + (height - icon_h) / 2);
      childbox.x2 = (int)(x + icon_w);
      childbox.y2 = (int)(childbox.y1 + icon_h);

      clutter_actor_allocate (priv->icon, &childbox, flags);

      icon_w += priv->spacing;
    }

  if (priv->marker)
    {
      clutter_actor_get_preferred_height (priv->marker, -1,
                                          NULL, &nat_marker_h);

      if (height >= nat_marker_h)
        {
          marker_h = nat_marker_h;
          clutter_actor_get_preferred_width (priv->marker, -1, NULL, &marker_w);
        }
      else
        {
          marker_h = height;
          clutter_actor_get_preferred_width (priv->marker, marker_h,
                                             NULL, &marker_w);
        }

      childbox.x2 = (int)(x + width);
      childbox.x1 = (int)(childbox.x2 - marker_w);
      childbox.y1 = (int)(y + (height - marker_h) / 2);
      childbox.y2 = (int)(childbox.y1 + marker_h);

      clutter_actor_allocate (priv->marker, &childbox, flags);

      marker_w += priv->spacing;
    }

  clutter_actor_get_preferred_height (priv->label, -1, NULL, &label_h);

  childbox.x1 = (int)(x + icon_w);
  childbox.y1 = (int)(y + (height / 2 - label_h / 2));
  childbox.x2 = (int)(x + width - marker_w);
  childbox.y2 = (int)(childbox.y1 + label_h);
  clutter_actor_allocate (priv->label, &childbox, flags);

  menu = (ClutterActor*) mx_widget_get_menu (MX_WIDGET (actor));
  clutter_actor_get_preferred_height (menu, (box->x2 - box->x1), &min_menu_h,
                                      &nat_menu_h);

  childbox.x1 = 0;
  childbox.x2 = (box->x2 - box->x1);
  childbox.y1 = (box->y2 - box->y1);

  stage = clutter_actor_get_stage (actor);
  if (stage != NULL)
    {
      ClutterVertex point = { 0, };
      gfloat stage_w, stage_h, combo_h = box->y2 - box->y1;

      clutter_actor_get_size (stage, &stage_w, &stage_h);
      point.y = combo_h + nat_menu_h;
      clutter_actor_apply_transform_to_point (actor, &point, &point);

      /* If the menu would appear off the stage, flip it around. */
      if ((point.x < 0) || (point.x >= stage_w) ||
          (point.y < 0) || (point.y >= stage_h))
        {
          childbox.y1 = -nat_menu_h;
        }
    }

  childbox.y2 = childbox.y1 + nat_menu_h;
  clutter_actor_allocate (menu, &childbox, flags);
}
Ejemplo n.º 21
0
static void
clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
                              ClutterContainer       *container,
                              const ClutterActorBox  *allocation,
                              ClutterAllocationFlags  flags)
{
  ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
  GList *l, *children = clutter_container_get_children (container);
  gfloat avail_width, avail_height;
  gfloat item_x, item_y;
  gint line_item_count;
  gint items_per_line;
  gint line_index;

  if (children == NULL)
    return;

  clutter_actor_box_get_size (allocation, &avail_width, &avail_height);

  items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager),
                                  avail_width, avail_height);

  item_x = item_y = 0;

  line_item_count = 0;
  line_index = 0;

  for (l = children; l != NULL; l = l->next)
    {
      ClutterActor *child = l->data;
      ClutterActorBox child_alloc;
      gfloat item_width, item_height;
      gfloat new_x, new_y;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      new_x = new_y = 0;

      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
        {
          if (line_item_count == items_per_line && line_item_count > 0)
            {
              item_y += g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

              if (line_index >= 0)
                item_y += priv->row_spacing;

              line_item_count = 0;
              line_index += 1;

              item_x = 0;
            }

          new_x = ((line_item_count + 1) * (avail_width + priv->col_spacing))
                / items_per_line;
          item_width = new_x - item_x - priv->col_spacing;
          item_height = g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

          if (!priv->is_homogeneous)
            {
              gfloat child_min, child_natural;

              clutter_actor_get_preferred_width (child, item_height,
                                                 &child_min,
                                                 &child_natural);
              item_width = MIN (item_width, child_natural);

              clutter_actor_get_preferred_height (child, item_width,
                                                  &child_min,
                                                  &child_natural);
              item_height = MIN (item_height, child_natural);
            }
        }
      else
        {
          if (line_item_count == items_per_line && line_item_count > 0)
            {
              item_x += g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

              if (line_index >= 0)
                item_x += priv->col_spacing;

              line_item_count = 0;
              line_index += 1;

              item_y = 0;
            }

          new_y = ((line_item_count + 1) * (avail_height + priv->row_spacing))
                / items_per_line;
          item_height = new_y - item_y - priv->row_spacing;
          item_width = g_array_index (priv->line_natural,
                                      gfloat,
                                      line_index);

          if (!priv->is_homogeneous)
            {
              gfloat child_min, child_natural;

              clutter_actor_get_preferred_width (child, item_height,
                                                 &child_min,
                                                 &child_natural);
              item_width = MIN (item_width, child_natural);

              clutter_actor_get_preferred_height (child, item_width,
                                                  &child_min,
                                                  &child_natural);
              item_height = MIN (item_height, child_natural);
            }
        }

      CLUTTER_NOTE (LAYOUT,
                    "flow[line:%d, item:%d/%d] ="
                    "{ %.2f, %.2f, %.2f, %.2f }",
                    line_index, line_item_count + 1, items_per_line,
                    item_x, item_y, item_width, item_height);

      child_alloc.x1 = ceil (item_x);
      child_alloc.y1 = ceil (item_y);
      child_alloc.x2 = ceil (child_alloc.x1 + item_width);
      child_alloc.y2 = ceil (child_alloc.y1 + item_height);
      clutter_actor_allocate (child, &child_alloc, flags);

      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
        item_x = new_x;
      else
        item_y = new_y;

      line_item_count += 1;
    }

  g_list_free (children);
}
Ejemplo n.º 22
0
static void
clutter_flow_layout_allocate (ClutterLayoutManager   *manager,
                              ClutterContainer       *container,
                              const ClutterActorBox  *allocation,
                              ClutterAllocationFlags  flags)
{
  ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
  ClutterActor *actor, *child;
  ClutterActorIter iter;
  gfloat x_off, y_off;
  gfloat avail_width, avail_height;
  gfloat item_x, item_y;
  gint line_item_count;
  gint items_per_line;
  gint line_index;

  actor = CLUTTER_ACTOR (container);
  if (clutter_actor_get_n_children (actor) == 0)
    return;

  clutter_actor_box_get_origin (allocation, &x_off, &y_off);
  clutter_actor_box_get_size (allocation, &avail_width, &avail_height);

  /* blow the cached preferred size and re-compute with the given
   * available size in case the FlowLayout wasn't given the exact
   * size it requested
   */
  if ((priv->req_width >= 0 && avail_width != priv->req_width) ||
      (priv->req_height >= 0 && avail_height != priv->req_height))
    {
      clutter_flow_layout_get_preferred_width (manager, container,
                                               avail_height,
                                               NULL, NULL);
      clutter_flow_layout_get_preferred_height (manager, container,
                                                avail_width,
                                                NULL, NULL);
    }

  items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager),
                                  avail_width, avail_height);

  item_x = x_off;
  item_y = y_off;

  line_item_count = 0;
  line_index = 0;

  clutter_actor_iter_init (&iter, actor);
  while (clutter_actor_iter_next (&iter, &child))
    {
      ClutterActorBox child_alloc;
      gfloat item_width, item_height;
      gfloat new_x, new_y;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      new_x = new_y = 0;

      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
        {
          if (line_item_count == items_per_line && line_item_count > 0)
            {
              item_y += g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

              if (line_index >= 0)
                item_y += priv->row_spacing;

              line_item_count = 0;
              line_index += 1;

              item_x = x_off;
            }

          new_x = x_off + ((line_item_count + 1) * (avail_width + priv->col_spacing))
                / items_per_line;
          item_width = new_x - item_x - priv->col_spacing;
          item_height = g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

          if (!priv->is_homogeneous)
            {
              gfloat child_min, child_natural;

              clutter_actor_get_preferred_width (child, item_height,
                                                 &child_min,
                                                 &child_natural);
              item_width = MIN (item_width, child_natural);

              clutter_actor_get_preferred_height (child, item_width,
                                                  &child_min,
                                                  &child_natural);
              item_height = MIN (item_height, child_natural);
            }
        }
      else
        {
          if (line_item_count == items_per_line && line_item_count > 0)
            {
              item_x += g_array_index (priv->line_natural,
                                       gfloat,
                                       line_index);

              if (line_index >= 0)
                item_x += priv->col_spacing;

              line_item_count = 0;
              line_index += 1;

              item_y = y_off;
            }

          new_y = y_off + ((line_item_count + 1) * (avail_height + priv->row_spacing))
                / items_per_line;
          item_height = new_y - item_y - priv->row_spacing;
          item_width = g_array_index (priv->line_natural,
                                      gfloat,
                                      line_index);

          if (!priv->is_homogeneous)
            {
              gfloat child_min, child_natural;

              clutter_actor_get_preferred_width (child, item_height,
                                                 &child_min,
                                                 &child_natural);
              item_width = MIN (item_width, child_natural);

              clutter_actor_get_preferred_height (child, item_width,
                                                  &child_min,
                                                  &child_natural);
              item_height = MIN (item_height, child_natural);
            }
        }

      CLUTTER_NOTE (LAYOUT,
                    "flow[line:%d, item:%d/%d] ="
                    "{ %.2f, %.2f, %.2f, %.2f }",
                    line_index, line_item_count + 1, items_per_line,
                    item_x, item_y, item_width, item_height);

      child_alloc.x1 = ceil (item_x);
      child_alloc.y1 = ceil (item_y);
      child_alloc.x2 = ceil (child_alloc.x1 + item_width);
      child_alloc.y2 = ceil (child_alloc.y1 + item_height);
      clutter_actor_allocate (child, &child_alloc, flags);

      if (priv->orientation == CLUTTER_FLOW_HORIZONTAL)
        item_x = new_x;
      else
        item_y = new_y;

      line_item_count += 1;
    }
}
Ejemplo n.º 23
0
static void
clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager,
                                         ClutterContainer     *container,
                                         gfloat                for_height,
                                         gfloat               *min_width_p,
                                         gfloat               *nat_width_p)
{
  ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv;
  gint n_rows, line_item_count, line_count;
  gfloat total_min_width, total_natural_width;
  gfloat line_min_width, line_natural_width;
  gfloat max_min_width, max_natural_width;
  ClutterActor *actor, *child;
  ClutterActorIter iter;
  gfloat item_y;

  n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height);

  total_min_width = 0;
  total_natural_width = 0;

  line_min_width = 0;
  line_natural_width = 0;

  line_item_count = 0;
  line_count = 0;

  item_y = 0;

  actor = CLUTTER_ACTOR (container);

  /* clear the line width arrays */
  if (priv->line_min != NULL)
    g_array_free (priv->line_min, TRUE);

  if (priv->line_natural != NULL)
    g_array_free (priv->line_natural, TRUE);

  priv->line_min = g_array_sized_new (FALSE, FALSE,
                                      sizeof (gfloat),
                                      16);
  priv->line_natural = g_array_sized_new (FALSE, FALSE,
                                          sizeof (gfloat),
                                          16);

  if (clutter_actor_get_n_children (actor) != 0)
    line_count = 1;

  max_min_width = max_natural_width = 0;

  clutter_actor_iter_init (&iter, actor);
  while (clutter_actor_iter_next (&iter, &child))
    {
      gfloat child_min, child_natural;
      gfloat new_y, item_height;

      if (!CLUTTER_ACTOR_IS_VISIBLE (child))
        continue;

      if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
        {
          clutter_actor_get_preferred_height (child, -1,
                                              &child_min,
                                              &child_natural);

          if ((priv->snap_to_grid && line_item_count == n_rows) ||
              (!priv->snap_to_grid && item_y + child_natural > for_height))
            {
              total_min_width += line_min_width;
              total_natural_width += line_natural_width;

              g_array_append_val (priv->line_min,
                                  line_min_width);
              g_array_append_val (priv->line_natural,
                                  line_natural_width);

              line_min_width = line_natural_width = 0;

              line_item_count = 0;
              line_count += 1;
              item_y = 0;
            }

          if (priv->snap_to_grid)
            {
              new_y = ((line_item_count + 1) * (for_height + priv->row_spacing))
                    / n_rows;
              item_height = new_y - item_y - priv->row_spacing;
            }
          else
            {
              new_y = item_y + child_natural + priv->row_spacing;
              item_height = child_natural;
            }

          clutter_actor_get_preferred_width (child, item_height,
                                             &child_min,
                                             &child_natural);

          line_min_width = MAX (line_min_width, child_min);
          line_natural_width = MAX (line_natural_width, child_natural);

          item_y = new_y;
          line_item_count += 1;

          max_min_width = MAX (max_min_width, line_min_width);
          max_natural_width = MAX (max_natural_width, line_natural_width);
        }
      else
        {
          clutter_actor_get_preferred_width (child, for_height,
                                             &child_min,
                                             &child_natural);

          max_min_width = MAX (max_min_width, child_min);
          max_natural_width = MAX (max_natural_width, child_natural);

          total_min_width += max_min_width;
          total_natural_width += max_natural_width;
          line_count += 1;
        }
    }

  priv->col_width = max_natural_width;

  if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width)
    priv->col_width = MAX (priv->max_col_width, max_min_width);

  if (priv->col_width < priv->min_col_width)
    priv->col_width = priv->min_col_width;

  if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0)
    {
      /* if we have a non-full row we need to add it */
      if (line_item_count > 0)
        {
          total_min_width += line_min_width;
          total_natural_width += line_natural_width;

          g_array_append_val (priv->line_min,
                              line_min_width);
          g_array_append_val (priv->line_natural,
                              line_natural_width);
        }

      priv->line_count = line_count;

      if (priv->line_count > 0)
        {
          gfloat total_spacing;

          total_spacing = priv->col_spacing * (priv->line_count - 1);

          total_min_width += total_spacing;
          total_natural_width += total_spacing;
        }
    }
  else
    {
      g_array_append_val (priv->line_min, line_min_width);
      g_array_append_val (priv->line_natural, line_natural_width);

      priv->line_count = line_count;

      if (priv->line_count > 0)
        {
          gfloat total_spacing;

          total_spacing = priv->col_spacing * (priv->line_count - 1);

          total_min_width += total_spacing;
          total_natural_width += total_spacing;
        }
    }

  CLUTTER_NOTE (LAYOUT,
                "Flow[w]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f",
                n_rows, priv->line_count,
                total_min_width,
                total_natural_width,
                for_height);

  priv->req_height = for_height;

  if (min_width_p)
    *min_width_p = max_min_width;

  if (nat_width_p)
    *nat_width_p = total_natural_width;
}