Added initial detailed docs for GtkCellArea.

This commit is contained in:
Tristan Van Berkom 2010-11-29 10:54:07 +09:00
parent c1cbc8790e
commit 9ee9081405

View File

@ -21,6 +21,289 @@
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:gtkcellarea
* @Short_Description: An abstract class for laying out #GtkCellRenderers
* @Title: GtkCellArea
*
* The #GtkCellArea is an abstract class for laying out #GtkCellRenderers
* onto a given area of a #GtkWidget.
*
* The work of rendering #GtkCellRenderers can be very complicated; it involves
* requesting size for cells, driving keyboard focus from cell to cell, rendering
* the actual cells, painting the focus onto the currently focused cell and finally
* activating cells which are %GTK_CELL_RENDERER_MODE_ACTIVATABLE and editing cells
* which are %GTK_CELL_RENDERER_MODE_EDITABLE. The work is even more complex since
* a cell renderer as opposed to a widget, is used to interact with an arbitrary
* number of #GtkTreeModel rows instead of always displaying the same data.
*
* <refsect2 id="cell-area-geometry-management">
* <title>Requesting area sizes</title>
* <para>
* As outlined in <link linkend="geometry-management">GtkWidget's
* geometry management section</link>, GTK+ uses a height-for-width
* geometry managemen system to compute the sizes of widgets and user
* interfaces. #GtkCellArea uses the same semantics to calculate the
* size of an area for an arbitrary number of #GtkTreeModel rows.
*
* When requesting the size of a #GtkCellArea one needs to calculate
* the size of a handful of rows, this will be done differently by
* different #GtkCellLayout widgets. For instance a #GtkTreeViewColumn
* always lines up the areas from top to bottom while a #GtkIconView
* on the other hand might enforce that areas maintain a fixed width
* and then wrap the area around, thus requesting height for more
* areas when allocated less width.
*
* It's also important for #GtkCellAreas to maintain some cell
* alignments with areas rendered for different rows so that
* a handful of rendered rows can allocate the same size for
* a said cell across rows (and also to make sure to request
* an appropriate size for the largest row after requesting
* a hand full of rows). For this reason the #GtkCellArea
* uses a #GtkCellAreaContext object to store the alignments
* and sizes along the way.
*
* In order to request the width of all the rows at the root level
* of a #GtkTreeModel one would do the following:
* <example>
* <title>Requesting the width of a hand full of GtkTreeModel rows.</title>
* <programlisting>
* GtkTreeIter iter;
* gint minimum_width;
* gint natural_width;
*
* valid = gtk_tree_model_get_iter_first (model, &iter);
* while (valid)
* {
* gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
* gtk_cell_area_get_preferred_width (area, context, widget, NULL, NULL);
*
* valid = gtk_tree_model_iter_next (model, &iter);
* }
* gtk_cell_area_context_get_preferred_width (context, &minimum_width, &natural_width);
* </programlisting>
* </example>
* Note that in this example it's not important to observe the returned minimum and
* natural width of the area for each row unless the cell layouting object is actually
* interested in the widths of individual rows. The overall width is however stored
* in the accompanying #GtkCellAreaContext object and can be consulted at any time.
*
* This can be useful since #GtkCellLayout widgets usually have to support requesting
* and rendering rows in treemodels with an exceedingly large amount of rows. The
* #GtkCellLayout widget in that case would calculate the required width of the rows
* in an idle or timeout source (see g_timeout_add()) and when the widget is requested
* its actual width in #GtkWidgetClass.get_preferred_width() it can simply consult the
* width accumulated so far in the #GtkCellAreaContext object.
*
* A simple example where rows are rendered from top to bottom and take up the full
* width of the layouting widget would look like:
* <example>
* <title>Requesting the width of a hand full of GtkTreeModel rows.</title>
* <programlisting>
* static void
* foo_get_preferred_width (GtkWidget *widget,
* gint *minimum_size,
* gint *natural_size)
* {
* Foo *foo = FOO (widget);
* FooPrivate *priv = foo->priv;
*
* foo_ensure_at_least_one_handfull_of_rows_have_been_requested (foo);
*
* gtk_cell_area_context_get_preferred_width (priv->context, minimum_size, natural_size);
* }
* </programlisting>
* </example>
*
* In the above example the Foo widget has to make sure that some row sizes have
* been calculated (the amount of rows that Foo judged was appropriate to request
* space for in a single timeout iteration) before simply returning the amount
* of space required by the area via the #GtkCellAreaContext.
*
* Requesting the height for width (or width for height) of an area is a similar
* task except in this case the #GtkCellAreaContext does not store the data (actually
* it does not know how much space the layouting widget plans to allocate it for
* every row, it's up to the layouting widget to render each row of data with
* the appropriate height and width which was requested by the #GtkCellArea).
*
* In order to request the height for width of all the rows at the root level
* of a #GtkTreeModel one would do the following:
* <example>
* <title>Requesting the height for width of a hand full of GtkTreeModel rows.</title>
* <programlisting>
* GtkTreeIter iter;
* gint minimum_height;
* gint natural_height;
* gint full_minimum_height = 0;
* gint full_natural_height = 0;
*
* valid = gtk_tree_model_get_iter_first (model, &iter);
* while (valid)
* {
* gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
* gtk_cell_area_get_preferred_height_for_width (area, context, widget,
* width, &minimum_height, &natural_height);
*
* if (width_is_for_allocation)
* cache_row_height (&iter, minimum_height, natural_height);
*
* full_minimum_height += minimum_height;
* full_natural_height += natural_height;
*
* valid = gtk_tree_model_iter_next (model, &iter);
* }
* </programlisting>
* </example>
*
* Note that in the above example we would need to cache the heights returned for each
* treemodel row so that we would know what sizes to render the areas for each row. However
* we would only want to really cache the heights if the request is intended for the
* layouting widgets real allocation.
*
* In some cases the layouting widget is requested the height for an arbitrary for_width,
* this is a special case for layouting widgets who need to request size for tens of thousands
* of treemodel rows. For this case it's only important that the layouting widget calculate
* one reasonably sized chunk of rows and return that height synchronously. The reasoning here
* is that any layouting widget is at least capable of synchronously calculating enough
* height to fill the screen height (or scrolled window height) in response to a single call to
* #GtkWidgetClass.get_preferred_height_for_width(). Returning a perfect height for width that
* is larger than the screen area is inconsequential since after the layouting receives an
* allocation from a scrolled window it simply continues to drive the the scrollbar
* values while more and mode height is required for the row heights that are calculated
* in the background.
* </para>
* </refsect2>
* <refsect2 id="cell-area-rendering">
* <title>Rendering Areas</title>
* <para>
* Once area sizes have been aquired at least for the rows in the visible area of the
* layouting widget they can be rendered at #GtkWidgetClass.draw() time.
*
* A crued example of how to render all the rows at the root level runs as follows:
* <example>
* <title>Requesting the width of a hand full of GtkTreeModel rows.</title>
* <programlisting>
* GtkAllocation allocation;
* GdkRectangle cell_area = { 0, };
* GtkTreeIter iter;
* gint minimum_width;
* gint natural_width;
*
* gtk_widget_get_allocation (widget, &allocation);
* cell_area.width = allocation.width;
*
* valid = gtk_tree_model_get_iter_first (model, &iter);
* while (valid)
* {
* cell_area.height = get_cached_height_for_row (&iter);
*
* gtk_cell_area_apply_attributes (area, model, &iter, FALSE, FALSE);
* gtk_cell_area_render (area, context, widget, cr,
* &cell_area, &cell_area, state_flags, FALSE);
*
* cell_area.y += cell_area.height;
*
* valid = gtk_tree_model_iter_next (model, &iter);
* }
* </programlisting>
* </example>
* Note that the cached height in this example really depends on how the layouting
* widget works. The layouting widget might decide to give every row it's minimum
* or natural height or if the model content is expected to fit inside the layouting
* widget with not scrolled window it would make sense to calculate the allocation
* for each row at #GtkWidget.size_allocate() time using gtk_distribute_natural_allocation().
* </para>
* </refsect2>
* <refsect2 id="cell-area-events-and-focus">
* <title>Handling Events and Driving Keyboard Focus</title>
* <para>
* Passing events to the area is as simple as handling events on any normal
* widget and then passing them to the gtk_cell_area_event() api as they come
* in. Usually #GtkCellArea is only interested in button events, however some
* customized derived areas can be implemented who are interested in handling
* other events. Handling an event can trigger the #GtkCellArea::focus-changed
* signal to fire as well as #GtkCellArea::add-editable in the case that
* an editable cell was clicked and needs to start editing.
*
* The #GtkCellArea drives keyboard focus from cell to cell in a way similar
* to #GtkWidget. For layouting widgets that support giving focus to cells it's
* important to remember to pass %GTK_CELL_RENDERER_FOCUSED to the area functions
* for the row that has focus and to tell the area to paint the focus at render
* time.
*
* Layouting widgets that accept focus on cells should implement the #GtkWidgetClass.focus()
* virtual method. The layouting widget is always responsible for knowing where
* #GtkTreeModel rows are rendered inside the widget, so at #GtkWidgetClass.focus() time
* the layouting widget should use the #GtkCellArea methods to navigate focus inside the
* area and then observe the GtkDirectionType to pass the focus to adjacent rows and
* areas.
*
* A basic example of how the #GtkWidgetClass.focus() virtual method should be implemented:
* <example>
* <title>Implementing keyboard focus navigation when displaying rows from top to bottom.</title>
* <programlisting>
* static void
* foo_focus (GtkWidget *widget,
* GtkDirectionType direction)
* {
* Foo *foo = FOO (widget);
* FooPrivate *priv = foo->priv;
* gint focus_row;
* gboolean have_focus = FALSE;
*
* focus_row = priv->focus_row;
*
* if (!gtk_widget_has_focus (widget))
* gtk_widget_grab_focus (widget);
*
* valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, priv->focus_row);
* while (valid)
* {
* gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
*
* if (gtk_cell_area_focus (priv->area, direction))
* {
* priv->focus_row = focus_row;
* have_focus = TRUE;
* break;
* }
* else
* {
* if (direction == GTK_DIR_RIGHT ||
* direction == GTK_DIR_LEFT)
* break;
* else if (direction == GTK_DIR_UP ||
* direction == GTK_DIR_TAB_BACKWARD)
* {
* if (focus_row == 0)
* break;
* else
* {
* focus_row--;
* valid = gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, focus_row);
* }
* }
* else
* {
* if (focus_row == last_row)
* break;
* else
* {
* focus_row++;
* valid = gtk_tree_model_iter_next (priv->model, &iter);
* }
* }
* }
* }
* return have_focus;
* }
* </programlisting>
* </example>
* </para>
* </refsect2>
*
*/
#include "config.h"
#include <stdarg.h>
@ -275,7 +558,6 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
class->activate = gtk_cell_area_real_activate;
/* Signals */
/**
* GtkCellArea::apply-attributes:
* @area: the #GtkCellArea to apply the attributes to
@ -372,6 +654,11 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
G_TYPE_STRING);
/* Properties */
/**
* GtkCellArea:focus-cell:
*
* The cell in the area that currently has focus
*/
g_object_class_install_property (object_class,
PROP_FOCUS_CELL,
g_param_spec_object
@ -381,6 +668,14 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
GTK_TYPE_CELL_RENDERER,
GTK_PARAM_READWRITE));
/**
* GtkCellArea:edited-cell:
*
* The cell in the area that is currently edited
*
* This property is read-only and only changes as
* a result of a call gtk_cell_area_activate_cell().
*/
g_object_class_install_property (object_class,
PROP_EDITED_CELL,
g_param_spec_object
@ -390,6 +685,14 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
GTK_TYPE_CELL_RENDERER,
G_PARAM_READABLE));
/**
* GtkCellArea:edit-widget:
*
* The widget currently editing the edited cell
*
* This property is read-only and only changes as
* a result of a call gtk_cell_area_activate_cell().
*/
g_object_class_install_property (object_class,
PROP_EDIT_WIDGET,
g_param_spec_object
@ -989,7 +1292,7 @@ gtk_cell_area_has_renderer (GtkCellArea *area,
}
/**
* gtk_cell_area_forall
* gtk_cell_area_forall:
* @area: a #GtkCellArea
* @callback: the #GtkCellCallback to call
* @callback_data: user provided data pointer