forked from AuroraMiddleware/gtk
Added initial detailed docs for GtkCellArea.
This commit is contained in:
parent
c1cbc8790e
commit
9ee9081405
@ -21,6 +21,289 @@
|
|||||||
* Boston, MA 02111-1307, USA.
|
* 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 "config.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@ -275,7 +558,6 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
|
|||||||
class->activate = gtk_cell_area_real_activate;
|
class->activate = gtk_cell_area_real_activate;
|
||||||
|
|
||||||
/* Signals */
|
/* Signals */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GtkCellArea::apply-attributes:
|
* GtkCellArea::apply-attributes:
|
||||||
* @area: the #GtkCellArea to apply the attributes to
|
* @area: the #GtkCellArea to apply the attributes to
|
||||||
@ -372,6 +654,11 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
|
|||||||
G_TYPE_STRING);
|
G_TYPE_STRING);
|
||||||
|
|
||||||
/* Properties */
|
/* Properties */
|
||||||
|
/**
|
||||||
|
* GtkCellArea:focus-cell:
|
||||||
|
*
|
||||||
|
* The cell in the area that currently has focus
|
||||||
|
*/
|
||||||
g_object_class_install_property (object_class,
|
g_object_class_install_property (object_class,
|
||||||
PROP_FOCUS_CELL,
|
PROP_FOCUS_CELL,
|
||||||
g_param_spec_object
|
g_param_spec_object
|
||||||
@ -381,6 +668,14 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
|
|||||||
GTK_TYPE_CELL_RENDERER,
|
GTK_TYPE_CELL_RENDERER,
|
||||||
GTK_PARAM_READWRITE));
|
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,
|
g_object_class_install_property (object_class,
|
||||||
PROP_EDITED_CELL,
|
PROP_EDITED_CELL,
|
||||||
g_param_spec_object
|
g_param_spec_object
|
||||||
@ -390,6 +685,14 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
|
|||||||
GTK_TYPE_CELL_RENDERER,
|
GTK_TYPE_CELL_RENDERER,
|
||||||
G_PARAM_READABLE));
|
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,
|
g_object_class_install_property (object_class,
|
||||||
PROP_EDIT_WIDGET,
|
PROP_EDIT_WIDGET,
|
||||||
g_param_spec_object
|
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
|
* @area: a #GtkCellArea
|
||||||
* @callback: the #GtkCellCallback to call
|
* @callback: the #GtkCellCallback to call
|
||||||
* @callback_data: user provided data pointer
|
* @callback_data: user provided data pointer
|
||||||
|
Loading…
Reference in New Issue
Block a user