From 34f4b5c190e8a1f9a7e942cb46d33858a8723161 Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Wed, 8 Dec 2010 21:18:05 +0900 Subject: [PATCH] Added 'background_area' calculation to GtkCellAreaClass->foreach_alloc vfunc This allows us to reduce code allocation code paths in subclasses, as a result GtkCellArea implements the ->render() vfunc and the subclass only decides the cell area and background area distributions in a single code path. --- gtk/gtkcellarea.c | 134 ++++++++++++++++++++++++++-- gtk/gtkcellarea.h | 6 +- gtk/gtkcellareabox.c | 204 +++++++++---------------------------------- 3 files changed, 173 insertions(+), 171 deletions(-) diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c index 1fabc94d0d..73831b9939 100644 --- a/gtk/gtkcellarea.c +++ b/gtk/gtkcellarea.c @@ -317,7 +317,7 @@ * The #GtkCellArea introduces cell properties for #GtkCellRenderers in very * much the same way that #GtkContainer introduces child properties * for #GtkWidgets. This provides some general interfaces for defining the relationship cell areas - * have with thier cells. For instance in a #GtkCellAreaBox a cell might "expand" and recieve extra + * have with their cells. For instance in a #GtkCellAreaBox a cell might "expand" and recieve extra * space when the area is allocated more than it's full natural request, or a cell might be configured * to "align" with adjacent rows which were requested and rendered with the same #GtkCellAreaContext. * @@ -371,6 +371,14 @@ static gint gtk_cell_area_real_event (GtkCellArea GdkEvent *event, const GdkRectangle *cell_area, GtkCellRendererState flags); +static void gtk_cell_area_real_render (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + cairo_t *cr, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus); static void gtk_cell_area_real_apply_attributes (GtkCellArea *area, GtkTreeModel *tree_model, GtkTreeIter *iter, @@ -430,6 +438,18 @@ typedef struct { GdkRectangle allocation; } RendererAllocationData; +/* Used in foreach loop to render cells */ +typedef struct { + GtkCellArea *area; + GtkWidget *widget; + cairo_t *cr; + GdkRectangle focus_rect; + GtkCellRendererState render_flags; + guint paint_focus : 1; + guint focus_all : 1; + guint first_focus : 1; +} CellRenderData; + /* Used in foreach loop to get a cell by position */ typedef struct { gint x; @@ -587,7 +607,7 @@ gtk_cell_area_class_init (GtkCellAreaClass *class) class->remove = NULL; class->foreach = NULL; class->event = gtk_cell_area_real_event; - class->render = NULL; + class->render = gtk_cell_area_real_render; class->apply_attributes = gtk_cell_area_real_apply_attributes; /* geometry */ @@ -1005,6 +1025,103 @@ gtk_cell_area_real_event (GtkCellArea *area, return retval; } +static gboolean +render_cell (GtkCellRenderer *renderer, + const GdkRectangle *cell_area, + const GdkRectangle *cell_background, + CellRenderData *data) +{ + GtkCellRenderer *focus_cell; + GtkCellRendererState flags; + GdkRectangle inner_area; + + focus_cell = gtk_cell_area_get_focus_cell (data->area); + flags = data->render_flags; + + gtk_cell_area_inner_cell_area (data->area, data->widget, cell_area, &inner_area); + + if ((flags & GTK_CELL_RENDERER_FOCUSED) && + (data->focus_all || + (focus_cell && + (renderer == focus_cell || + gtk_cell_area_is_focus_sibling (data->area, focus_cell, renderer))))) + { + GdkRectangle cell_focus; + + gtk_cell_renderer_get_aligned_area (renderer, data->widget, flags, &inner_area, &cell_focus); + + if (data->first_focus) + { + data->first_focus = FALSE; + data->focus_rect = cell_focus; + } + else + { + gdk_rectangle_union (&data->focus_rect, &cell_focus, &data->focus_rect); + } + } + else + flags &= ~GTK_CELL_RENDERER_FOCUSED; + + gtk_cell_renderer_render (renderer, data->cr, data->widget, + cell_background, &inner_area, flags); + + return FALSE; +} + +static void +gtk_cell_area_real_render (GtkCellArea *area, + GtkCellAreaContext *context, + GtkWidget *widget, + cairo_t *cr, + const GdkRectangle *background_area, + const GdkRectangle *cell_area, + GtkCellRendererState flags, + gboolean paint_focus) +{ + CellRenderData render_data = + { + area, + widget, + cr, + { 0, }, + flags, + paint_focus, + FALSE, TRUE + }; + + /* Make sure we dont paint a focus rectangle while there + * is an editable widget in play + */ + if (gtk_cell_area_get_edited_cell (area)) + render_data.paint_focus = FALSE; + + /* If no cell can activate but the caller wants focus painted, + * then we paint focus around all cells */ + if ((flags & GTK_CELL_RENDERER_FOCUSED) != 0 && paint_focus && + !gtk_cell_area_is_activatable (area)) + render_data.focus_all = TRUE; + + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, + (GtkCellAllocCallback)render_cell, &render_data); + + if (render_data.paint_focus && + render_data.focus_rect.width != 0 && + render_data.focus_rect.height != 0) + { + GtkStateType renderer_state = + flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED : + (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT : + (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL)); + + gtk_paint_focus (gtk_widget_get_style (widget), cr, + renderer_state, widget, + gtk_cell_area_get_style_detail (area), + render_data.focus_rect.x, render_data.focus_rect.y, + render_data.focus_rect.width, render_data.focus_rect.height); + } +} + static void apply_cell_attributes (GtkCellRenderer *renderer, CellInfo *info, @@ -1476,6 +1593,7 @@ gtk_cell_area_foreach (GtkCellArea *area, * @context: the #GtkCellAreaContext for this row of data. * @widget: the #GtkWidget that @area is rendering to * @cell_area: the @widget relative coordinates and size for @area + * @background_area: the @widget relative coordinates of the background area * @callback: the #GtkCellAllocCallback to call * @callback_data: user provided data pointer * @@ -1489,6 +1607,7 @@ gtk_cell_area_foreach_alloc (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, const GdkRectangle *cell_area, + const GdkRectangle *background_area, GtkCellAllocCallback callback, gpointer callback_data) { @@ -1503,7 +1622,7 @@ gtk_cell_area_foreach_alloc (GtkCellArea *area, class = GTK_CELL_AREA_GET_CLASS (area); if (class->foreach_alloc) - class->foreach_alloc (area, context, widget, cell_area, callback, callback_data); + class->foreach_alloc (area, context, widget, cell_area, background_area, callback, callback_data); else g_warning ("GtkCellAreaClass::foreach_alloc not implemented for `%s'", g_type_name (G_TYPE_FROM_INSTANCE (area))); @@ -1647,6 +1766,7 @@ gtk_cell_area_get_style_detail (GtkCellArea *area) static gboolean get_cell_allocation (GtkCellRenderer *renderer, const GdkRectangle *cell_area, + const GdkRectangle *cell_background, RendererAllocationData *data) { if (data->renderer == renderer) @@ -1687,7 +1807,7 @@ gtk_cell_area_get_cell_allocation (GtkCellArea *area, g_return_if_fail (cell_area != NULL); g_return_if_fail (allocation != NULL); - gtk_cell_area_foreach_alloc (area, context, widget, cell_area, + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, (GtkCellAllocCallback)get_cell_allocation, &data); *allocation = data.allocation; @@ -1696,6 +1816,7 @@ gtk_cell_area_get_cell_allocation (GtkCellArea *area, static gboolean get_cell_by_position (GtkCellRenderer *renderer, const GdkRectangle *cell_area, + const GdkRectangle *cell_background, CellByPositionData *data) { if (data->x >= cell_area->x && data->x < cell_area->x + cell_area->width && @@ -1723,7 +1844,7 @@ get_cell_by_position (GtkCellRenderer *renderer, * Gets the #GtkCellRenderer at @x and @y coordinates inside @area and optionally * returns the full cell allocation for it inside @cell_area. * - * Returns: the #GtkCellRenderer at @x and @y. + * Returns value: the #GtkCellRenderer at @x and @y. * * Since: 3.0 */ @@ -1745,7 +1866,7 @@ gtk_cell_area_get_cell_at_position (GtkCellArea *area, g_return_val_if_fail (x >= cell_area->x && x <= cell_area->x + cell_area->width, NULL); g_return_val_if_fail (y >= cell_area->y && y <= cell_area->y + cell_area->height, NULL); - gtk_cell_area_foreach_alloc (area, context, widget, cell_area, + gtk_cell_area_foreach_alloc (area, context, widget, cell_area, cell_area, (GtkCellAllocCallback)get_cell_by_position, &data); if (alloc_area) @@ -1754,7 +1875,6 @@ gtk_cell_area_get_cell_at_position (GtkCellArea *area, return data.renderer; } - /************************************************************* * API: Geometry * *************************************************************/ diff --git a/gtk/gtkcellarea.h b/gtk/gtkcellarea.h index c8021f73c7..15d2856711 100644 --- a/gtk/gtkcellarea.h +++ b/gtk/gtkcellarea.h @@ -76,6 +76,8 @@ typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer, * GtkCellAllocCallback: * @renderer: the cell renderer to operate on * @cell_area: the area allocated to @renderer inside the rectangle provided to gtk_cell_area_foreach_alloc(). + * @cell_background: the background area for @renderer inside the background + * area provided to gtk_cell_area_foreach_alloc(). * @data: user-supplied data * * The type of the callback functions used for iterating over @@ -86,6 +88,7 @@ typedef gboolean (*GtkCellCallback) (GtkCellRenderer *renderer, */ typedef gboolean (*GtkCellAllocCallback) (GtkCellRenderer *renderer, const GdkRectangle *cell_area, + const GdkRectangle *cell_background, gpointer data); @@ -174,6 +177,7 @@ struct _GtkCellAreaClass GtkCellAreaContext *context, GtkWidget *widget, const GdkRectangle *cell_area, + const GdkRectangle *background_area, GtkCellAllocCallback callback, gpointer callback_data); gint (* event) (GtkCellArea *area, @@ -273,6 +277,7 @@ void gtk_cell_area_foreach_alloc (GtkCellArea GtkCellAreaContext *context, GtkWidget *widget, const GdkRectangle *cell_area, + const GdkRectangle *background_area, GtkCellAllocCallback callback, gpointer callback_data); gint gtk_cell_area_event (GtkCellArea *area, @@ -308,7 +313,6 @@ GtkCellRenderer *gtk_cell_area_get_cell_at_position (GtkCellArea gint y, GdkRectangle *alloc_area); - /* Geometry */ GtkCellAreaContext *gtk_cell_area_create_context (GtkCellArea *area); GtkSizeRequestMode gtk_cell_area_get_request_mode (GtkCellArea *area); diff --git a/gtk/gtkcellareabox.c b/gtk/gtkcellareabox.c index e97767962f..2222b4b36d 100644 --- a/gtk/gtkcellareabox.c +++ b/gtk/gtkcellareabox.c @@ -78,16 +78,9 @@ static void gtk_cell_area_box_foreach_alloc (GtkCellArea GtkCellAreaContext *context, GtkWidget *widget, const GdkRectangle *cell_area, + const GdkRectangle *background_area, GtkCellAllocCallback callback, gpointer callback_data); -static void gtk_cell_area_box_render (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus); static void gtk_cell_area_box_set_cell_property (GtkCellArea *area, GtkCellRenderer *renderer, guint prop_id, @@ -262,7 +255,6 @@ gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class) area_class->remove = gtk_cell_area_box_remove; area_class->foreach = gtk_cell_area_box_foreach; area_class->foreach_alloc = gtk_cell_area_box_foreach_alloc; - area_class->render = gtk_cell_area_box_render; area_class->set_cell_property = gtk_cell_area_box_set_cell_property; area_class->get_cell_property = gtk_cell_area_box_get_cell_property; @@ -1040,6 +1032,7 @@ gtk_cell_area_box_foreach_alloc (GtkCellArea *area, GtkCellAreaContext *context, GtkWidget *widget, const GdkRectangle *cell_area, + const GdkRectangle *background_area, GtkCellAllocCallback callback, gpointer callback_data) { @@ -1047,9 +1040,13 @@ gtk_cell_area_box_foreach_alloc (GtkCellArea *area, GtkCellAreaBoxPrivate *priv = box->priv; GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); GSList *allocated_cells, *l; - GdkRectangle allocation; + GdkRectangle cell_alloc, cell_background; + gboolean rtl; - allocation = *cell_area; + rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); + + cell_alloc = *cell_area; /* Get a list of cells with allocation sizes decided regardless * of alignments and pack order etc. */ @@ -1062,96 +1059,23 @@ gtk_cell_area_box_foreach_alloc (GtkCellArea *area, if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { - allocation.x = cell_area->x + cell->position; - allocation.width = cell->size; + cell_alloc.x = cell_area->x + cell->position; + cell_alloc.width = cell->size; } else { - allocation.y = cell_area->y + cell->position; - allocation.height = cell->size; + cell_alloc.y = cell_area->y + cell->position; + cell_alloc.height = cell->size; } - if (callback (cell->renderer, &allocation, callback_data)) - break; - } - - g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL); - g_slist_free (allocated_cells); -} - -static void -gtk_cell_area_box_render (GtkCellArea *area, - GtkCellAreaContext *context, - GtkWidget *widget, - cairo_t *cr, - const GdkRectangle *background_area, - const GdkRectangle *cell_area, - GtkCellRendererState flags, - gboolean paint_focus) -{ - GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area); - GtkCellAreaBoxPrivate *priv = box->priv; - GtkCellAreaBoxContext *box_context = GTK_CELL_AREA_BOX_CONTEXT (context); - GSList *allocated_cells, *l; - GdkRectangle cell_background, inner_area; - GtkCellRenderer *focus_cell = NULL; - GdkRectangle focus_rect = { 0, }; - gboolean first_focus_cell = TRUE; - gboolean focus_all = FALSE; - gboolean rtl; - - rtl = (priv->orientation == GTK_ORIENTATION_HORIZONTAL && - gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); - - /* Make sure we dont paint a focus rectangle while there - * is an editable widget in play - */ - if (gtk_cell_area_get_edited_cell (area)) - paint_focus = FALSE; - - if (flags & GTK_CELL_RENDERER_FOCUSED) - { - focus_cell = gtk_cell_area_get_focus_cell (area); - flags &= ~GTK_CELL_RENDERER_FOCUSED; - - /* If no cell can activate but the caller wants focus painted, - * then we paint focus around all cells */ - if (paint_focus && !gtk_cell_area_is_activatable (area)) - focus_all = TRUE; - } - - cell_background = *cell_area; - - /* Get a list of cells with allocation sizes decided regardless - * of alignments and pack order etc. */ - allocated_cells = get_allocated_cells (box, box_context, widget, - cell_area->width, cell_area->height); - - for (l = allocated_cells; l; l = l->next) - { - AllocatedCell *cell = l->data; - GtkCellRendererState cell_fields = 0; - GdkRectangle render_background; - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - cell_background.x = cell_area->x + cell->position; - cell_background.width = cell->size; - } - else - { - cell_background.y = cell_area->y + cell->position; - cell_background.height = cell->size; - } - - /* Stop rendering cells if they flow out of the render area, + /* Stop iterating over cells if they flow out of the render area, * this can happen because the render area can actually be * smaller than the requested area (treeview columns can * be user resizable and can be resized to be smaller than * the actual requested area). */ - if (cell_background.x > cell_area->x + cell_area->width || - cell_background.x + cell_background.width < cell_area->x || - cell_background.y > cell_area->y + cell_area->height) + if (cell_alloc.x > cell_area->x + cell_area->width || + cell_alloc.x + cell_alloc.width < cell_area->x || + cell_alloc.y > cell_area->y + cell_area->height) break; /* Special case for the last cell (or first cell in rtl)... let the last cell consume @@ -1164,13 +1088,13 @@ gtk_cell_area_box_render (GtkCellArea *area, if (rtl) { /* Fill the leading space for the first cell in the area (still last in the list) */ - cell_background.width = (cell_background.x - cell_area->x) + cell_background.width; - cell_background.x = cell_area->x; + cell_alloc.width = (cell_alloc.x - cell_area->x) + cell_alloc.width; + cell_alloc.x = cell_area->x; } else { - cell_background.width = cell_area->x + cell_area->width - cell_background.x; - cell_background.height = cell_area->y + cell_area->height - cell_background.y; + cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; + cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; } } else @@ -1179,98 +1103,52 @@ gtk_cell_area_box_render (GtkCellArea *area, * so that the underlying renderer has a chance to deal with it (for instance * text renderers get a chance to ellipsize). */ - if (cell_background.x + cell_background.width > cell_area->x + cell_area->width) - cell_background.width = cell_area->x + cell_area->width - cell_background.x; + if (cell_alloc.x + cell_alloc.width > cell_area->x + cell_area->width) + cell_alloc.width = cell_area->x + cell_area->width - cell_alloc.x; - if (cell_background.y + cell_background.height > cell_area->y + cell_area->height) - cell_background.height = cell_area->y + cell_area->height - cell_background.y; + if (cell_alloc.y + cell_alloc.height > cell_area->y + cell_area->height) + cell_alloc.height = cell_area->y + cell_area->height - cell_alloc.y; } - /* Remove margins from the background area to produce the cell area - */ - gtk_cell_area_inner_cell_area (area, widget, &cell_background, &inner_area); - - /* Add portions of the background_area to the cell_background - * to create the render_background */ - render_background = cell_background; + /* Add portions of the background_area to the cell_alloc + * to create the cell_background */ + cell_background = cell_alloc; if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { if (l == allocated_cells) { - render_background.width += render_background.x - background_area->x; - render_background.x = background_area->x; + cell_background.width += cell_background.x - background_area->x; + cell_background.x = background_area->x; } if (l->next == NULL) - render_background.width = - background_area->width - (render_background.x - background_area->x); + cell_background.width = + background_area->width - (cell_background.x - background_area->x); - render_background.y = background_area->y; - render_background.height = background_area->height; + cell_background.y = background_area->y; + cell_background.height = background_area->height; } else { if (l == allocated_cells) { - render_background.height += render_background.y - background_area->y; - render_background.y = background_area->y; + cell_background.height += cell_background.y - background_area->y; + cell_background.y = background_area->y; } if (l->next == NULL) - render_background.height = - background_area->height - (render_background.y - background_area->y); + cell_background.height = + background_area->height - (cell_background.y - background_area->y); - render_background.x = background_area->x; - render_background.width = background_area->width; + cell_background.x = background_area->x; + cell_background.width = background_area->width; } - if (focus_all || - (focus_cell && - (cell->renderer == focus_cell || - gtk_cell_area_is_focus_sibling (area, focus_cell, cell->renderer)))) - { - cell_fields |= GTK_CELL_RENDERER_FOCUSED; - - if (paint_focus) - { - GdkRectangle cell_focus; - - gtk_cell_renderer_get_aligned_area (cell->renderer, widget, flags, &inner_area, &cell_focus); - - /* Accumulate the focus rectangle for all focus siblings */ - if (first_focus_cell) - { - focus_rect = cell_focus; - first_focus_cell = FALSE; - } - else - gdk_rectangle_union (&focus_rect, &cell_focus, &focus_rect); - } - } - - /* We have to do some per-cell considerations for the 'flags' - * for focus handling */ - gtk_cell_renderer_render (cell->renderer, cr, widget, - &render_background, &inner_area, - flags | cell_fields); + if (callback (cell->renderer, &cell_alloc, &cell_background, callback_data)) + break; } - if (paint_focus && focus_rect.width != 0 && focus_rect.height != 0) - { - GtkStateType renderer_state = - flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED : - (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT : - (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL)); - - gtk_paint_focus (gtk_widget_get_style (widget), cr, - renderer_state, widget, - gtk_cell_area_get_style_detail (area), - focus_rect.x, focus_rect.y, - focus_rect.width, focus_rect.height); - } - - g_slist_foreach (allocated_cells, (GFunc)allocated_cell_free, NULL); g_slist_free (allocated_cells); }