python-efl/efl/evas/efl.evas_object_smart.pxi

817 lines
24 KiB
Cython

# Copyright (C) 2007-2015 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 eo_do, eo_do_ret, eo_key_data_del, eo_key_data_set, eo_key_data_get
from efl.eo cimport Eo
from cpython cimport PyMem_Malloc, PyMethod_New, Py_INCREF, Py_DECREF
#cdef object _smart_classes
#_smart_classes = list()
cdef void _smart_object_delete(Evas_Object *o) with gil:
cdef:
void *tmp
Smart cls
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
try:
cls.delete(obj)
except Exception:
traceback.print_exc()
# eo_do(self.obj,
# eo_event_callback_del(EO_EV_DEL, _eo_event_del_cb, <const void *>self))
eo_do(o, eo_key_data_del("python-eo"))
#evas_object_smart_data_set(obj.obj, NULL)
obj.obj = NULL
Py_DECREF(obj)
cdef void _smart_object_move(Evas_Object *o, Evas_Coord x, Evas_Coord y) with gil:
cdef:
void *tmp
Smart cls
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.move is not None:
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
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.resize is not None:
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
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.show is not None:
try:
cls.show(obj)
except Exception:
traceback.print_exc()
cdef void _smart_object_hide(Evas_Object *o) with gil:
cdef:
void *tmp
Smart cls
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.hide is not None:
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
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.color_set is not None:
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
Eo obj
Object other
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
other = object_from_instance(clip)
if cls.clip_set is not None:
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
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.clip_unset is not None:
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
Eo obj
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
if cls.calculate is not None:
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
Eo obj
Object other
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
other = object_from_instance(clip)
if cls.member_add is not None:
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_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
other = object_from_instance(clip)
if cls.member_del is not None:
try:
cls.member_del(obj, other)
except Exception:
traceback.print_exc()
cdef void _smart_callback(void *data, Evas_Object *o, void *event_info) with gil:
cdef:
void *tmp
Smart cls
Eo obj
object event, ei
tmp = evas_smart_data_get(evas_object_smart_smart_get(o))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "cls is NULL!", NULL)
return
cls = <Smart>tmp
eo_do_ret(o, tmp, eo_key_data_get("python-eo"))
if tmp == NULL:
EINA_LOG_DOM_WARN(PY_EFL_EVAS_LOG_DOMAIN, "obj is NULL!", NULL)
obj = None
else:
obj = <Eo>tmp
event = <object>data
ei = <object>event_info
lst = tuple(obj._smart_callbacks[event])
for func, args, kargs in lst:
try:
func(obj, ei, *args, **kargs)
except Exception:
traceback.print_exc()
cdef class Smart:
cdef Evas_Smart *cls
def __cinit__(self, clipped=False):
cdef Evas_Smart_Class *cls_def
cls_def = <Evas_Smart_Class*>PyMem_Malloc(sizeof(Evas_Smart_Class))
if cls_def == NULL:
return # raise MemoryError
name = self.__class__.__name__
if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
cls_def.name = name
cls_def.version = EVAS_SMART_CLASS_VERSION
if clipped:
evas_object_smart_clipped_smart_set(cls_def)
else:
cls_def.add = NULL # use python constructor
cls_def.delete = _smart_object_delete
cls_def.move = _smart_object_move
cls_def.resize = _smart_object_resize
cls_def.show = _smart_object_show
cls_def.hide = _smart_object_hide
cls_def.color_set = _smart_object_color_set
cls_def.clip_set = _smart_object_clip_set
cls_def.clip_unset = _smart_object_clip_unset
cls_def.calculate = _smart_object_calculate
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.interfaces = NULL
cls_def.data = <void *>self
self.cls = evas_smart_class_new(cls_def)
Py_INCREF(self)
def delete(self):
evas_smart_free(self.cls)
self.cls = NULL
Py_DECREF(self)
@staticmethod
def delete(obj):
pass
@staticmethod
def member_add(obj, Object child):
pass
@staticmethod
def member_del(obj, Object child):
pass
@staticmethod
def move(obj, int x, int y):
pass
@staticmethod
def resize(obj, int w, int h):
raise NotImplementedError("%s.resize(w, h) not implemented." % obj.__class__.__name__)
@staticmethod
def show(obj):
raise NotImplementedError("%s.show() not implemented." % obj.__class__.__name__)
@staticmethod
def hide(obj):
raise NotImplementedError("%s.hide() not implemented." % obj.__class__.__name__)
@staticmethod
def color_set(obj, int r, int g, int b, int a):
raise NotImplementedError("%s.color_set(r, g, b, a) not implemented." % obj.__class__.__name__)
@staticmethod
def clip_set(obj, Object clip):
raise NotImplementedError("%s.clip_set(object) not implemented." % obj.__class__.__name__)
@staticmethod
def clip_unset(obj):
raise NotImplementedError("%s.clip_unset() not implemented." % obj.__class__.__name__)
@staticmethod
def calculate(obj):
pass
cdef class SmartObjectIterator:
"""Retrieves an iterator of the member objects of a given Evas smart
object
:return: Returns the iterator of the member objects of @p obj.
.. versionadded:: 1.14
"""
cdef Eina_Iterator *itr
def __cinit__(self, SmartObject obj):
self.itr = evas_object_smart_iterator_new(obj.obj)
def __iter__(self):
return self
def __next__(self):
cdef:
void* tmp
Eina_Bool result
if not eina_iterator_next(self.itr, &tmp):
raise StopIteration
return <Object>tmp
def __dealloc__(self):
eina_iterator_free(self.itr)
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.
Behavior is defined by defining the following methods:
:py:func:`delete`
called in order to remove object from canvas and deallocate its
resources. Usually you delete object's children here. *Default
implementation deletes all registered children.*
:py:func:`move`
called in order to move object to given position. Usually you move
children here. *Default implementation calculates offset and move
registered children by it.*
:py:func:`resize`
called in order to resize object. *No default implementation.*
:py:func:`show`
called in order to show the given element. Usually you call the same
function on children. *No default implementation.*
:py:func:`hide`
called in order to hide the given element. Usually you call the same
function on children. *No default implementation.*
:py:func:`color_set`
called in order to change object color. *No default implementation.*
:py:func:`clip_set`
called in order to limit object's visible area. *No default
implementation.*
:py:func:`clip_unset`
called in order to unlimit object's visible area. *No default
implementation.*
:py:func:`calculate`
called before object is used for rendering and it is marked as
dirty/changed with :py:func:`changed`. *Default implementation does
nothing.*
:py:func:`member_add`
called when children is added to object. *Default implementation
does nothing.*
:py:func:`member_del`
called when children is removed from object. *Default implementation
does nothing.*
.. note::
You should never instantiate the SmartObject base class directly,
but inherit and implement methods, then instantiate this new subclass.
.. 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.
.. note::
Do not call your parent on methods you want to replace the behavior
instead of extending it. For example, some methods have default
implementation, you may want to remove and replace it with something
else.
:seealso: :py:class:`ClippedSmartObject`
:param canvas: Evas canvas for this object
:type canvas: Canvas
: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
"""
def __cinit__(self, *a, **ka):
self._smart_callbacks = dict()
def __dealloc__(self):
self._smart_callbacks = None
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)
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
eo_do(self.obj, eo_key_data_set("python-eo", <void *>self, NULL))
# eo_do(self.obj,
# eo_event_callback_add(EO_EV_DEL, _eo_event_del_cb, <const void *>self))
Py_INCREF(self)
return 1
def __iter__(self):
return SmartObjectIterator(self)
# property parent:
# def __get__(self):
# return object_from_instance(evas_object_parent_get(self.obj))
# def parent_get(self):
# return object_from_instance(evas_object_parent_get(self.obj))
def member_add(self, Object child):
"""member_add(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)
def member_del(self, Object child):
"""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):
return <Smart>evas_smart_data_get(evas_object_smart_smart_get(self.obj))
def smart_get(self):
return <Smart>evas_smart_data_get(evas_object_smart_smart_get(self.obj))
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.
"""
if not callable(func):
raise TypeError("func must be callable")
if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
lst = self._smart_callbacks.setdefault(name, [])
if not lst:
evas_object_smart_callback_add(self.obj, name, _smart_callback,
<void *>name)
lst.append((func, args, kargs))
def callback_del(self, name, func):
"""callback_del(event, 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.
"""
if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
try:
lst = self._smart_callbacks[name]
except KeyError:
raise ValueError("Unknown event %r" % name)
cdef:
int i = -1
object f = None
for i, (f, a, k) in enumerate(lst):
if func == f:
break
if f != func:
raise ValueError("Callback %s was not registered with event %r" %
(func, name))
lst.pop(i)
if lst:
return
self._smart_callbacks.pop(name)
evas_object_smart_callback_del(self.obj, name, _smart_callback)
def callback_call(self, name, event_info=None):
"""callback_call(event, 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)
def move_children_relative(self, int dx, int dy):
"""move_children_relative(int dx, int dy)
Move all children relatively.
"""
evas_object_smart_move_children_relative(self.obj, dx, dy)
def changed(self):
"""changed()
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)
_object_mapping_register("Evas_Smart", SmartObject)