[gtk+/treeview-refactor] Added initial detailed docs for GtkCellArea.



commit 9ee908140589968c069b75aa37fb0d3ef3fb954f
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Mon Nov 29 10:54:07 2010 +0900

    Added initial detailed docs for GtkCellArea.

 gtk/gtkcellarea.c |  307 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 305 insertions(+), 2 deletions(-)
---
diff --git a/gtk/gtkcellarea.c b/gtk/gtkcellarea.c
index e68fb65..f170786 100644
--- a/gtk/gtkcellarea.c
+++ b/gtk/gtkcellarea.c
@@ -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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]