[gtk+/treeview-refactor] GtkCellArea now paints focus on cells
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/treeview-refactor] GtkCellArea now paints focus on cells
- Date: Thu, 11 Nov 2010 07:09:59 +0000 (UTC)
commit f330b40521cf1f5b2b33b33bf7c5df0f59355838
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Thu Nov 11 16:13:06 2010 +0900
GtkCellArea now paints focus on cells
Added concept of "Focus Siblings" to GtkCellArea so that some
static text/icon may be included in the focus/click area of
an activatable or editable cell, implemented focus drawing
as well, updated testcellarea to reflect the changes.
gtk/gtkcellarea.c | 173 +++++++++++++++++++++++++++++++++++++++++++++-
gtk/gtkcellarea.h | 25 ++++++-
gtk/gtkcellareabox.c | 98 ++++++++++++++++++++++----
tests/cellareascaffold.c | 19 +++---
tests/testcellarea.c | 26 ++++++-
5 files changed, 311 insertions(+), 30 deletions(-)
---
diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c
index de24277..e34c163 100644
--- a/gtk/gtkcellarea.c
+++ b/gtk/gtkcellarea.c
@@ -97,6 +97,13 @@ static void gtk_cell_area_reorder (GtkCellLayout
gint position);
static GList *gtk_cell_area_get_cells (GtkCellLayout *cell_layout);
+
+/* Used in forall loop to check if a child renderer is present */
+typedef struct {
+ GtkCellRenderer *renderer;
+ gboolean has_renderer;
+} HasRendererCheck;
+
/* Attribute/Cell metadata */
typedef struct {
const gchar *attribute;
@@ -154,6 +161,9 @@ struct _GtkCellAreaPrivate
*/
GHashTable *cell_info;
+ /* Tracking which cells are focus siblings of focusable cells */
+ GHashTable *focus_siblings;
+
/* The cell border decides how much space to reserve
* around each cell for the background_area
*/
@@ -222,6 +232,11 @@ gtk_cell_area_init (GtkCellArea *area)
NULL,
(GDestroyNotify)cell_info_free);
+ priv->focus_siblings = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)g_list_free);
+
priv->cell_border.left = 0;
priv->cell_border.right = 0;
priv->cell_border.top = 0;
@@ -470,9 +485,10 @@ gtk_cell_area_finalize (GObject *object)
GtkCellAreaPrivate *priv = area->priv;
/* All cell renderers should already be removed at this point,
- * just kill our hash table here.
+ * just kill our (empty) hash tables here.
*/
g_hash_table_destroy (priv->cell_info);
+ g_hash_table_destroy (priv->focus_siblings);
g_free (priv->current_path);
@@ -858,6 +874,7 @@ gtk_cell_area_remove (GtkCellArea *area,
{
GtkCellAreaClass *class;
GtkCellAreaPrivate *priv;
+ GList *renderers, *l;
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
@@ -868,6 +885,25 @@ gtk_cell_area_remove (GtkCellArea *area,
/* Remove any custom attributes and custom cell data func here first */
g_hash_table_remove (priv->cell_info, renderer);
+ /* Remove focus siblings of this renderer */
+ g_hash_table_remove (priv->focus_siblings, renderer);
+
+ /* Remove this renderer from any focus renderer's sibling list */
+ renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
+
+ for (l = renderers; l; l = l->next)
+ {
+ GtkCellRenderer *focus_renderer = l->data;
+
+ if (gtk_cell_area_is_focus_sibling (area, focus_renderer, renderer))
+ {
+ gtk_cell_area_remove_focus_sibling (area, focus_renderer, renderer);
+ break;
+ }
+ }
+
+ g_list_free (renderers);
+
if (class->remove)
class->remove (area, renderer);
else
@@ -875,6 +911,28 @@ gtk_cell_area_remove (GtkCellArea *area,
g_type_name (G_TYPE_FROM_INSTANCE (area)));
}
+static void
+get_has_renderer (GtkCellRenderer *renderer,
+ HasRendererCheck *check)
+{
+ if (renderer == check->renderer)
+ check->has_renderer = TRUE;
+}
+
+gboolean
+gtk_cell_area_has_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ HasRendererCheck check = { renderer, FALSE };
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
+
+ gtk_cell_area_forall (area, (GtkCellCallback)get_has_renderer, &check);
+
+ return check.has_renderer;
+}
+
/**
* gtk_cell_area_forall
* @area: a #GtkCellArea
@@ -972,8 +1030,10 @@ gtk_cell_area_render (GtkCellArea *area,
GtkCellAreaIter *iter,
GtkWidget *widget,
cairo_t *cr,
+ const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- GtkCellRendererState flags)
+ GtkCellRendererState flags,
+ gboolean paint_focus)
{
GtkCellAreaClass *class;
@@ -981,12 +1041,13 @@ gtk_cell_area_render (GtkCellArea *area,
g_return_if_fail (GTK_IS_CELL_AREA_ITER (iter));
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (cr != NULL);
+ g_return_if_fail (background_area != NULL);
g_return_if_fail (cell_area != NULL);
class = GTK_CELL_AREA_GET_CLASS (area);
if (class->render)
- class->render (area, iter, widget, cr, cell_area, flags);
+ class->render (area, iter, widget, cr, background_area, cell_area, flags, paint_focus);
else
g_warning ("GtkCellAreaClass::render not implemented for `%s'",
g_type_name (G_TYPE_FROM_INSTANCE (area)));
@@ -1136,6 +1197,7 @@ gtk_cell_area_attribute_connect (GtkCellArea *area,
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
g_return_if_fail (attribute != NULL);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
priv = area->priv;
info = g_hash_table_lookup (priv->cell_info, renderer);
@@ -1202,6 +1264,7 @@ gtk_cell_area_attribute_disconnect (GtkCellArea *area,
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
g_return_if_fail (attribute != NULL);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
priv = area->priv;
info = g_hash_table_lookup (priv->cell_info, renderer);
@@ -1822,6 +1885,110 @@ gtk_cell_area_get_focus_cell (GtkCellArea *area)
/*************************************************************
+ * API: Focus Siblings *
+ *************************************************************/
+void
+gtk_cell_area_add_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
+ g_return_if_fail (renderer != sibling);
+ g_return_if_fail (gtk_cell_area_has_renderer (area, renderer));
+ g_return_if_fail (gtk_cell_area_has_renderer (area, sibling));
+ g_return_if_fail (!gtk_cell_area_is_focus_sibling (area, renderer, sibling));
+
+ /* XXX We should also check that sibling is not in any other renderer's sibling
+ * list already, a renderer can be sibling of only one focusable renderer
+ * at a time.
+ */
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ if (siblings)
+ siblings = g_list_append (siblings, sibling);
+ else
+ {
+ siblings = g_list_append (siblings, sibling);
+ g_hash_table_insert (priv->focus_siblings, renderer, siblings);
+ }
+}
+
+void
+gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings;
+
+ g_return_if_fail (GTK_IS_CELL_AREA (area));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
+ g_return_if_fail (GTK_IS_CELL_RENDERER (sibling));
+ g_return_if_fail (gtk_cell_area_is_focus_sibling (area, renderer, sibling));
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ siblings = g_list_copy (siblings);
+ siblings = g_list_remove (siblings, sibling);
+
+ if (!siblings)
+ g_hash_table_remove (priv->focus_siblings, renderer);
+ else
+ g_hash_table_insert (priv->focus_siblings, renderer, siblings);
+}
+
+gboolean
+gtk_cell_area_is_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling)
+{
+ GtkCellAreaPrivate *priv;
+ GList *siblings, *l;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), FALSE);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (sibling), FALSE);
+
+ priv = area->priv;
+
+ siblings = g_hash_table_lookup (priv->focus_siblings, renderer);
+
+ for (l = siblings; l; l = l->next)
+ {
+ GtkCellRenderer *a_sibling = l->data;
+
+ if (a_sibling == sibling)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const GList *
+gtk_cell_area_get_focus_siblings (GtkCellArea *area,
+ GtkCellRenderer *renderer)
+{
+ GtkCellAreaPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CELL_AREA (area), NULL);
+ g_return_val_if_fail (GTK_IS_CELL_RENDERER (renderer), NULL);
+
+ priv = area->priv;
+
+ return g_hash_table_lookup (priv->focus_siblings, renderer);
+}
+
+/*************************************************************
* API: Cell Activation/Editing *
*************************************************************/
static void
diff --git a/gtk/gtkcellarea.h b/gtk/gtkcellarea.h
index 6271e99..c120354 100644
--- a/gtk/gtkcellarea.h
+++ b/gtk/gtkcellarea.h
@@ -95,8 +95,10 @@ struct _GtkCellAreaClass
GtkCellAreaIter *iter,
GtkWidget *widget,
cairo_t *cr,
+ const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- GtkCellRendererState flags);
+ GtkCellRendererState flags,
+ gboolean paint_focus);
/* Geometry */
GtkCellAreaIter *(* create_iter) (GtkCellArea *area);
@@ -165,6 +167,8 @@ void gtk_cell_area_add (GtkCellArea
GtkCellRenderer *renderer);
void gtk_cell_area_remove (GtkCellArea *area,
GtkCellRenderer *renderer);
+gboolean gtk_cell_area_has_renderer (GtkCellArea *area,
+ GtkCellRenderer *renderer);
void gtk_cell_area_forall (GtkCellArea *area,
GtkCellCallback callback,
gpointer callback_data);
@@ -184,8 +188,10 @@ void gtk_cell_area_render (GtkCellArea
GtkCellAreaIter *iter,
GtkWidget *widget,
cairo_t *cr,
+ const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- GtkCellRendererState flags);
+ GtkCellRendererState flags,
+ gboolean paint_focus);
/* Geometry */
GtkCellAreaIter *gtk_cell_area_create_iter (GtkCellArea *area);
@@ -283,6 +289,21 @@ void gtk_cell_area_set_focus_cell (GtkCellArea
GtkCellRenderer *renderer);
GtkCellRenderer *gtk_cell_area_get_focus_cell (GtkCellArea *area);
+
+/* Focus siblings */
+void gtk_cell_area_add_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+void gtk_cell_area_remove_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+gboolean gtk_cell_area_is_focus_sibling (GtkCellArea *area,
+ GtkCellRenderer *renderer,
+ GtkCellRenderer *sibling);
+G_CONST_RETURN GList *gtk_cell_area_get_focus_siblings (GtkCellArea *area,
+ GtkCellRenderer *renderer);
+
+
/* Cell Activation/Editing */
void gtk_cell_area_set_edited_cell (GtkCellArea *area,
GtkCellRenderer *renderer);
diff --git a/gtk/gtkcellareabox.c b/gtk/gtkcellareabox.c
index 66058d8..45355ae 100644
--- a/gtk/gtkcellareabox.c
+++ b/gtk/gtkcellareabox.c
@@ -66,8 +66,10 @@ static void gtk_cell_area_box_render (GtkCellArea
GtkCellAreaIter *iter,
GtkWidget *widget,
cairo_t *cr,
+ const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- GtkCellRendererState flags);
+ GtkCellRendererState flags,
+ gboolean paint_focus);
static void gtk_cell_area_box_set_cell_property (GtkCellArea *area,
GtkCellRenderer *renderer,
guint prop_id,
@@ -900,15 +902,19 @@ gtk_cell_area_box_render (GtkCellArea *area,
GtkCellAreaIter *iter,
GtkWidget *widget,
cairo_t *cr,
+ const GdkRectangle *background_area,
const GdkRectangle *cell_area,
- GtkCellRendererState flags)
+ GtkCellRendererState flags,
+ gboolean paint_focus)
{
GtkCellAreaBox *box = GTK_CELL_AREA_BOX (area);
GtkCellAreaBoxPrivate *priv = box->priv;
GtkCellAreaBoxIter *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
GSList *allocated_cells, *l;
- GdkRectangle background_area, inner_area;
+ GdkRectangle cell_background, inner_area;
GtkCellRenderer *focus_cell = NULL;
+ GdkRectangle focus_rect = { 0, };
+ gboolean first_focus_cell = TRUE;
if (flags & GTK_CELL_RENDERER_FOCUSED)
{
@@ -916,7 +922,7 @@ gtk_cell_area_box_render (GtkCellArea *area,
flags &= ~GTK_CELL_RENDERER_FOCUSED;
}
- background_area = *cell_area;
+ cell_background = *cell_area;
/* Get a list of cells with allocation sizes decided regardless
* of alignments and pack order etc. */
@@ -929,29 +935,95 @@ gtk_cell_area_box_render (GtkCellArea *area,
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
- background_area.x = cell_area->x + cell->position;
- background_area.width = cell->size;
+ cell_background.x = cell_area->x + cell->position;
+ cell_background.width = cell->size;
}
else
{
- background_area.y = cell_area->y + cell->position;
- background_area.height = cell->size;
+ cell_background.y = cell_area->y + cell->position;
+ cell_background.height = cell->size;
}
- if (cell->renderer == focus_cell)
- cell_fields |= GTK_CELL_RENDERER_FOCUSED;
-
/* Remove margins from the background area to produce the cell area
*/
- gtk_cell_area_inner_cell_area (area, &background_area, &inner_area);
+ gtk_cell_area_inner_cell_area (area, &cell_background, &inner_area);
+
+ if (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;
+ gint opposite_size, x_offset, y_offset;
+
+ cell_focus = inner_area;
+
+ /* Trim up the focus size */
+ if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gtk_cell_renderer_get_preferred_height_for_width (cell->renderer, widget,
+ cell_focus.width,
+ NULL, &opposite_size);
+
+ cell_focus.height = MIN (opposite_size, cell_focus.height);
+ }
+ else
+ {
+ gtk_cell_renderer_get_preferred_width_for_height (cell->renderer, widget,
+ cell_focus.height,
+ NULL, &opposite_size);
+
+ cell_focus.width = MIN (opposite_size, cell_focus.width);
+ }
+
+ /* offset the cell position */
+ _gtk_cell_renderer_calc_offset (cell->renderer, &inner_area, GTK_TEXT_DIR_LTR,
+ cell_focus.width, cell_focus.height,
+ &x_offset, &y_offset);
+
+ cell_focus.x += x_offset;
+ cell_focus.y += y_offset;
+
+ /* 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,
- &background_area, &inner_area,
+ &cell_background, &inner_area,
flags | cell_fields);
}
+ 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,
+ /* XXX This hint should be a property on GtkCellArea I suppose */
+ "treeview",
+ 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);
}
diff --git a/tests/cellareascaffold.c b/tests/cellareascaffold.c
index c0b72d7..6d40be9 100644
--- a/tests/cellareascaffold.c
+++ b/tests/cellareascaffold.c
@@ -405,7 +405,9 @@ cell_area_scaffold_draw (GtkWidget *widget,
}
gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
- gtk_cell_area_render (priv->area, priv->iter, widget, cr, &render_area, flags);
+ gtk_cell_area_render (priv->area, priv->iter, widget, cr,
+ &render_area, &render_area, flags,
+ (have_focus && i == priv->focus_row));
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
@@ -726,13 +728,11 @@ cell_area_scaffold_focus (GtkWidget *widget,
/* If focus stays in the area we dont need to do any more */
if (gtk_cell_area_focus (priv->area, direction))
{
- GtkCellRenderer *renderer = gtk_cell_area_get_focus_cell (priv->area);
-
priv->focus_row = focus_row;
-
- g_print ("focusing in direction %s: focus set on a %s in row %d\n",
- DIRECTION_STR (direction), G_OBJECT_TYPE_NAME (renderer), priv->focus_row);
-
+
+ /* XXX A smarter implementation would only invalidate the rectangles where
+ * focus was removed from and new focus was placed */
+ gtk_widget_queue_draw (widget);
return TRUE;
}
else
@@ -802,8 +802,9 @@ cell_area_scaffold_focus (GtkWidget *widget,
}
}
- g_print ("focus leaving with no cells in focus (direction %s, focus_row %d)\n",
- DIRECTION_STR (direction), priv->focus_row);
+ /* XXX A smarter implementation would only invalidate the rectangles where
+ * focus was removed from and new focus was placed */
+ gtk_widget_queue_draw (widget);
return FALSE;
}
diff --git a/tests/testcellarea.c b/tests/testcellarea.c
index d547f71..0acf2eb 100644
--- a/tests/testcellarea.c
+++ b/tests/testcellarea.c
@@ -246,6 +246,8 @@ simple_cell_area (void)
/*******************************************************
* Focus Test *
*******************************************************/
+static GtkCellRenderer *focus_renderer, *sibling_renderer;
+
enum {
FOCUS_COLUMN_NAME,
FOCUS_COLUMN_CHECK,
@@ -328,7 +330,7 @@ focus_scaffold (void)
gtk_cell_area_attribute_connect (area, renderer, "text", FOCUS_COLUMN_NAME);
/* Catch signal ... */
- renderer = gtk_cell_renderer_toggle_new ();
+ focus_renderer = renderer = gtk_cell_renderer_toggle_new ();
g_object_set (G_OBJECT (renderer), "xalign", 0.0F, NULL);
gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, TRUE);
gtk_cell_area_attribute_connect (area, renderer, "active", FOCUS_COLUMN_CHECK);
@@ -336,7 +338,7 @@ focus_scaffold (void)
g_signal_connect (G_OBJECT (renderer), "toggled",
G_CALLBACK (cell_toggled), scaffold);
- renderer = gtk_cell_renderer_text_new ();
+ sibling_renderer = renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer),
"wrap-mode", PANGO_WRAP_WORD,
"wrap-width", 150,
@@ -347,6 +349,21 @@ focus_scaffold (void)
return scaffold;
}
+static void
+focus_sibling_toggled (GtkToggleButton *toggle,
+ CellAreaScaffold *scaffold)
+{
+ GtkCellArea *area = cell_area_scaffold_get_area (scaffold);
+ gboolean active = gtk_toggle_button_get_active (toggle);
+
+ if (active)
+ gtk_cell_area_add_focus_sibling (area, focus_renderer, sibling_renderer);
+ else
+ gtk_cell_area_remove_focus_sibling (area, focus_renderer, sibling_renderer);
+
+ gtk_widget_queue_draw (GTK_WIDGET (scaffold));
+}
+
static void
focus_cell_area (void)
@@ -375,10 +392,13 @@ focus_cell_area (void)
gtk_widget_show (vbox);
gtk_box_pack_end (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
- widget = gtk_check_button_new_with_label ("check button");
+ widget = gtk_check_button_new_with_label ("Focus Sibling");
gtk_widget_show (widget);
gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+ g_signal_connect (G_OBJECT (widget), "toggled",
+ G_CALLBACK (focus_sibling_toggled), scaffold);
+
gtk_container_add (GTK_CONTAINER (window), hbox);
gtk_widget_show (window);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]