kuuko pushed a commit to branch master.

http://git.enlightenment.org/bindings/python/python-efl.git/commit/?id=4ccea0f35d1a920aeb1b30451c58dab53909664b

commit 4ccea0f35d1a920aeb1b30451c58dab53909664b
Author: Kai Huuhko <[email protected]>
Date:   Fri Mar 13 19:21:36 2015 +0200

    Evas.SmartObject: Add smart callback introspection
---
 efl/evas/efl.evas_object_smart.pxi          | 234 +++++++++++++++++++++++++++-
 examples/elementary/test_core_evas_smart.py |  26 ++--
 include/efl.evas.pxd                        |  22 ++-
 3 files changed, 262 insertions(+), 20 deletions(-)

diff --git a/efl/evas/efl.evas_object_smart.pxi 
b/efl/evas/efl.evas_object_smart.pxi
index 460f3b7..b8bc667 100644
--- a/efl/evas/efl.evas_object_smart.pxi
+++ b/efl/evas/efl.evas_object_smart.pxi
@@ -21,10 +21,78 @@ from efl.eo cimport Eo, EoIterator
 
 from cpython cimport Py_INCREF, Py_DECREF, PyObject_Call, \
     PyMem_Malloc, PyMem_Free
+from libc.stdlib cimport malloc
+from libc.string cimport strdup
 
 #cdef object _smart_classes
 #_smart_classes = list()
 
+cdef list _descriptions_to_list(const Evas_Smart_Cb_Description **arr, 
unsigned int arr_len):
+    cdef:
+        unsigned int i
+        list ret = list()
+
+    if arr == NULL:
+        return ret
+
+    for i in range(arr_len):
+        ret.append(SmartCbDescription.create(arr[i]))
+
+    if arr[i+1] != NULL:
+        EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "array was not NULL 
terminated!", NULL)
+
+    return ret
+
+cdef Evas_Smart_Cb_Description *_descriptions_to_array(descs):
+    cdef:
+        unsigned int arr_len = len(descs)
+        Evas_Smart_Cb_Description *arr
+        SmartCbDescription desc
+
+    if arr_len == 0:
+        return NULL
+
+    # allocate arr_len + 1 so it's NULL terminated
+    arr = <Evas_Smart_Cb_Description *>malloc(arr_len + 1 * 
sizeof(Evas_Smart_Cb_Description))
+
+    for i, desc in enumerate(descs):
+        arr[i] = desc.desc[0]
+
+    return arr
+
+
+cdef class SmartCbDescription:
+    """Introspection description for a smart callback"""
+    cdef const Evas_Smart_Cb_Description *desc
+
+    def __init__(self, name, types):
+        cdef Evas_Smart_Cb_Description *tmp
+        tmp = <Evas_Smart_Cb_Description 
*>malloc(sizeof(Evas_Smart_Cb_Description*))
+        if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
+        tmp.name = strdup(name)
+        if isinstance(types, unicode): types = PyUnicode_AsUTF8String(types)
+        tmp.type = strdup(types)
+        self.desc = <const Evas_Smart_Cb_Description *>tmp
+
+    @staticmethod
+    cdef create(const Evas_Smart_Cb_Description *desc):
+        cdef SmartCbDescription ret = 
SmartCbDescription.__new__(SmartCbDescription)
+        ret.desc = desc
+        return ret
+
+    def __repr__(self):
+        return "%s(%r, %r)" % (self.__class__.__name__, self.name, self.type)
+
+    property name:
+        """:type: string"""
+        def __get__(self):
+            return _ctouni(self.desc.name)
+
+    property type:
+        """:type: string"""
+        def __get__(self):
+            return _ctouni(self.desc.type)
+
 
 cdef void _smart_object_delete(Evas_Object *o) with gil:
     cdef:
@@ -416,8 +484,9 @@ cdef class Smart(object):
     .. versionadded:: 1.14
     """
 
-    def __cinit__(self, bint clipped=False):
-        cdef Evas_Smart_Class *cls_def
+    def __cinit__(self, Smart parent=None, bint clipped=False, 
callback_descriptions=[], *args, **kwargs):
+        cdef:
+            Evas_Smart_Class *cls_def
 
         cls_def = <Evas_Smart_Class*>PyMem_Malloc(sizeof(Evas_Smart_Class))
         if cls_def == NULL:
@@ -445,20 +514,55 @@ cdef class Smart(object):
             cls_def.member_add = _smart_object_member_add
             cls_def.member_del = _smart_object_member_del
 
-        cls_def.parent = NULL
-        cls_def.callbacks = NULL
+        cls_def.parent = parent.cls_def if parent is not None else NULL
+
+        cls_def.callbacks = _descriptions_to_array(callback_descriptions)
+
+        # TODO: interfaces?
         cls_def.interfaces = NULL
+
         cls_def.data = <void *>self
 
-        self.cls = evas_smart_class_new(cls_def)
+        self.cls_def = <const Evas_Smart_Class *>cls_def
+        self.cls = evas_smart_class_new(self.cls_def)
 
     def __dealloc__(self):
         cdef const Evas_Smart_Class *cls_def
         cls_def = evas_smart_class_get(self.cls)
         PyMem_Free(<void*>cls_def)
-        evas_smart_free(self.cls)
+        evas_smart_free(self.cls) # FIXME: Check that all resources (cb 
descriptions etc.) are truly freed
         self.cls = NULL
 
+    property callback_descriptions:
+        def __get__(self):
+            cdef:
+                const Evas_Smart_Cb_Description **descriptions
+                unsigned int count
+
+            descriptions = evas_smart_callbacks_descriptions_get(self.cls, 
&count)
+
+            return _descriptions_to_list(descriptions, count)
+
+    def callback_descriptions_get(self):
+        cdef:
+            const Evas_Smart_Cb_Description **descriptions
+            unsigned int count
+
+        descriptions = evas_smart_callbacks_descriptions_get(self.cls, &count)
+
+        return _descriptions_to_list(descriptions, count)
+
+    def callback_description_find(self, name):
+        cdef:
+            const Evas_Smart_Cb_Description *desc
+
+        if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
+        desc = evas_smart_callback_description_find(self.cls, name)
+        if desc == NULL:
+            return None
+
+        return SmartCbDescription.create(desc)
+
     @staticmethod
     def delete(obj):
         """
@@ -638,7 +742,7 @@ cdef class SmartObject(Object):
         #_smart_classes.append(<uintptr_t>cls_def)
         self._set_obj(evas_object_smart_add(canvas.obj, smart.cls))
         self._set_properties_from_keyword_args(kwargs)
-        self.smart = smart
+        self._smart = smart
 
     cdef int _set_obj(self, cEo *obj) except 0:
         assert self.obj == NULL, "Object must be clean"
@@ -699,6 +803,13 @@ cdef class SmartObject(Object):
         eina_list_free(lst)
         return tuple(ret)
 
+    property smart:
+        def __get__(self):
+            if self._smart is not None:
+                return self._smart
+            else:
+                return Smart.create(evas_object_smart_smart_get(self.obj))
+
     def smart_get(self):
         return self.smart
 
@@ -947,5 +1058,114 @@ cdef class SmartObject(Object):
             <void*>event_info if event_info is not None else NULL
             )
 
+    def callback_descriptions_set(self, descriptions):
+        """Set an smart object **instance's** smart callbacks descriptions.
+
+        :return: ``True`` on success, ``False`` on failure.
+
+        These descriptions are hints to be used by introspection and are
+        not enforced in any way.
+
+        It will not be checked if instance callbacks descriptions have the same
+        name as respective possibly registered in the smart object **class**.
+        Both are kept in different arrays and users of
+        :meth:`callbacks_descriptions_get` should handle this case as they
+        wish.
+
+        .. note::
+
+            While instance callbacks descriptions are possible, they are
+            **not** recommended. Use **class** callbacks descriptions
+            instead as they make you smart object user's life simpler and
+            will use less memory, as descriptions and arrays will be
+            shared among all instances.
+
+
+        :param descriptions: A list with :class:`SmartCbDescription`
+            descriptions. List elements won't be modified at run time, but
+            references to them and their contents will be made, so this array
+            should be kept alive during the whole object's lifetime.
+
+        """
+
+        if not evas_object_smart_callbacks_descriptions_set(
+                self.obj,
+                <const Evas_Smart_Cb_Description 
*>_descriptions_to_array(descriptions)
+                ):
+            raise ValueError("Could not set callback descriptions")
+
+    def callback_descriptions_get(self, get_class=True, get_instance=True):
+        """Retrieve a smart object's known smart callback descriptions
+
+        This call searches for registered callback descriptions for both
+        instance and class of the given smart object. These lists will be
+        sorted by name.
+
+        .. note::
+
+            If just class descriptions are of interest, try
+            :meth:`Smart.callbacks_descriptions_get` instead.
+
+        :param bool get_class: Get class descriptions
+        :param bool get_instance: Get instance descriptions
+        :return: A tuple with two lists, for both class and instance
+            descriptions.
+        :rtype: tuple
+        """
+        cdef:
+            const Evas_Smart_Cb_Description **class_descriptions
+            const Evas_Smart_Cb_Description **instance_descriptions
+            unsigned int class_count, instance_count
+
+        evas_object_smart_callbacks_descriptions_get(
+            self.obj,
+            &class_descriptions if get_class is True else NULL,
+            &class_count,
+            &instance_descriptions if get_instance is True else NULL,
+            &instance_count
+            )
+        return (
+            _descriptions_to_list(class_descriptions, class_count),
+            _descriptions_to_list(instance_descriptions, instance_count)
+            )
+
+    def callback_description_find(self, name, search_class=True, 
search_instance=True):
+        """Find callback description for callback given in ``name``.
+
+        or ``None`` if not found.
+
+        :param string name: name of desired callback, must **not** be ``None``.
+        :param bool search_class: whether to search in class descriptions
+        :param bool search_instance: whether to search in instance descriptions
+        :return: reference to description if found, ``None`` if not found.
+
+        ..
+            The
+            search have a special case for ``name`` being the same
+            pointer as registered with Evas_Smart_Cb_Description, one
+            can use it to avoid excessive use of strcmp().
+        """
+        cdef:
+            const Evas_Smart_Cb_Description *class_description
+            const Evas_Smart_Cb_Description *instance_description
+            list ret = list()
+
+        if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
+
+        evas_object_smart_callback_description_find(
+            self.obj, name,
+            &class_description if search_class is True else NULL,
+            &instance_description if search_instance is True else NULL
+            )
+
+        if class_description != NULL:
+            ret.append(SmartCbDescription.create(class_description))
+        else:
+            ret.append(None)
+        if instance_description != NULL:
+            ret.append(SmartCbDescription.create(instance_description))
+        else:
+            ret.append(None)
+        return ret
 
 _object_mapping_register("Evas_Smart", SmartObject)
diff --git a/examples/elementary/test_core_evas_smart.py 
b/examples/elementary/test_core_evas_smart.py
index e1bb7cf..adb73fc 100644
--- a/examples/elementary/test_core_evas_smart.py
+++ b/examples/elementary/test_core_evas_smart.py
@@ -4,9 +4,8 @@
 import os
 from random import randint
 
-from efl.eo import Eo
 from efl.evas import SmartObject, Smart, EXPAND_BOTH, FILL_BOTH, Rectangle, \
-    Line, FilledImage, Polygon, Text
+    Line, FilledImage, Polygon, Text, SmartCbDescription
 from efl import elementary
 from efl.elementary.window import StandardWindow
 from efl.elementary.box import Box
@@ -23,6 +22,7 @@ def random_color():
 
 
 class MySmart(Smart):
+
     @staticmethod
     def resize(smart_object, w, h):
         print("RESIZE", w, h)
@@ -72,10 +72,15 @@ class MySmart(Smart):
     def clip_unset(smart_object):
         pass
 
+descriptions = (
+    SmartCbDescription("mycb", "i"),
+    )
+
+
 class MySmartObj(SmartObject):
 
-    def __init__(self, canvas):
-        SmartObject.__init__(self, canvas, MySmart())
+    def __init__(self, canvas, smart):
+        SmartObject.__init__(self, canvas, smart)
 
         # gray background
         self.bg = Rectangle(canvas, color=(128, 128, 128, 128))
@@ -131,11 +136,13 @@ class MySmartObj(SmartObject):
 
 
 def btn_add_cb(b):
-    sm = MySmartObj(b.evas)
-    sm.size = 100, 100
-    sm.pos = 100, 100
-    sm.show()
-    objects.append(sm)
+    sm = MySmart(callback_descriptions=descriptions)
+    print(sm.callback_descriptions)
+    so = MySmartObj(b.evas, sm)
+    so.size = 100, 100
+    so.pos = 100, 100
+    so.show()
+    objects.append(so)
 
 
 def btn_del_cb(b):
@@ -154,6 +161,7 @@ def btn_show_cb(b):
 
 def core_evas_smart_clicked(obj, item=None):
     win = StandardWindow("evassmart", "Evas Smart Object Test", autodel=True)
+    print(win.callback_descriptions_get())
     if obj is None:
         win.callback_delete_request_add(lambda o: elementary.exit())
 
diff --git a/include/efl.evas.pxd b/include/efl.evas.pxd
index 1ca573f..15ccc57 100644
--- a/include/efl.evas.pxd
+++ b/include/efl.evas.pxd
@@ -387,7 +387,7 @@ cdef extern from "Evas.h":
         void (*member_add)(Evas_Object *o, Evas_Object *child)
         void (*member_del)(Evas_Object *o, Evas_Object *child)
         const Evas_Smart_Class *parent
-        Evas_Smart_Cb_Description *callbacks
+        const Evas_Smart_Cb_Description *callbacks
         const Evas_Smart_Interface **interfaces
         const void *data
 
@@ -781,12 +781,14 @@ cdef extern from "Evas.h":
 
 
     ####################################################################
-    # Smart Object (py3:TODO)
+    # Smart Object
     #
     void evas_smart_free(Evas_Smart *s)
     Evas_Smart *evas_smart_class_new(Evas_Smart_Class *sc)
     Evas_Smart_Class *evas_smart_class_get(Evas_Smart *s)
     const Eo_Class *evas_object_smart_class_get()
+    const Evas_Smart_Cb_Description 
**evas_smart_callbacks_descriptions_get(const Evas_Smart *s, unsigned int 
*count)
+    const Evas_Smart_Cb_Description  
*evas_smart_callback_description_find(const Evas_Smart *s, const char *name)
 
     void *evas_smart_data_get(Evas_Smart *s)
 
@@ -809,6 +811,9 @@ cdef extern from "Evas.h":
     void evas_object_smart_move_children_relative(Evas_Object *obj, int dx, 
int dy)
     Eina_Iterator *evas_object_smart_iterator_new(const Evas_Object_Smart *obj)
     void evas_object_smart_clipped_smart_set(Evas_Smart_Class *sc)
+    Eina_Bool evas_object_smart_callbacks_descriptions_set(Evas_Object_Smart 
*obj, const Evas_Smart_Cb_Description *descriptions)
+    void evas_object_smart_callbacks_descriptions_get(const Evas_Object_Smart 
*obj, const Evas_Smart_Cb_Description ***class_descriptions, unsigned int 
*class_count, const Evas_Smart_Cb_Description ***instance_descriptions, 
unsigned int *instance_count)
+    void evas_object_smart_callback_description_find(const Evas_Object_Smart 
*obj, const char *name, const Evas_Smart_Cb_Description **class_description, 
const Evas_Smart_Cb_Description **instance_description)
 
 
     ####################################################################
@@ -1210,11 +1215,20 @@ cdef class Textblock(Object):
 #     ctypedef object(*Smart_Conv_Func)(void *)
 
 cdef class Smart:
-    cdef Evas_Smart *cls
+    cdef:
+        Evas_Smart *cls
+        const Evas_Smart_Class *cls_def
+
+    @staticmethod
+    cdef inline create(Evas_Smart *cls):
+        cdef Smart ret = Smart.__new__(Smart)
+        ret.cls = cls
+        ret.cls_def = evas_smart_class_get(cls)
+        return ret
 
 cdef class SmartObject(Object):
     cdef:
-        public Smart smart
+        Smart _smart
         dict _smart_callback_specs
         int _set_obj(self, cEo *obj) except 0
         int _callback_add_full(self, event, object(*)(void*), func, tuple 
args, dict kargs) except 0

-- 


Reply via email to