Ironed out the kinks in editing apis for GtkCellArea

- Added gtk_cell_area_aligned_cell_area() to get the aligned
   internal area use by a cell (for focus painting and for
   event areas).

 - Provide the event area in "editing-started" signal

 - Fire "remove-editable" when editing is canceled by the user,
   an implementing layouting widget need only catch "editing-started"
   and "remove-editable" now.

 - CellAreaScaffold/testcellarea now edit textrenderers.
This commit is contained in:
Tristan Van Berkom 2010-11-12 19:25:07 +09:00
parent 7e821aa980
commit 38666b406f
7 changed files with 290 additions and 96 deletions

View File

@ -132,7 +132,8 @@ static gint cell_attribute_find (CellAttribute *cell_attribut
/* Internal signal emissions */
static void gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable);
GtkCellEditable *editable,
GdkRectangle *cell_area);
static void gtk_cell_area_editing_canceled (GtkCellArea *area,
GtkCellRenderer *renderer);
static void gtk_cell_area_editing_done (GtkCellArea *area,
@ -292,10 +293,11 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
G_SIGNAL_RUN_FIRST,
0, /* No class closure here */
NULL, NULL,
_gtk_marshal_VOID__OBJECT_OBJECT_STRING,
G_TYPE_NONE, 3,
_gtk_marshal_VOID__OBJECT_OBJECT_BOXED_STRING,
G_TYPE_NONE, 4,
GTK_TYPE_CELL_RENDERER,
GTK_TYPE_CELL_EDITABLE,
GDK_TYPE_RECTANGLE,
G_TYPE_STRING);
cell_area_signals[SIGNAL_EDITING_CANCELED] =
@ -2079,10 +2081,11 @@ gtk_cell_area_get_focus_from_sibling (GtkCellArea *area,
static void
gtk_cell_area_editing_started (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *editable)
GtkCellEditable *editable,
GdkRectangle *cell_area)
{
g_signal_emit (area, cell_area_signals[SIGNAL_EDITING_STARTED], 0,
renderer, editable, area->priv->current_path);
renderer, editable, cell_area, area->priv->current_path);
}
static void
@ -2276,14 +2279,18 @@ gtk_cell_area_activate_cell (GtkCellArea *area,
if (editable_widget != NULL)
{
GdkRectangle edit_area;
g_return_val_if_fail (GTK_IS_CELL_EDITABLE (editable_widget), FALSE);
gtk_cell_area_set_edited_cell (area, renderer);
gtk_cell_area_set_edit_widget (area, editable_widget);
gtk_cell_area_aligned_cell_area (area, widget, renderer, &inner_area, &edit_area);
/* Signal that editing started so that callers can get
* a handle on the editable_widget */
gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget);
gtk_cell_area_editing_started (area, priv->focus_cell, editable_widget, &edit_area);
return TRUE;
}
@ -2304,6 +2311,9 @@ gtk_cell_area_stop_editing (GtkCellArea *area,
if (priv->edited_cell)
{
GtkCellEditable *edit_widget = g_object_ref (priv->edit_widget);
GtkCellRenderer *edit_cell = g_object_ref (priv->edited_cell);
/* Stop editing of the cell renderer */
gtk_cell_renderer_stop_editing (priv->edited_cell, canceled);
@ -2314,6 +2324,13 @@ gtk_cell_area_stop_editing (GtkCellArea *area,
/* Remove any references to the editable widget */
gtk_cell_area_set_edited_cell (area, NULL);
gtk_cell_area_set_edit_widget (area, NULL);
/* Send the remove-widget signal explicitly (this is done after setting
* the edit cell/widget NULL to avoid feedback)
*/
gtk_cell_area_remove_editable (area, edit_cell, edit_widget);
g_object_unref (edit_cell);
g_object_unref (edit_widget);
}
}
@ -2426,23 +2443,72 @@ gtk_cell_area_set_cell_margin_bottom (GtkCellArea *area,
void
gtk_cell_area_inner_cell_area (GtkCellArea *area,
const GdkRectangle *background_area,
GdkRectangle *cell_area)
const GdkRectangle *cell_area,
GdkRectangle *inner_area)
{
GtkCellAreaPrivate *priv;
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (background_area != NULL);
g_return_if_fail (cell_area != NULL);
g_return_if_fail (inner_area != NULL);
priv = area->priv;
*cell_area = *background_area;
*inner_area = *cell_area;
cell_area->x += priv->cell_border.left;
cell_area->width -= (priv->cell_border.left + priv->cell_border.right);
cell_area->y += priv->cell_border.top;
cell_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
inner_area->x += priv->cell_border.left;
inner_area->width -= (priv->cell_border.left + priv->cell_border.right);
inner_area->y += priv->cell_border.top;
inner_area->height -= (priv->cell_border.top + priv->cell_border.bottom);
}
void
gtk_cell_area_aligned_cell_area (GtkCellArea *area,
GtkWidget *widget,
GtkCellRenderer *renderer,
const GdkRectangle *cell_area,
GdkRectangle *aligned_area)
{
GtkCellAreaPrivate *priv;
gint opposite_size, x_offset, y_offset;
g_return_if_fail (GTK_IS_CELL_AREA (area));
g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (cell_area != NULL);
g_return_if_fail (aligned_area != NULL);
priv = area->priv;
*aligned_area = *cell_area;
/* Trim up the aligned size */
if (gtk_cell_renderer_get_request_mode (renderer) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
{
gtk_cell_renderer_get_preferred_height_for_width (renderer, widget,
aligned_area->width,
NULL, &opposite_size);
aligned_area->height = MIN (opposite_size, aligned_area->height);
}
else
{
gtk_cell_renderer_get_preferred_width_for_height (renderer, widget,
aligned_area->height,
NULL, &opposite_size);
aligned_area->width = MIN (opposite_size, aligned_area->width);
}
/* offset the cell position */
_gtk_cell_renderer_calc_offset (renderer, cell_area,
gtk_widget_get_direction (widget),
aligned_area->width,
aligned_area->height,
&x_offset, &y_offset);
aligned_area->x += x_offset;
aligned_area->y += y_offset;
}
void

View File

@ -343,7 +343,15 @@ void gtk_cell_area_set_cell_margin_bottom (GtkCellArea
/* Distinguish the inner cell area from the whole requested area including margins */
void gtk_cell_area_inner_cell_area (GtkCellArea *area,
const GdkRectangle *cell_area,
GdkRectangle *inner_cell_area);
GdkRectangle *inner_area);
/* Aligns a cell renderer into cell_area by requesting it's size ... used for focus and cell edit areas */
void gtk_cell_area_aligned_cell_area (GtkCellArea *area,
GtkWidget *widget,
GtkCellRenderer *renderer,
const GdkRectangle *cell_area,
GdkRectangle *aligned_area);
/* Request the size of a cell while respecting the cell margins (requests are margin inclusive) */
void gtk_cell_area_request_renderer (GtkCellArea *area,

View File

@ -903,11 +903,8 @@ gtk_cell_area_box_event (GtkCellArea *area,
GtkCellAreaBoxIter *box_iter = GTK_CELL_AREA_BOX_ITER (iter);
GSList *allocated_cells, *l;
GdkRectangle cell_background, inner_area;
GtkAllocation allocation;
gint event_x, event_y;
gtk_widget_get_allocation (widget, &allocation);
/* We may need some semantics to tell us the offset of the event
* window we are handling events for (i.e. GtkTreeView has a bin_window) */
event_x = button_event->x;
@ -1025,6 +1022,7 @@ gtk_cell_area_box_render (GtkCellArea *area,
{
AllocatedCell *cell = l->data;
GtkCellRendererState cell_fields = 0;
GdkRectangle render_background;
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
@ -1041,37 +1039,39 @@ gtk_cell_area_box_render (GtkCellArea *area,
*/
gtk_cell_area_inner_cell_area (area, &cell_background, &inner_area);
/* Here after getting the inner area of the cell background,
* add portions of the background area to the cell background */
/* Add portions of the background_area to the cell_background
* to create the render_background */
render_background = cell_background;
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
if (l == allocated_cells)
{
cell_background.width += cell_background.x - background_area->x;
cell_background.x = background_area->x;
render_background.width += render_background.x - background_area->x;
render_background.x = background_area->x;
}
if (l->next == NULL)
cell_background.width =
background_area->width - (cell_background.x - background_area->x);
render_background.width =
background_area->width - (render_background.x - background_area->x);
cell_background.y = background_area->y;
cell_background.height = background_area->height;
render_background.y = background_area->y;
render_background.height = background_area->height;
}
else
{
if (l == allocated_cells)
{
cell_background.height += cell_background.y - background_area->y;
cell_background.y = background_area->y;
render_background.height += render_background.y - background_area->y;
render_background.y = background_area->y;
}
if (l->next == NULL)
cell_background.height =
background_area->height - (cell_background.y - background_area->y);
render_background.height =
background_area->height - (render_background.y - background_area->y);
cell_background.x = background_area->x;
cell_background.width = background_area->width;
render_background.x = background_area->x;
render_background.width = background_area->width;
}
if (focus_cell &&
@ -1083,35 +1083,8 @@ gtk_cell_area_box_render (GtkCellArea *area,
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;
gtk_cell_area_aligned_cell_area (area, widget, cell->renderer, &inner_area, &cell_focus);
/* Accumulate the focus rectangle for all focus siblings */
if (first_focus_cell)
@ -1127,7 +1100,7 @@ gtk_cell_area_box_render (GtkCellArea *area,
/* We have to do some per-cell considerations for the 'flags'
* for focus handling */
gtk_cell_renderer_render (cell->renderer, cr, widget,
&cell_background, &inner_area,
&render_background, &inner_area,
flags | cell_fields);
}

View File

@ -92,6 +92,7 @@ VOID:OBJECT,UINT,FLAGS
VOID:OBJECT,STRING
VOID:OBJECT,OBJECT,STRING
VOID:OBJECT,OBJECT,OBJECT
VOID:OBJECT,OBJECT,BOXED,STRING
VOID:POINTER
VOID:POINTER,INT
VOID:POINTER,BOOLEAN

View File

@ -64,6 +64,19 @@ static gint cell_area_scaffold_focus (GtkWidget
static gboolean cell_area_scaffold_button_press (GtkWidget *widget,
GdkEventButton *event);
/* GtkContainerClass */
static void cell_area_scaffold_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data);
static void cell_area_scaffold_remove (GtkContainer *container,
GtkWidget *child);
static void cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
GtkWidget *edit_widget,
gint x,
gint y,
gint width,
gint height);
/* CellAreaScaffoldClass */
static void cell_area_scaffold_activate (CellAreaScaffold *scaffold);
@ -76,6 +89,16 @@ static void focus_changed_cb (GtkCellArea
GtkCellRenderer *renderer,
const gchar *path,
CellAreaScaffold *scaffold);
static void editing_started_cb (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *edit_widget,
GdkRectangle *cell_area,
const gchar *path,
CellAreaScaffold *scaffold);
static void remove_editable_cb (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *edit_widget,
CellAreaScaffold *scaffold);
static void row_changed_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
@ -124,7 +147,11 @@ struct _CellAreaScaffoldPrivate {
* we need to queue a redraw */
gulong size_changed_id;
/* Currently edited widget */
GtkWidget *edit_widget;
GdkRectangle edit_rect;
gulong editing_started_id;
gulong remove_editable_id;
};
enum {
@ -149,7 +176,7 @@ static guint scaffold_signals[N_SIGNALS] = { 0 };
(dir) == GTK_DIR_LEFT ? "left" : \
(dir) == GTK_DIR_RIGHT ? "right" : "invalid")
G_DEFINE_TYPE_WITH_CODE (CellAreaScaffold, cell_area_scaffold, GTK_TYPE_WIDGET,
G_DEFINE_TYPE_WITH_CODE (CellAreaScaffold, cell_area_scaffold, GTK_TYPE_CONTAINER,
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL));
@ -171,13 +198,23 @@ cell_area_scaffold_init (CellAreaScaffold *scaffold)
gtk_widget_set_has_window (GTK_WIDGET (scaffold), FALSE);
gtk_widget_set_can_focus (GTK_WIDGET (scaffold), TRUE);
priv->size_changed_id =
g_signal_connect (priv->iter, "notify",
G_CALLBACK (size_changed_cb), scaffold);
priv->focus_changed_id =
g_signal_connect (priv->area, "focus-changed",
G_CALLBACK (focus_changed_cb), scaffold);
priv->size_changed_id =
g_signal_connect (priv->iter, "notify",
G_CALLBACK (size_changed_cb), scaffold);
priv->editing_started_id =
g_signal_connect (priv->area, "editing-started",
G_CALLBACK (editing_started_cb), scaffold);
priv->remove_editable_id =
g_signal_connect (priv->area, "remove-editable",
G_CALLBACK (remove_editable_cb), scaffold);
}
static void
@ -185,14 +222,15 @@ cell_area_scaffold_class_init (CellAreaScaffoldClass *class)
{
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
gobject_class = G_OBJECT_CLASS(class);
gobject_class = G_OBJECT_CLASS (class);
gobject_class->dispose = cell_area_scaffold_dispose;
gobject_class->finalize = cell_area_scaffold_finalize;
gobject_class->get_property = cell_area_scaffold_get_property;
gobject_class->set_property = cell_area_scaffold_set_property;
widget_class = GTK_WIDGET_CLASS(class);
widget_class = GTK_WIDGET_CLASS (class);
widget_class->realize = cell_area_scaffold_realize;
widget_class->unrealize = cell_area_scaffold_unrealize;
widget_class->draw = cell_area_scaffold_draw;
@ -206,6 +244,10 @@ cell_area_scaffold_class_init (CellAreaScaffoldClass *class)
widget_class->focus = cell_area_scaffold_focus;
widget_class->button_press_event = cell_area_scaffold_button_press;
container_class = GTK_CONTAINER_CLASS (class);
container_class->forall = cell_area_scaffold_forall;
container_class->remove = cell_area_scaffold_remove;
class->activate = cell_area_scaffold_activate;
g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
@ -264,6 +306,8 @@ cell_area_scaffold_dispose (GObject *object)
{
/* Disconnect signals */
g_signal_handler_disconnect (priv->area, priv->focus_changed_id);
g_signal_handler_disconnect (priv->area, priv->editing_started_id);
g_signal_handler_disconnect (priv->area, priv->remove_editable_id);
g_object_unref (priv->area);
priv->area = NULL;
@ -443,6 +487,9 @@ cell_area_scaffold_draw (GtkWidget *widget,
valid = gtk_tree_model_iter_next (priv->model, &iter);
}
/* Draw the edit widget after drawing everything else */
GTK_WIDGET_CLASS (cell_area_scaffold_parent_class)->draw (widget, cr);
return FALSE;
}
@ -538,6 +585,10 @@ cell_area_scaffold_size_allocate (GtkWidget *widget,
allocation->width,
allocation->height);
/* Allocate the child GtkCellEditable widget if one is currently editing a row */
if (priv->edit_widget)
gtk_widget_size_allocate (priv->edit_widget, &priv->edit_rect);
if (!priv->model)
return;
@ -546,13 +597,13 @@ cell_area_scaffold_size_allocate (GtkWidget *widget,
/* Cache the per-row sizes and allocate the iter */
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
get_row_sizes (scaffold, priv->row_data, allocation->width);
gtk_cell_area_iter_allocate_width (priv->iter, allocation->width);
get_row_sizes (scaffold, priv->row_data, allocation->width);
}
else
{
get_row_sizes (scaffold, priv->row_data, allocation->height);
gtk_cell_area_iter_allocate_height (priv->iter, allocation->height);
get_row_sizes (scaffold, priv->row_data, allocation->height);
}
}
@ -892,8 +943,8 @@ cell_area_scaffold_button_press (GtkWidget *widget,
{
event_area.height = data->size;
if (event->y >= allocation.y + event_area.y &&
event->y <= allocation.y + event_area.y + event_area.height)
if (event->y >= event_area.y &&
event->y <= event_area.y + event_area.height)
{
/* XXX A real implementation would assemble GtkCellRendererState flags here */
gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
@ -909,8 +960,8 @@ cell_area_scaffold_button_press (GtkWidget *widget,
{
event_area.width = data->size;
if (event->x >= allocation.x + event_area.x &&
event->x <= allocation.x + event_area.x + event_area.width)
if (event->x >= event_area.x &&
event->x <= event_area.x + event_area.width)
{
/* XXX A real implementation would assemble GtkCellRendererState flags here */
gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
@ -930,6 +981,55 @@ cell_area_scaffold_button_press (GtkWidget *widget,
return handled;
}
/*********************************************************
* GtkContainerClass *
*********************************************************/
static void
cell_area_scaffold_put_edit_widget (CellAreaScaffold *scaffold,
GtkWidget *edit_widget,
gint x,
gint y,
gint width,
gint height)
{
CellAreaScaffoldPrivate *priv = scaffold->priv;
priv->edit_rect.x = x;
priv->edit_rect.y = y;
priv->edit_rect.width = width;
priv->edit_rect.height = height;
priv->edit_widget = edit_widget;
gtk_widget_set_parent (edit_widget, GTK_WIDGET (scaffold));
}
static void
cell_area_scaffold_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data)
{
CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (container);
CellAreaScaffoldPrivate *priv = scaffold->priv;
if (priv->edit_widget)
(* callback) (priv->edit_widget, callback_data);
}
static void
cell_area_scaffold_remove (GtkContainer *container,
GtkWidget *child)
{
CellAreaScaffold *scaffold = CELL_AREA_SCAFFOLD (container);
CellAreaScaffoldPrivate *priv = scaffold->priv;
g_return_if_fail (child == priv->edit_widget);
gtk_widget_unparent (priv->edit_widget);
priv->edit_widget = NULL;
}
/*********************************************************
* CellAreaScaffoldClass *
*********************************************************/
@ -1005,7 +1105,6 @@ focus_changed_cb (GtkCellArea *area,
CellAreaScaffoldPrivate *priv = scaffold->priv;
GtkWidget *widget = GTK_WIDGET (scaffold);
GtkTreePath *treepath;
gboolean found = FALSE;
gint *indices;
if (!priv->model)
@ -1023,8 +1122,6 @@ focus_changed_cb (GtkCellArea *area,
gtk_tree_path_free (treepath);
g_print ("Focus changed signal, new focus row %d\n", priv->focus_row);
/* Make sure we have focus now */
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
@ -1032,6 +1129,36 @@ focus_changed_cb (GtkCellArea *area,
gtk_widget_queue_draw (widget);
}
static void
editing_started_cb (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *edit_widget,
GdkRectangle *cell_area,
const gchar *path,
CellAreaScaffold *scaffold)
{
GtkAllocation allocation;
gtk_widget_get_allocation (GTK_WIDGET (scaffold), &allocation);
cell_area_scaffold_put_edit_widget (scaffold, GTK_WIDGET (edit_widget),
allocation.x + cell_area->x,
allocation.y + cell_area->y,
cell_area->width, cell_area->height);
gtk_cell_editable_start_editing (edit_widget, NULL);
gtk_widget_grab_focus (GTK_WIDGET (edit_widget));
}
static void
remove_editable_cb (GtkCellArea *area,
GtkCellRenderer *renderer,
GtkCellEditable *edit_widget,
CellAreaScaffold *scaffold)
{
gtk_container_remove (GTK_CONTAINER (scaffold), GTK_WIDGET (edit_widget));
}
static void
rebuild_and_flush_internals (CellAreaScaffold *scaffold)
{

View File

@ -43,14 +43,14 @@ typedef struct _CellAreaScaffoldPrivate CellAreaScaffoldPrivate;
struct _CellAreaScaffold
{
GtkWidget widget;
GtkContainer widget;
CellAreaScaffoldPrivate *priv;
};
struct _CellAreaScaffoldClass
{
GtkWidgetClass parent_class;
GtkContainerClass parent_class;
void (* activate) (CellAreaScaffold *scaffold);
};

View File

@ -291,7 +291,7 @@ focus_list_model (void)
static void
cell_toggled (GtkCellRendererToggle *cell_renderer,
gchar *path,
const gchar *path,
CellAreaScaffold *scaffold)
{
GtkTreeModel *model = cell_area_scaffold_get_model (scaffold);
@ -307,6 +307,23 @@ cell_toggled (GtkCellRendererToggle *cell_renderer,
gtk_list_store_set (GTK_LIST_STORE (model), &iter, FOCUS_COLUMN_CHECK, !active, -1);
}
static void
cell_edited (GtkCellRendererToggle *cell_renderer,
const gchar *path,
const gchar *new_text,
CellAreaScaffold *scaffold)
{
GtkTreeModel *model = cell_area_scaffold_get_model (scaffold);
GtkTreeIter iter;
g_print ("Cell edited with new text '%s' !\n", new_text);
if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
return;
gtk_list_store_set (GTK_LIST_STORE (model), &iter, FOCUS_COLUMN_NAME, new_text, -1);
}
static GtkWidget *
focus_scaffold (void)
{
@ -329,7 +346,9 @@ focus_scaffold (void)
gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, TRUE, FALSE);
gtk_cell_area_attribute_connect (area, renderer, "text", FOCUS_COLUMN_NAME);
/* Catch signal ... */
g_signal_connect (G_OBJECT (renderer), "edited",
G_CALLBACK (cell_edited), scaffold);
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);