From 62efa76e503d9e5bad027d779d2f91a5158d1e4e Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Wed, 18 May 2011 07:04:32 +0000 Subject: [PATCH] add grid layout obj to evas (along with table and box) SVN revision: 59481 --- legacy/evas/ChangeLog | 7 + legacy/evas/src/lib/Evas.h | 148 ++++++ legacy/evas/src/lib/canvas/Makefile.am | 1 + legacy/evas/src/lib/canvas/evas_object_grid.c | 467 ++++++++++++++++++ 4 files changed, 623 insertions(+) create mode 100644 legacy/evas/src/lib/canvas/evas_object_grid.c diff --git a/legacy/evas/ChangeLog b/legacy/evas/ChangeLog index 1b312310c5..a47425107f 100644 --- a/legacy/evas/ChangeLog +++ b/legacy/evas/ChangeLog @@ -337,3 +337,10 @@ * Textblock: fix segfault with evas_textblock_cursor_content_get +2011-05-12 Carsten Haitzler (The Rasterman) + + * Add a smart "Grid" layout object that lays out objects in a regular + "virtual resolution" grid (eg 100x100) so you can lay them out + as a "scaled percentage" of the virtual resolution. virtual + resolution is defineable as is the geometry of each member. + diff --git a/legacy/evas/src/lib/Evas.h b/legacy/evas/src/lib/Evas.h index 6589f933c4..164bad474d 100644 --- a/legacy/evas/src/lib/Evas.h +++ b/legacy/evas/src/lib/Evas.h @@ -7910,6 +7910,154 @@ EAPI Eina_Accessor *evas_object_table_accessor_new (cons */ EAPI Eina_List *evas_object_table_children_get (const Evas_Object *o) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; EAPI Evas_Object *evas_object_table_child_get (const Evas_Object *o, unsigned short col, unsigned short row) EINA_ARG_NONNULL(1); + +/** + * @defgroup Evas_Object_Grid Grid Smart Object. + * + * Convenience smart object that packs children using a regular grid + * layout using Their virtual grid location and size to determine + * position inside the grid object + * + * @ingroup Evas_Smart_Object_Group + * @since 1.1.0 + */ + +/** + * Create a new grid. + * + * It's set to a virtual size of 1x1 by default and add children with + * evas_object_grid_pack(). + * @since 1.1.0 + */ +EAPI Evas_Object *evas_object_grid_add (Evas *evas) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +/** + * Create a grid that is child of a given element @a parent. + * + * @see evas_object_grid_add() + * @since 1.1.0 + */ +EAPI Evas_Object *evas_object_grid_add_to (Evas_Object *parent) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +/** + * Set the virtual resolution for the grid + * + * @param o The grid object to modify + * @param w The virtual horizontal size (resolution) in integer units + * @param h The virtual vertical size (resolution) in integer units + * @since 1.1.0 + */ +EAPI void evas_object_grid_size_set (Evas_Object *o, int w, int h) EINA_ARG_NONNULL(1); + +/** + * Get the current virtual resolution + * + * @param o The grid object to query + * @param w A pointer to an integer to store the virtual width + * @param h A pointer to an integer to store the virtual height + * @see evas_object_grid_size_set() + * @since 1.1.0 + */ +EAPI void evas_object_grid_size_get (const Evas_Object *o, int *w, int *h) EINA_ARG_NONNULL(1) EINA_PURE; + +/** + * Sets the mirrored mode of the grid. In mirrored mode the grid items go + * from right to left instead of left to right. That is, 0,0 is top right, not + * to left. + * + * @param obj The grid object. + * @param mirrored the mirrored mode to set + * @since 1.1.0 + */ +EAPI void evas_object_grid_mirrored_set (Evas_Object *o, Eina_Bool mirrored) EINA_ARG_NONNULL(1); + +/** + * Gets the mirrored mode of the grid. + * + * @param obj The grid object. + * @return EINA_TRUE if it's a mirrored grid, EINA_FALSE otherwise. + * @see evas_object_grid_mirrored_set() + * @since 1.1.0 + */ +EAPI Eina_Bool evas_object_grid_mirrored_get (const Evas_Object *o) EINA_ARG_NONNULL(1); + +/** + * Add a new child to a grid object. + * + * @param o The given grid object. + * @param child The child object to add. + * @param x The virtual x coordinate of the child + * @param y The virtual y coordinate of the child + * @param w The virtual width of the child + * @param h The virtual height of the child + * @return 1 on success, 0 on failure. + * @since 1.1.0 + */ +EAPI Eina_Bool evas_object_grid_pack (Evas_Object *o, Evas_Object *child, int x, int y, int w, int h) EINA_ARG_NONNULL(1, 2); + +/** + * Remove child from grid. + * + * @note removing a child will immediately call a walk over children in order + * to recalculate numbers of columns and rows. If you plan to remove + * all children, use evas_object_grid_clear() instead. + * + * @return 1 on success, 0 on failure. + * @since 1.1.0 + */ +EAPI Eina_Bool evas_object_grid_unpack (Evas_Object *o, Evas_Object *child) EINA_ARG_NONNULL(1, 2); + +/** + * Faster way to remove all child objects from a grid object. + * + * @param o The given grid object. + * @param clear if true, it will delete just removed children. + * @since 1.1.0 + */ +EAPI void evas_object_grid_clear (Evas_Object *o, Eina_Bool clear) EINA_ARG_NONNULL(1); + +/** + * Get the pack options for a grid child + * + * Get the pack x, y, width and height in virtual coordinates set by + * evas_object_grid_pack() + * @param o The grid object + * @param child The grid child to query for coordinates + * @param x The pointer to where the x coordinate will be returned + * @param y The pointer to where the y coordinate will be returned + * @param w The pointer to where the width will be returned + * @param h The pointer to where the height will be returned + * @return 1 on success, 0 on failure. + * @since 1.1.0 + */ +EAPI Eina_Bool evas_object_grid_pack_get (Evas_Object *o, Evas_Object *child, int *x, int *y, int *w, int *h); + +/** + * Get an iterator to walk the list of children for the grid. + * + * @note Do not remove or delete objects while walking the list. + * @since 1.1.0 + */ +EAPI Eina_Iterator *evas_object_grid_iterator_new (const Evas_Object *o) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +/** + * Get an accessor to get random access to the list of children for the grid. + * + * @note Do not remove or delete objects while walking the list. + * @since 1.1.0 + */ +EAPI Eina_Accessor *evas_object_grid_accessor_new (const Evas_Object *o) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; + +/** + * Get the list of children for the grid. + * + * @note This is a duplicate of the list kept by the grid internally. + * It's up to the user to destroy it when it no longer needs it. + * It's possible to remove objects from the grid when walking this + * list, but these removals won't be reflected on it. + * @since 1.1.0 + */ +EAPI Eina_List *evas_object_grid_children_get (const Evas_Object *o) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1) EINA_MALLOC; typedef enum { diff --git a/legacy/evas/src/lib/canvas/Makefile.am b/legacy/evas/src/lib/canvas/Makefile.am index ecaef358d2..cf2d61b766 100644 --- a/legacy/evas/src/lib/canvas/Makefile.am +++ b/legacy/evas/src/lib/canvas/Makefile.am @@ -42,6 +42,7 @@ evas_object_box.c \ evas_object_table.c \ evas_object_text.c \ evas_object_textblock.c \ +evas_object_grid.c \ evas_font_dir.c \ evas_rectangle.c \ evas_render.c \ diff --git a/legacy/evas/src/lib/canvas/evas_object_grid.c b/legacy/evas/src/lib/canvas/evas_object_grid.c new file mode 100644 index 0000000000..59b1308e9f --- /dev/null +++ b/legacy/evas/src/lib/canvas/evas_object_grid.c @@ -0,0 +1,467 @@ +#include +#include "evas_common.h" + +typedef struct _Evas_Object_Grid_Data Evas_Object_Grid_Data; +typedef struct _Evas_Object_Grid_Option Evas_Object_Grid_Option; +typedef struct _Evas_Object_Grid_Iterator Evas_Object_Grid_Iterator; +typedef struct _Evas_Object_Grid_Accessor Evas_Object_Grid_Accessor; + +struct _Evas_Object_Grid_Option +{ + Evas_Object *obj; + Eina_List *l; + int x, y, w, h; +}; + +struct _Evas_Object_Grid_Data +{ + Evas_Object_Smart_Clipped_Data base; + Eina_List *children; + struct { + int w, h; + } size; + Eina_Bool is_mirrored : 1; +}; + +struct _Evas_Object_Grid_Iterator +{ + Eina_Iterator iterator; + + Eina_Iterator *real_iterator; + const Evas_Object *grid; +}; + +struct _Evas_Object_Grid_Accessor +{ + Eina_Accessor accessor; + + Eina_Accessor *real_accessor; + const Evas_Object *grid; +}; + +#define EVAS_OBJECT_GRID_DATA_GET(o, ptr) \ + Evas_Object_Grid_Data *ptr = evas_object_smart_data_get(o) + +#define EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, ptr) \ + EVAS_OBJECT_GRID_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRIT("no widget data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + abort(); \ + return; \ + } + +#define EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, ptr, val) \ + EVAS_OBJECT_GRID_DATA_GET(o, ptr); \ + if (!ptr) \ + { \ + CRIT("No widget data for object %p (%s)", \ + o, evas_object_type_get(o)); \ + abort(); \ + return val; \ + } + +static const char EVAS_OBJECT_GRID_OPTION_KEY[] = "Evas_Object_Grid_Option"; + +static Eina_Bool +_evas_object_grid_iterator_next(Evas_Object_Grid_Iterator *it, void **data) +{ + Evas_Object_Grid_Option *opt; + + if (!eina_iterator_next(it->real_iterator, (void **)&opt)) + return EINA_FALSE; + if (data) *data = opt->obj; + return EINA_TRUE; +} + +static Evas_Object * +_evas_object_grid_iterator_get_container(Evas_Object_Grid_Iterator *it) +{ + return (Evas_Object *)it->grid; +} + +static void +_evas_object_grid_iterator_free(Evas_Object_Grid_Iterator *it) +{ + eina_iterator_free(it->real_iterator); + free(it); +} + +static Eina_Bool +_evas_object_grid_accessor_get_at(Evas_Object_Grid_Accessor *it, unsigned int index, void **data) +{ + Evas_Object_Grid_Option *opt = NULL; + + if (!eina_accessor_data_get(it->real_accessor, index, (void **)&opt)) + return EINA_FALSE; + if (data) *data = opt->obj; + return EINA_TRUE; +} + +static Evas_Object * +_evas_object_grid_accessor_get_container(Evas_Object_Grid_Accessor *it) +{ + return (Evas_Object *)it->grid; +} + +static void +_evas_object_grid_accessor_free(Evas_Object_Grid_Accessor *it) +{ + eina_accessor_free(it->real_accessor); + free(it); +} + +static Evas_Object_Grid_Option * +_evas_object_grid_option_get(Evas_Object *o) +{ + return evas_object_data_get(o, EVAS_OBJECT_GRID_OPTION_KEY); +} + +static void +_evas_object_grid_option_set(Evas_Object *o, const Evas_Object_Grid_Option *opt) +{ + evas_object_data_set(o, EVAS_OBJECT_GRID_OPTION_KEY, opt); +} + +static Evas_Object_Grid_Option * +_evas_object_grid_option_del(Evas_Object *o) +{ + return evas_object_data_del(o, EVAS_OBJECT_GRID_OPTION_KEY); +} + +static void +_on_child_del(void *data, Evas *evas __UNUSED__, Evas_Object *child, void *einfo __UNUSED__) +{ + Evas_Object *grid = data; + evas_object_grid_unpack(grid, child); +} + +static void +_evas_object_grid_child_connect(Evas_Object *o, Evas_Object *child) +{ + evas_object_event_callback_add + (child, EVAS_CALLBACK_DEL, _on_child_del, o); +} + +static void +_evas_object_grid_child_disconnect(Evas_Object *o, Evas_Object *child) +{ + evas_object_event_callback_del_full + (child, EVAS_CALLBACK_DEL, _on_child_del, o); +} + +EVAS_SMART_SUBCLASS_NEW("Evas_Object_Grid", _evas_object_grid, + Evas_Smart_Class, Evas_Smart_Class, + evas_object_smart_clipped_class_get, NULL) + +static void +_evas_object_grid_smart_add(Evas_Object *o) +{ + EVAS_SMART_DATA_ALLOC(o, Evas_Object_Grid_Data) + + priv->size.w = 100; + priv->size.h = 100; + + _evas_object_grid_parent_sc->add(o); +} + +static void +_evas_object_grid_smart_del(Evas_Object *o) +{ + EVAS_OBJECT_GRID_DATA_GET(o, priv); + Eina_List *l; + + l = priv->children; + while (l) + { + Evas_Object_Grid_Option *opt = l->data; + _evas_object_grid_child_disconnect(o, opt->obj); + _evas_object_grid_option_del(opt->obj); + free(opt); + l = eina_list_remove_list(l, l); + } + _evas_object_grid_parent_sc->del(o); +} + +static void +_evas_object_grid_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h) +{ + Evas_Coord ow, oh; + evas_object_geometry_get(o, NULL, NULL, &ow, &oh); + if ((ow == w) && (oh == h)) return; + evas_object_smart_changed(o); +} + +static void +_evas_object_grid_smart_calculate(Evas_Object *o) +{ + Eina_List *l; + Evas_Object_Grid_Option *opt; + Evas_Coord x, y, w, h, vw, vh, t; + Eina_Bool mirror; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv); + if (!priv) return; + if (!priv->children) return; + evas_object_geometry_get(o, &x, &y, &w, &h); + mirror = priv->is_mirrored; + vw = priv->size.w; + vh = priv->size.h; + EINA_LIST_FOREACH(priv->children, l, opt) + { + Evas_Coord x1, y1, x2, y2; + + x1 = x + ((w * opt->x) / vw); + y1 = y + ((h * opt->y) / vh); + x2 = x + ((w * (opt->x + opt->w)) / vw); + y2 = y + ((h * (opt->y + opt->h)) / vh); + if (mirror) + { + t = x1; x1 = x2; x2 = t; + t = y1; y1 = y2; y2 = t; + } + evas_object_move(opt->obj, x1, y1); + evas_object_resize(opt->obj, x2 - x1, y2 - y1); + } +} + +static void +_evas_object_grid_smart_set_user(Evas_Smart_Class *sc) +{ + sc->add = _evas_object_grid_smart_add; + sc->del = _evas_object_grid_smart_del; + sc->resize = _evas_object_grid_smart_resize; + sc->calculate = _evas_object_grid_smart_calculate; +} + +EAPI Evas_Object * +evas_object_grid_add(Evas *evas) +{ + return evas_object_smart_add(evas, _evas_object_grid_smart_class_new()); +} + +EAPI Evas_Object * +evas_object_grid_add_to(Evas_Object *parent) +{ + Evas *evas; + Evas_Object *o; + + evas = evas_object_evas_get(parent); + o = evas_object_grid_add(evas); + evas_object_smart_member_add(o, parent); + return o; +} + +EAPI void +evas_object_grid_size_set(Evas_Object *o, int w, int h) +{ + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv); + if ((priv->size.w == w) && (priv->size.h == h)) return; + priv->size.w = w; + priv->size.h = h; + evas_object_smart_changed(o); +} + +EAPI void +evas_object_grid_size_get(const Evas_Object *o, int *w, int *h) +{ + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv); + if (priv) + { + if (w) *w = priv->size.w; + if (h) *h = priv->size.h; + } + else + { + if (w) *w = 0; + if (h) *h = 0; + } +} + +EAPI Eina_Bool +evas_object_grid_pack(Evas_Object *o, Evas_Object *child, int x, int y, int w, int h) +{ + Evas_Object_Grid_Option *opt; + Eina_Bool newobj = EINA_FALSE; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0); + + opt = _evas_object_grid_option_get(child); + if (!opt) + { + opt = malloc(sizeof(*opt)); + if (!opt) + { + ERR("could not allocate grid option data."); + return EINA_FALSE; + } + newobj = EINA_TRUE; + } + + opt->x = x; + opt->y = y; + opt->w = w; + opt->h = h; + + if (newobj) + { + opt->obj = child; + priv->children = eina_list_append(priv->children, opt); + opt->l = eina_list_last(priv->children); + _evas_object_grid_option_set(child, opt); + evas_object_smart_member_add(child, o); + _evas_object_grid_child_connect(o, child); + } + // FIXME: we could keep a changed list + evas_object_smart_changed(o); + return EINA_TRUE; +} + +static void +_evas_object_grid_remove_opt(Evas_Object_Grid_Data *priv, Evas_Object_Grid_Option *opt) +{ + priv->children = eina_list_remove_list(priv->children, opt->l); + opt->l = NULL; +} + +EAPI Eina_Bool +evas_object_grid_unpack(Evas_Object *o, Evas_Object *child) +{ + Evas_Object_Grid_Option *opt; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0); + + if (o != evas_object_smart_parent_get(child)) + { + ERR("cannot unpack child from incorrect grid!"); + return EINA_FALSE; + } + + opt = _evas_object_grid_option_del(child); + if (!opt) + { + ERR("cannot unpack child with no packing option!"); + return EINA_FALSE; + } + + _evas_object_grid_child_disconnect(o, child); + _evas_object_grid_remove_opt(priv, opt); + evas_object_smart_member_del(child); + free(opt); + return EINA_TRUE; +} + +EAPI void +evas_object_grid_clear(Evas_Object *o, Eina_Bool clear) +{ + Evas_Object_Grid_Option *opt; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(o, priv); + + EINA_LIST_FREE(priv->children, opt) + { + _evas_object_grid_child_disconnect(o, opt->obj); + _evas_object_grid_option_del(opt->obj); + evas_object_smart_member_del(opt->obj); + if (clear) + evas_object_del(opt->obj); + free(opt); + } +} + +EAPI Eina_Bool +evas_object_grid_pack_get(Evas_Object *o, Evas_Object *child, int *x, int *y, int *w, int *h) +{ + Evas_Object_Grid_Option *opt; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, 0); + opt = _evas_object_grid_option_get(child); + if (!opt) return 0; + if (x) *x = opt->x; + if (y) *y = opt->y; + if (w) *w = opt->w; + if (h) *h = opt->h; + return 1; +} + +EAPI Eina_Iterator * +evas_object_grid_iterator_new(const Evas_Object *o) +{ + Evas_Object_Grid_Iterator *it; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL); + + if (!priv->children) return NULL; + + it = calloc(1, sizeof(Evas_Object_Grid_Iterator)); + if (!it) return NULL; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + + it->real_iterator = eina_list_iterator_new(priv->children); + it->grid = o; + + it->iterator.next = FUNC_ITERATOR_NEXT(_evas_object_grid_iterator_next); + it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_evas_object_grid_iterator_get_container); + it->iterator.free = FUNC_ITERATOR_FREE(_evas_object_grid_iterator_free); + + return &it->iterator; +} + +EAPI Eina_Accessor * +evas_object_grid_accessor_new(const Evas_Object *o) +{ + Evas_Object_Grid_Accessor *it; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL); + + if (!priv->children) return NULL; + + it = calloc(1, sizeof(Evas_Object_Grid_Accessor)); + if (!it) return NULL; + + EINA_MAGIC_SET(&it->accessor, EINA_MAGIC_ACCESSOR); + + it->real_accessor = eina_list_accessor_new(priv->children); + it->grid = o; + + it->accessor.get_at = FUNC_ACCESSOR_GET_AT(_evas_object_grid_accessor_get_at); + it->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(_evas_object_grid_accessor_get_container); + it->accessor.free = FUNC_ACCESSOR_FREE(_evas_object_grid_accessor_free); + + return &it->accessor; +} + +EAPI Eina_List * +evas_object_grid_children_get(const Evas_Object *o) +{ + Eina_List *new_list = NULL, *l; + Evas_Object_Grid_Option *opt; + + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(o, priv, NULL); + + EINA_LIST_FOREACH(priv->children, l, opt) + new_list = eina_list_append(new_list, opt->obj); + + return new_list; +} + +EAPI Eina_Bool +evas_object_grid_mirrored_get(const Evas_Object *obj) +{ + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN_VAL(obj, priv, EINA_FALSE); + return priv->is_mirrored; +} + +EAPI void +evas_object_grid_mirrored_set(Evas_Object *obj, Eina_Bool mirrored) +{ + EVAS_OBJECT_GRID_DATA_GET_OR_RETURN(obj, priv); + mirrored = !!mirrored; + if (priv->is_mirrored != mirrored) + { + priv->is_mirrored = mirrored; + _evas_object_grid_smart_calculate(obj); + } +}