1141 lines
35 KiB
Cython
1141 lines
35 KiB
Cython
# Copyright (C) 2007-2022 various contributors (see AUTHORS)
|
|
#
|
|
# This file is part of Python-EFL.
|
|
#
|
|
# Python-EFL is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 3 of the License, or (at your option) any later version.
|
|
#
|
|
# Python-EFL is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this Python-EFL. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from efl.utils.conversions cimport eina_list_objects_to_python_list
|
|
from efl.c_eo cimport efl_key_data_set, efl_key_data_get
|
|
from efl.eo cimport Eo, EoIterator
|
|
|
|
from cpython cimport Py_INCREF, Py_DECREF, PyObject_Call, \
|
|
PyMem_Malloc, PyMem_Free
|
|
from libc.stdlib cimport malloc, calloc
|
|
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 = 0
|
|
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!")
|
|
|
|
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 *>calloc(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:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.delete(obj)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_move(Evas_Object *o, Evas_Coord x, Evas_Coord y) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.move(obj, x, y)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.resize(obj, w, h)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_show(Evas_Object *o) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.show(obj)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_hide(Evas_Object *o) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.hide(obj)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_color_set(Evas_Object *o, int r, int g, int b, int a) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.color_set(obj, r, g, b, a)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_clip_set(Evas_Object *o, Evas_Object *clip) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
Object other
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
other = object_from_instance(clip)
|
|
|
|
try:
|
|
cls.clip_set(obj, other)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_clip_unset(Evas_Object *o) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.clip_unset(obj)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_calculate(Evas_Object *o) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
try:
|
|
cls.calculate(obj)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_member_add(Evas_Object *o, Evas_Object *clip) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
SmartObject obj
|
|
Object other
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
other = object_from_instance(clip)
|
|
|
|
try:
|
|
cls.member_add(obj, other)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef void _smart_object_member_del(Evas_Object *o, Evas_Object *clip) with gil:
|
|
cdef:
|
|
void *tmp
|
|
Smart cls
|
|
Eo obj
|
|
Object other
|
|
|
|
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!")
|
|
return
|
|
cls = <Smart>tmp
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
obj = None
|
|
else:
|
|
obj = <Eo>tmp
|
|
|
|
other = object_from_instance(clip)
|
|
|
|
try:
|
|
cls.member_del(obj, other)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef class _SmartCb:
|
|
cdef:
|
|
SmartObject obj
|
|
bytes event
|
|
object(*event_conv)(void*)
|
|
uintptr_t conv
|
|
object func
|
|
tuple args
|
|
dict kargs
|
|
|
|
|
|
cdef object _smart_cb_pass_conv(void *addr):
|
|
return <object>addr
|
|
|
|
|
|
cdef void _smart_callback(void *data, Evas_Object *o, void *event_info) with gil:
|
|
if data == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "data is NULL!")
|
|
return
|
|
|
|
cdef:
|
|
void *tmp = NULL
|
|
SmartObject obj
|
|
object event, ei
|
|
_SmartCb spec
|
|
list tmp_args
|
|
list lst
|
|
|
|
tmp = efl_key_data_get(o, "python-eo")
|
|
if tmp == NULL:
|
|
EINA_LOG_DOM_ERR(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!")
|
|
return
|
|
else:
|
|
obj = <SmartObject>tmp
|
|
|
|
event = <object>data
|
|
lst = <list>obj._smart_callback_specs[event]
|
|
|
|
for spec in lst:
|
|
if event_info == NULL:
|
|
try:
|
|
tmp_args = [spec.obj]
|
|
if spec.event_conv != NULL:
|
|
tmp_args.append(None)
|
|
tmp_args.extend(spec.args)
|
|
PyObject_Call(spec.func, tuple(tmp_args), spec.kargs)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
elif event_info != NULL and spec.event_conv == NULL:
|
|
#EINA_LOG_DOM_WARN(
|
|
# PY_EFL_EVAS_LOG_DOMAIN,
|
|
# 'event_info for event "%s" is not NULL and there is no event_conv!',
|
|
# <const char*>event
|
|
# )
|
|
try:
|
|
tmp_args = [spec.obj]
|
|
tmp_args.extend(spec.args)
|
|
PyObject_Call(spec.func, tuple(tmp_args), spec.kargs)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
else:
|
|
try:
|
|
tmp_args = [spec.obj]
|
|
tmp_args.append(spec.event_conv(event_info))
|
|
tmp_args.extend(spec.args)
|
|
PyObject_Call(spec.func, tuple(tmp_args), spec.kargs)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
cdef class Smart(object):
|
|
|
|
"""
|
|
An abstract class that defines the behavior of the SmartObject.
|
|
|
|
:param clipped: Make this Smart use a clipped class, ignoring the provided
|
|
callback methods, except :meth:`calculate` and :meth:`resize`.
|
|
:type clipped: bool
|
|
|
|
.. versionadded:: 1.14
|
|
|
|
.. staticmethod:: delete(obj)
|
|
|
|
Called in order to remove object from canvas and deallocate its resources.
|
|
|
|
Usually you delete object's children here.
|
|
|
|
.. staticmethod:: member_add(obj, Object child)
|
|
|
|
Called when children is added to object.
|
|
|
|
.. staticmethod:: member_del(obj, Object child)
|
|
|
|
Called when children is removed from object.
|
|
|
|
.. staticmethod:: move(obj, int x, int y)
|
|
|
|
Called in order to move object to given position.
|
|
|
|
Usually you move children here.
|
|
|
|
.. staticmethod:: resize(obj, int w, int h)
|
|
|
|
Called in order to resize object.
|
|
|
|
.. staticmethod:: show(obj)
|
|
|
|
Called in order to show the given element.
|
|
|
|
Usually you call the same function on children.
|
|
|
|
.. staticmethod:: hide(obj)
|
|
|
|
Called in order to hide the given element.
|
|
|
|
Usually you call the same function on children.
|
|
|
|
.. staticmethod:: color_set(obj, int r, int g, int b, int a)
|
|
|
|
Called in order to change object color.
|
|
|
|
.. staticmethod:: clip_set(obj, Eo clip)
|
|
|
|
Called in order to limit object's visible area.
|
|
|
|
.. staticmethod:: clip_unset(obj)
|
|
|
|
Called in order to unlimit object's visible area.
|
|
|
|
.. staticmethod:: calculate(obj)
|
|
|
|
Called before object is used for rendering and it is marked as dirty/changed with :py:func:`changed`.
|
|
|
|
"""
|
|
|
|
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:
|
|
raise MemoryError
|
|
|
|
name = self.__class__.__name__
|
|
if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
|
|
|
|
cls_def.name = name
|
|
cls_def.version = enums.EVAS_SMART_CLASS_VERSION
|
|
|
|
if clipped:
|
|
evas_object_smart_clipped_smart_set(cls_def)
|
|
# override add to NULL?
|
|
else:
|
|
cls_def.add = NULL # use python constructor
|
|
|
|
if "delete" in self.__class__.__dict__:
|
|
cls_def.delete = _smart_object_delete
|
|
else:
|
|
cls_def.delete = NULL
|
|
|
|
if "move" in self.__class__.__dict__:
|
|
cls_def.move = _smart_object_move
|
|
else:
|
|
cls_def.move = NULL
|
|
|
|
if "show" in self.__class__.__dict__:
|
|
cls_def.show = _smart_object_show
|
|
else:
|
|
cls_def.show = NULL
|
|
|
|
if "hide" in self.__class__.__dict__:
|
|
cls_def.hide = _smart_object_hide
|
|
else:
|
|
cls_def.hide = NULL
|
|
|
|
if "color_set" in self.__class__.__dict__:
|
|
cls_def.color_set = _smart_object_color_set
|
|
else:
|
|
cls_def.color_set = NULL
|
|
|
|
if "clip_set" in self.__class__.__dict__:
|
|
cls_def.clip_set = _smart_object_clip_set
|
|
else:
|
|
cls_def.clip_set = NULL
|
|
|
|
if "clip_unset" in self.__class__.__dict__:
|
|
cls_def.clip_unset = _smart_object_clip_unset
|
|
else:
|
|
cls_def.clip_unset = NULL
|
|
|
|
if "member_add" in self.__class__.__dict__:
|
|
cls_def.member_add = _smart_object_member_add
|
|
else:
|
|
cls_def.member_add = NULL
|
|
|
|
if "member_del" in self.__class__.__dict__:
|
|
cls_def.member_del = _smart_object_member_del
|
|
else:
|
|
cls_def.member_del = NULL
|
|
|
|
if "resize" in self.__class__.__dict__:
|
|
cls_def.resize = _smart_object_resize
|
|
else:
|
|
cls_def.resize = NULL
|
|
|
|
if "calculate" in self.__class__.__dict__:
|
|
cls_def.calculate = _smart_object_calculate
|
|
else:
|
|
cls_def.calculate = 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_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) # 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)
|
|
|
|
|
|
cdef class SmartObject(Object):
|
|
|
|
"""
|
|
Smart Evas Objects.
|
|
|
|
Smart objects are user-defined Evas components, often used to group
|
|
multiple basic elements, associate an object with a clip and deal with
|
|
them as an unit. See evas documentation for more details.
|
|
|
|
..
|
|
Recommended use is to create an **clipper** object and clip every other
|
|
member to it, then you can have all your other members to be always
|
|
visible and implement :py:func:`hide`, :py:func:`show`,
|
|
:py:func:`color_set`, :py:func:`clip_set` and :py:func:`clip_unset` to
|
|
just affect the **clipper**. See :py:class:`ClippedSmartObject`.
|
|
|
|
**Pay attention that just creating an object within the SmartObject
|
|
doesn't make it a member!** You must do :py:func:`member_add` or use one of
|
|
the provided factories to ensure that. Failing to do so will leave
|
|
created objects on different layer and no stacking will be done for you.
|
|
|
|
.. note::
|
|
If you redefine object's __init__(), you MUST call your parent!
|
|
Failing to do so will result in objects that just work from Python
|
|
and not from C, for instance, adding your object to Edje swallow
|
|
that clips or set color it will not behave as expected.
|
|
|
|
:param canvas: Evas canvas for this object
|
|
:type canvas: :class:`~efl.evas.Canvas`
|
|
:param smart: Smart prototype
|
|
:type smart: :class:`Smart`
|
|
|
|
..
|
|
:keyword size: Width and height
|
|
:type size: tuple of ints
|
|
:keyword pos: X and Y
|
|
:type pos: tuple of ints
|
|
:keyword geometry: X, Y, width, height
|
|
:type geometry: tuple of ints
|
|
:keyword color: R, G, B, A
|
|
:type color: tuple of ints
|
|
:keyword name: Object name
|
|
:type name: string
|
|
|
|
.. versionchanged:: 1.14
|
|
API was broken because this class had some "hackish" behavior which
|
|
could no longer be supported because of changes in Cython 0.21.1
|
|
|
|
The abstract methods are now in a separate class :class:`Smart` which
|
|
should be instantiated and passed to this classes constructor as
|
|
parameter ``smart``
|
|
"""
|
|
def __cinit__(self):
|
|
self._smart_callback_specs = dict()
|
|
|
|
def __init__(self, Canvas canvas not None, Smart smart not None, **kwargs):
|
|
#_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
|
|
|
|
cdef int _set_obj(self, cEo *obj) except 0:
|
|
assert self.obj == NULL, "Object must be clean"
|
|
assert obj != NULL, "Cannot set a NULL object"
|
|
|
|
self.obj = obj
|
|
efl_key_data_set(self.obj, "python-eo", <void *>self)
|
|
evas_object_event_callback_add(obj, enums.EVAS_CALLBACK_FREE,
|
|
obj_free_cb, <void *>self)
|
|
Py_INCREF(self)
|
|
|
|
return 1
|
|
|
|
def __iter__(self):
|
|
return EoIterator.create(evas_object_smart_iterator_new(self.obj))
|
|
|
|
def member_add(self, Object child):
|
|
"""Set an evas object as a member of this object.
|
|
|
|
Members will automatically be stacked and layered with the smart
|
|
object. The various stacking function will operate on members relative
|
|
to the other members instead of the entire canvas.
|
|
|
|
Non-member objects can not interleave a smart object's members.
|
|
|
|
:note: if **child** is already member of another SmartObject, it
|
|
will be deleted from that membership and added to this object.
|
|
"""
|
|
evas_object_smart_member_add(child.obj, self.obj)
|
|
|
|
@staticmethod
|
|
def member_del(Object child):
|
|
"""Removes a member object from a smart object.
|
|
|
|
.. attention:: this will actually map to C API as
|
|
``evas_object_smart_member_del(child)``, so the object will loose
|
|
it's parent **event if the object is not part of this object**.
|
|
"""
|
|
evas_object_smart_member_del(child.obj)
|
|
|
|
property members:
|
|
"""
|
|
|
|
:rtype: tuple of :py:class:`Object`
|
|
|
|
"""
|
|
def __get__(self):
|
|
cdef:
|
|
Eina_List *lst = evas_object_smart_members_get(self.obj)
|
|
list ret = eina_list_objects_to_python_list(lst)
|
|
eina_list_free(lst)
|
|
return tuple(ret)
|
|
|
|
def members_get(self):
|
|
cdef:
|
|
Eina_List *lst = evas_object_smart_members_get(self.obj)
|
|
list ret = eina_list_objects_to_python_list(lst)
|
|
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
|
|
|
|
def move_children_relative(self, int dx, int dy):
|
|
"""Moves all children objects relative to a given offset.
|
|
|
|
This will make each of object's children to move, from where
|
|
they before, with those delta values (offsets) on both directions.
|
|
|
|
.. note:: This is most useful on custom :func:`Smart.move` functions.
|
|
|
|
.. note:: Clipped smart objects already make use of this function on
|
|
their :func:`Smart.move` function definition.
|
|
|
|
:param dx: horizontal offset (delta).
|
|
:param dy: vertical offset (delta).
|
|
"""
|
|
evas_object_smart_move_children_relative(self.obj, dx, dy)
|
|
|
|
def changed(self):
|
|
"""Mark object as changed, so it's :py:func:`calculate()` will be called.
|
|
|
|
If an object is changed and it provides a calculate() method,
|
|
it will be called from :py:func:`Canvas.render()`, what we call pre-render
|
|
calculate.
|
|
|
|
This can be used to postpone heavy calculations until you need to
|
|
display the object, example: layout calculations.
|
|
"""
|
|
evas_object_smart_changed(self.obj)
|
|
|
|
property need_recalculate:
|
|
"""The need_recalculate flag of given smart object.
|
|
|
|
If this flag is set then calculate() callback (method) of the
|
|
given smart object will be called, if one is provided, during
|
|
render phase usually evas_render(). After this step, this flag
|
|
will be automatically unset.
|
|
|
|
If no calculate() is provided, this flag will be left unchanged.
|
|
|
|
.. note::
|
|
Just setting this flag will not make scene dirty and
|
|
evas_render() will have no effect. To do that, use
|
|
evas_object_smart_changed(), that will automatically call this
|
|
function with 1 as parameter.
|
|
|
|
.. note::
|
|
This flag will be unset during the render phase, after
|
|
calculate() is called if one is provided. If no calculate()
|
|
is provided, then the flag will be left unchanged after render
|
|
phase.
|
|
|
|
"""
|
|
def __set__(self, value):
|
|
evas_object_smart_need_recalculate_set(self.obj, value)
|
|
|
|
def __get__(self):
|
|
return evas_object_smart_need_recalculate_get(self.obj)
|
|
|
|
def need_recalculate_set(self, unsigned int value):
|
|
evas_object_smart_need_recalculate_set(self.obj, value)
|
|
|
|
def need_recalculate_get(self):
|
|
return evas_object_smart_need_recalculate_get(self.obj)
|
|
|
|
def calculate(self):
|
|
evas_object_smart_calculate(self.obj)
|
|
|
|
#
|
|
# Callbacks
|
|
# =========
|
|
#
|
|
|
|
cdef int _callback_add_full(self, event, object(*event_conv)(void *), func, tuple args, dict kargs) except 0:
|
|
"""Add a callback for the smart event specified by event.
|
|
|
|
:param event: event name
|
|
:type event: string
|
|
:param event_conv: Conversion function to get the
|
|
pointer (as a long) to the object to be given to the
|
|
function as the second parameter. If None, then no
|
|
parameter will be given to the callback.
|
|
:type event_conv: function
|
|
:param func: what to callback. Should have the signature::
|
|
|
|
function(object, event_info, *args, **kargs)
|
|
function(object, *args, **kargs) (if no event_conv is provided)
|
|
|
|
:type func: function
|
|
|
|
:raise TypeError: if **func** is not callable.
|
|
:raise TypeError: if **event_conv** is not callable or None.
|
|
|
|
"""
|
|
if not callable(func):
|
|
raise TypeError("func must be callable")
|
|
|
|
if event is None:
|
|
raise TypeError("event must be the name of the event")
|
|
|
|
cdef:
|
|
list lst
|
|
_SmartCb spec
|
|
|
|
if isinstance(event, unicode): event = PyUnicode_AsUTF8String(event)
|
|
|
|
spec = _SmartCb.__new__(_SmartCb)
|
|
spec.obj = self
|
|
spec.event = event
|
|
spec.event_conv = event_conv
|
|
spec.func = func
|
|
spec.args = args
|
|
spec.kargs = kargs
|
|
|
|
lst = <list>self._smart_callback_specs.setdefault(event, [])
|
|
if not lst:
|
|
evas_object_smart_callback_add(self.obj,
|
|
<const char*>spec.event,
|
|
_smart_callback,
|
|
<void *>spec.event
|
|
)
|
|
lst.append(spec)
|
|
|
|
return 1
|
|
|
|
cdef int _callback_del_full(self, event, object(*event_conv)(void *), func) except 0:
|
|
"""Remove a smart callback.
|
|
|
|
Removes a callback that was added by :py:func:`_callback_add_full()`.
|
|
|
|
:param event: event name
|
|
:type event: string
|
|
:param event_conv: same as registered with :py:func:`_callback_add_full()`
|
|
:type event_conv: function
|
|
:param func: what to callback, should have be previously registered.
|
|
:type func: function
|
|
|
|
:precond: **event**, **event_conv** and **func** must be used as
|
|
parameter for :py:func:`_callback_add_full()`.
|
|
|
|
:raise ValueError: if there was no **func** connected with this event.
|
|
|
|
"""
|
|
cdef:
|
|
_SmartCb spec
|
|
int found = 0
|
|
int i
|
|
void *tmp
|
|
list lst
|
|
|
|
if isinstance(event, unicode): event = PyUnicode_AsUTF8String(event)
|
|
|
|
lst = <list>self._smart_callback_specs.get(event, None)
|
|
if lst is None:
|
|
raise ValueError("No callbacks registered for the given event type")
|
|
|
|
for i, spec in enumerate(lst):
|
|
if spec.func == func:
|
|
found = 1
|
|
break
|
|
|
|
if found == 0:
|
|
raise ValueError("func not registered")
|
|
|
|
lst.pop(i)
|
|
|
|
if not lst:
|
|
tmp = evas_object_smart_callback_del(self.obj,
|
|
event,
|
|
_smart_callback
|
|
)
|
|
if tmp == NULL:
|
|
raise RuntimeError("Something went wrong while unregistering!")
|
|
|
|
return 1
|
|
|
|
cdef int _callback_add(self, event, func, args, kargs) except 0:
|
|
"""Add a callback for the smart event specified by event.
|
|
|
|
:param event: event name
|
|
:type event: string
|
|
:param func: what to callback. Should have the signature:
|
|
*function(object, *args, **kargs)*
|
|
:type func: function
|
|
|
|
:raise TypeError: if **func** is not callable.
|
|
|
|
"""
|
|
return self._callback_add_full(event, NULL, func, args, kargs)
|
|
|
|
cdef int _callback_del(self, event, func) except 0:
|
|
"""Remove a smart callback.
|
|
|
|
Removes a callback that was added by :py:func:`_callback_add()`.
|
|
|
|
:param event: event name
|
|
:type event: string
|
|
:param func: what to callback, should have be previously registered.
|
|
:type func: function
|
|
|
|
:precond: **event** and **func** must be used as parameter for
|
|
:py:func:`_callback_add()`.
|
|
|
|
:raise ValueError: if there was no **func** connected with this event.
|
|
|
|
"""
|
|
return self._callback_del_full(event, NULL, func)
|
|
|
|
def callback_add(self, name, func, *args, **kargs):
|
|
"""Add a callback for the smart event specified by event.
|
|
|
|
:param name: Event name
|
|
:param func:
|
|
What to callback.
|
|
Should have the signature::
|
|
|
|
function(object, event_info, *args, **kargs)
|
|
|
|
:raise TypeError: if **func** is not callable.
|
|
|
|
.. warning::
|
|
**event_info** will always be a python object, if the
|
|
signal is provided by a C-only class, it will crash.
|
|
|
|
"""
|
|
self._callback_add_full(name, _smart_cb_pass_conv, func, args, kargs)
|
|
|
|
def callback_del(self, name, func):
|
|
"""Remove a smart callback.
|
|
|
|
Removes a callback that was added by :py:func:`callback_add()`.
|
|
|
|
:param name: event name
|
|
:param func: what to callback, should have be previously registered.
|
|
:precond: **event** and **func** must be used as parameter for
|
|
:py:func:`callback_add`.
|
|
|
|
:raise ValueError: if there was no **func** connected with this event.
|
|
"""
|
|
self._callback_del_full(name, _smart_cb_pass_conv, func)
|
|
|
|
def callback_call(self, name, event_info=None):
|
|
"""Call any smart callbacks for event.
|
|
|
|
:param name: the event name
|
|
:param event_info: an event specific info to pass to the callback.
|
|
|
|
This should be called internally in the smart object when some
|
|
specific event has occurred. The documentation for the smart object
|
|
should include a list of possible events and what type of
|
|
**event_info** to expect.
|
|
|
|
.. attention::
|
|
**event_info** will always be a python object.
|
|
"""
|
|
if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
|
|
evas_object_smart_callback_call(
|
|
self.obj, name,
|
|
<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)
|