Evas_Smart_Interface support.

This gives Evas simple (smart object) interfaces, to extend the object behaviorextension beyond the limits of mere sub-classing.

Patch by: Gustavo Lima Chaves <glima@profusion.mobi>



SVN revision: 73302
This commit is contained in:
Gustavo Lima Chaves 2012-07-04 21:23:03 +00:00
parent 3ef67a7c87
commit b64fa6453a
4 changed files with 382 additions and 1 deletions

View File

@ -573,6 +573,22 @@ typedef struct _Evas_Precision_Position Evas_Precision_Position; /**< assoc
*/
typedef struct _Evas_Smart_Class Evas_Smart_Class;
/**
* @typedef Evas_Smart_Interface
*
* A smart object's @b base interface definition
*
* An Evas interface is exactly like the OO-concept: an 'contract' or
* API a given object is declared to support. A smart object may have
* more than one interface, thus extending the behavior it gets from
* sub-classing.
*
* @since 1.3
*
* @ingroup Evas_Smart_Group
*/
typedef struct _Evas_Smart_Interface Evas_Smart_Interface;
/**
* @typedef Evas_Smart_Cb_Description
*
@ -9633,10 +9649,33 @@ struct _Evas_Smart_Class
const Evas_Smart_Class *parent; /**< this class inherits from this parent */
const Evas_Smart_Cb_Description *callbacks; /**< callbacks at this level, @c NULL terminated */
void *interfaces; /**< to be used in a future near you */
const Evas_Smart_Interface **interfaces; /**< #Evas_Smart_Interface pointers array, @c NULL terminated. These will be the interfaces supported at this level for an object (parents may have others) @since 1.3 */
const void *data;
};
/**
* @struct _Evas_Smart_Interface
*
* A smart object's @b base interface definition
*
* Every Evas interface must have a name field, pointing to a global,
* constant string variable. This string pointer will be the only way
* of retrieving back a given interface from a smart object. Two
* function pointers must be defined, too, which will be called at
* object creation and deletion times.
*
* @since 1.3
*
* @ingroup Evas_Smart_Group
*/
struct _Evas_Smart_Interface
{
const char *name; /**< Name of the given interface */
unsigned private_size; /**< Size, in bytes, of the interface's private dada blob. This will be allocated and freed automatically for you. Get it with evas_object_smart_interface_data_get(). */
Eina_Bool (*add)(Evas_Object *obj); /**< Function to be called at object creation time */
void (*del)(Evas_Object *obj); /**< Function to be called at object deletion time */
};
/**
* @struct _Evas_Smart_Cb_Description
*
@ -9846,6 +9885,96 @@ struct _Evas_Smart_Cb_Description
return smart; \
}
/**
* @def EVAS_SMART_SUBCLASS_IFACE_NEW
*
* @since 1.3
*
* Convenience macro to subclass a given Evas smart class. This is the
* same as #EVAS_SMART_SUBCLASS_NEW, but now <b>declaring smart
* interfaces</b> besides the smart callbacks.
*
* @param smart_name The name used for the smart class. e.g:
* @c "Evas_Object_Box".
* @param prefix Prefix used for all variables and functions defined
* and referenced by this macro.
* @param api_type Type of the structure used as API for the smart
* class. Either #Evas_Smart_Class or something
* derived from it.
* @param parent_type Type of the parent class API.
* @param parent_func Function that gets the parent class. e.g:
* evas_object_box_smart_class_get().
* @param cb_desc Array of smart callback descriptions for this smart
* class.
* @param ifaces Array of Evas smart interafaces for this smart
* class.
*
* This macro saves some typing when writing a smart class derived
* from another one. In order to work, the user @b must provide some
* functions adhering to the following guidelines:
* - @<prefix@>_smart_set_user(): the @b internal @c _smart_set
* function (defined by this macro) will call this one, provided by
* the user, after inheriting everything from the parent, which
* should <b>take care of setting the right member functions for
* the class</b>, both overrides and extensions, if any.
* - If this new class should be subclassable as well, a @b public
* @c _smart_set() function is desirable to fill in the class used as
* parent by the children. It's up to the user to provide this
* interface, which will most likely call @<prefix@>_smart_set() to
* get the job done.
*
* After the macro's usage, the following will be defined for use:
* - @<prefix@>_parent_sc: A pointer to the @b parent smart
* class. When calling parent functions from overloaded ones, use
* this global variable.
* - @<prefix@>_smart_class_new(): this function returns the
* #Evas_Smart needed to create smart objects with this class,
* which should be passed to evas_object_smart_add().
*
* @warning @p smart_name has to be a pointer to a globally available
* string! The smart class created here will just have a pointer set
* to that, and all object instances will depend on it for smart class
* name lookup.
*
* @ingroup Evas_Smart_Group
*/
#define EVAS_SMART_SUBCLASS_IFACE_NEW(smart_name, \
prefix, \
api_type, \
parent_type, \
parent_func, \
cb_desc, \
ifaces) \
static const parent_type * prefix##_parent_sc = NULL; \
static void prefix##_smart_set_user(api_type * api); \
static void prefix##_smart_set(api_type * api) \
{ \
Evas_Smart_Class *sc; \
if (!(sc = (Evas_Smart_Class *)api)) \
return; \
if (!prefix##_parent_sc) \
prefix##_parent_sc = parent_func(); \
evas_smart_class_inherit(sc, prefix##_parent_sc); \
prefix##_smart_set_user(api); \
} \
static Evas_Smart *prefix##_smart_class_new(void) \
{ \
static Evas_Smart *smart = NULL; \
static api_type api; \
if (!smart) \
{ \
Evas_Smart_Class *sc = (Evas_Smart_Class *)&api; \
memset(&api, 0, sizeof(api_type)); \
sc->version = EVAS_SMART_CLASS_VERSION; \
sc->name = smart_name; \
sc->callbacks = cb_desc; \
sc->interfaces = ifaces; \
prefix##_smart_set(&api); \
smart = evas_smart_class_new(sc); \
} \
return smart; \
}
/**
* @def EVAS_SMART_DATA_ALLOC
*
@ -10461,6 +10590,34 @@ EAPI void evas_object_smart_callbacks_descriptions_get(const Evas_Object
*/
EAPI void evas_object_smart_callback_description_find(const Evas_Object *obj, const char *name, const Evas_Smart_Cb_Description **class_description, const Evas_Smart_Cb_Description **instance_description) EINA_ARG_NONNULL(1, 2);
/**
* Retrieve an Evas smart object's interface, by name string pointer.
*
* @param obj An Evas smart object.
* @param name Name string of the desired interface, which must be the
* same pointer used at the interface's declarion, when
* creating the smart object @a obj.
*
* @since 1.3
*
* @return The interface's handle pointer, if found, @c NULL
* otherwise.
*/
const void *evas_object_smart_interface_get(const Evas_Object *obj, const char *name);
/**
* Retrieve an Evas smart object interface's <b>private data</b>.
*
* @param obj An Evas smart object.
* @param iface The given object's interface handle.
*
* @since 1.3
*
* @return The object interface's private data blob pointer, if found,
* @c NULL otherwise.
*/
void *evas_object_smart_interface_data_get(const Evas_Object *obj, const Evas_Smart_Interface *iface);
/**
* Mark smart object as changed, dirty.
*

View File

@ -100,6 +100,54 @@ evas_object_smart_data_get(const Evas_Object *obj)
return o->data;
}
EAPI const void *
evas_object_smart_interface_get(const Evas_Object *obj,
const char *name)
{
unsigned int i;
Evas_Smart *s;
MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
return NULL;
MAGIC_CHECK_END();
s = evas_object_smart_smart_get(obj);
for (i = 0; i < s->interfaces.size; i++)
{
const Evas_Smart_Interface *iface;
iface = s->interfaces.array[i];
if (iface->name == name)
return iface;
}
return NULL;
}
EAPI void *
evas_object_smart_interface_data_get(const Evas_Object *obj,
const Evas_Smart_Interface *iface)
{
unsigned int i;
Evas_Smart *s;
MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ);
return NULL;
MAGIC_CHECK_END();
s = evas_object_smart_smart_get(obj);
for (i = 0; i < s->interfaces.size; i++)
{
if (iface == s->interfaces.array[i])
return obj->interface_privates[i];
}
return NULL;
}
EAPI Evas_Smart *
evas_object_smart_smart_get(const Evas_Object *obj)
{
@ -311,10 +359,76 @@ _evas_object_smart_members_all_del(Evas_Object *obj)
}
}
static void
_evas_smart_class_ifaces_private_data_alloc(Evas_Object *obj,
Evas_Smart *s)
{
unsigned int i, total_priv_sz = 0;
const Evas_Smart_Class *sc;
unsigned char *ptr;
/* get total size of interfaces private data */
for (sc = s->smart_class; sc; sc = sc->parent)
{
const Evas_Smart_Interface **ifaces_array = sc->interfaces;
if (!ifaces_array) continue;
while (*ifaces_array)
{
const Evas_Smart_Interface *iface = *ifaces_array;
if (!iface->name) break;
if (iface->private_size > 0)
{
unsigned int size = iface->private_size;
if (size % sizeof(void *) != 0)
size += sizeof(void *) - (size % sizeof(void *));
total_priv_sz += size;
}
ifaces_array++;
}
}
obj->interface_privates = malloc
(s->interfaces.size * sizeof(void *) + total_priv_sz);
if (!obj->interface_privates)
{
ERR("malloc failed!");
return;
}
/* make private data array ptrs point to right places, WHICH LIE ON
* THE SAME STRUCT, AFTER THE # OF INTERFACES COUNT */
ptr = (unsigned char *)(obj->interface_privates + s->interfaces.size);
for (i = 0; i < s->interfaces.size; i++)
{
unsigned int size;
size = s->interfaces.array[i]->private_size;
if (size == 0)
{
obj->interface_privates[i] = NULL;
continue;
}
obj->interface_privates[i] = ptr;
memset(ptr, 0, size);
if (size % sizeof(void *) != 0)
size += sizeof(void *) - (size % sizeof(void *));
ptr += size;
}
}
EAPI Evas_Object *
evas_object_smart_add(Evas *e, Evas_Smart *s)
{
Evas_Object *obj;
unsigned int i;
MAGIC_CHECK(e, Evas, MAGIC_EVAS);
return NULL;
@ -332,8 +446,26 @@ evas_object_smart_add(Evas *e, Evas_Smart *s)
evas_object_smart_use(s);
_evas_smart_class_ifaces_private_data_alloc(obj, s);
if (s->smart_class->add) s->smart_class->add(obj);
for (i = 0; i < s->interfaces.size; i++)
{
const Evas_Smart_Interface *iface;
iface = s->interfaces.array[i];
if (iface->add)
{
if (!iface->add(obj))
{
ERR("failed to create interface %s\n", iface->name);
evas_object_del(obj);
return NULL;
}
}
}
return obj;
}
@ -753,11 +885,25 @@ void
evas_object_smart_del(Evas_Object *obj)
{
Evas_Smart *s;
unsigned int i;
if (obj->delete_me) return;
s = obj->smart.smart;
if ((s) && (s->smart_class->del)) s->smart_class->del(obj);
if (obj->smart.parent) evas_object_smart_member_del(obj);
for (i = 0; i < s->interfaces.size; i++)
{
const Evas_Smart_Interface *iface;
iface = s->interfaces.array[i];
if (iface->del) iface->del(obj);
}
free(obj->interface_privates);
obj->interface_privates = NULL;
if (s) evas_object_smart_unuse(s);
}

View File

@ -3,6 +3,7 @@
static void _evas_smart_class_callbacks_create(Evas_Smart *s);
static void _evas_smart_class_interfaces_create(Evas_Smart *s);
/* all public */
@ -16,6 +17,8 @@ evas_smart_free(Evas_Smart *s)
if (s->usage > 0) return;
if (s->class_allocated) free((void *)s->smart_class);
free(s->callbacks.array);
free(s->interfaces.array);
free(s);
}
@ -36,6 +39,7 @@ evas_smart_class_new(const Evas_Smart_Class *sc)
s->smart_class = sc;
_evas_smart_class_callbacks_create(s);
_evas_smart_class_interfaces_create(s);
return s;
}
@ -238,6 +242,7 @@ _evas_smart_class_callbacks_create(Evas_Smart *s)
if (n == 0) return;
if (!evas_smart_cb_descriptions_resize(&s->callbacks, n)) return;
s->callbacks.size = n;
for (n = 0, sc = s->smart_class; sc; sc = sc->parent)
{
const Evas_Smart_Cb_Description *d;
@ -247,6 +252,67 @@ _evas_smart_class_callbacks_create(Evas_Smart *s)
evas_smart_cb_descriptions_fix(&s->callbacks);
}
static void
_evas_smart_class_interfaces_create(Evas_Smart *s)
{
unsigned int i, total_priv_sz;
const Evas_Smart_Class *sc;
/* get number of interfaces on the smart */
for (i = 0, sc = s->smart_class; sc; sc = sc->parent)
{
const Evas_Smart_Interface **ifaces_array = sc->interfaces;
if (!ifaces_array) continue;
while (*ifaces_array)
{
const Evas_Smart_Interface *iface = *ifaces_array;
if (!iface->name) break;
i++;
if (iface->private_size > 0)
{
unsigned int size = iface->private_size;
if (size % sizeof(void *) != 0)
size += sizeof(void *) - (size % sizeof(void *));
total_priv_sz += size;
}
ifaces_array++;
}
}
if (!i) return;
s->interfaces.array = malloc(i * sizeof(Evas_Smart_Interface *));
if (!s->interfaces.array)
{
ERR("malloc failed!");
return;
}
s->interfaces.size = i;
for (i = 0, sc = s->smart_class; sc; sc = sc->parent)
{
const Evas_Smart_Interface **ifaces_array = sc->interfaces;
if (!ifaces_array) continue;
while (*ifaces_array)
{
const Evas_Smart_Interface *iface = *ifaces_array;
if (!iface->name) break;
s->interfaces.array[i++] = iface;
ifaces_array++;
}
}
}
static int
_evas_smart_cb_description_cmp_search(const void *p1, const void *p2)
{

View File

@ -45,6 +45,7 @@ typedef struct _Evas_Callbacks Evas_Callbacks;
typedef struct _Evas_Format Evas_Format;
typedef struct _Evas_Map_Point Evas_Map_Point;
typedef struct _Evas_Smart_Cb_Description_Array Evas_Smart_Cb_Description_Array;
typedef struct _Evas_Smart_Interfaces_Array Evas_Smart_Interfaces_Array;
typedef struct _Evas_Post_Callback Evas_Post_Callback;
typedef struct _Evas_Coord_Touch_Point Evas_Coord_Touch_Point;
@ -245,6 +246,12 @@ struct _Evas_Smart_Cb_Description_Array
const Evas_Smart_Cb_Description **array;
};
struct _Evas_Smart_Interfaces_Array
{
unsigned int size;
const Evas_Smart_Interface **array;
};
struct _Evas_Smart
{
DATA32 magic;
@ -254,6 +261,7 @@ struct _Evas_Smart
const Evas_Smart_Class *smart_class;
Evas_Smart_Cb_Description_Array callbacks;
Evas_Smart_Interfaces_Array interfaces;
unsigned char delete_me : 1;
unsigned char class_allocated : 1;
@ -587,6 +595,10 @@ struct _Evas_Object
int in_move, in_resize;
} doing;
/* ptr array + data blob holding all interfaces private data for
* this object */
void **interface_privates;
unsigned int ref;
unsigned char delete_me;